316 lines
6.9 KiB
Plaintext
316 lines
6.9 KiB
Plaintext
{
|
|
package qdsl
|
|
|
|
type Limits struct {
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
}
|
|
|
|
type Limit struct {
|
|
Sort any `json:"sort"`
|
|
Limits *Limits `json:"limits"`
|
|
}
|
|
|
|
type Direction struct {
|
|
Direction string `json:"direction"`
|
|
Field any `json:"field"`
|
|
}
|
|
|
|
type Variable struct {
|
|
Variable []string `json:"variable"`
|
|
Op string `json:"op"`
|
|
Evaluation string `json:"evaluation"`
|
|
}
|
|
|
|
type Expression struct {
|
|
Expression *Variable `json:"expression"`
|
|
BoolOp string `json:"boolOp"`
|
|
}
|
|
|
|
type Filter struct {
|
|
Filter [][]*Expression `json:"filter"`
|
|
|
|
// ...limits
|
|
}
|
|
|
|
type Range struct {
|
|
From *string `json:"from"`
|
|
To *string `json:"to"`
|
|
}
|
|
|
|
type Node struct {
|
|
Name *string `json:"name"`
|
|
Ranges []*Range `json:"ranges"`
|
|
}
|
|
|
|
type Block struct {
|
|
*Filter `json:"filter"`
|
|
Any bool `json:"any"`
|
|
Catchall bool `json:"catchall"`
|
|
Node *Node `json:"node"`
|
|
IsGroup bool `json:"isGroup"`
|
|
Children []*Element `json:"children"`
|
|
}
|
|
|
|
type Path []*Block
|
|
|
|
type Element struct {
|
|
Action string `json:"action"`
|
|
Path Path `json:"path"`
|
|
RootExpand bool `json:"rootExpand"`
|
|
Query string `json:"query"`
|
|
}
|
|
|
|
func toString(i interface{}) string {
|
|
if i == nil {
|
|
return ""
|
|
}
|
|
switch i.(type) {
|
|
case string:
|
|
return i.(string)
|
|
default:
|
|
return string(i.([]byte))
|
|
}
|
|
}
|
|
|
|
func arrayToStringArray(arr interface{}) (result []string) {
|
|
for _, i := range arr.([]interface{}) {
|
|
result = append(result, toString(i))
|
|
}
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
start = QUERY
|
|
|
|
QUERY = base:(e:ELEMENT (__ "," __ / [ ]+) { return e, nil })* last:ELEMENT {
|
|
return append(base.([]any), last), nil
|
|
}
|
|
|
|
ELEMENT = action:UNARY levels:(l:LEVEL "." { return l, nil })* last:(LEVEL / "_") {
|
|
var blocks []*Block
|
|
|
|
for _, level := range levels.([]any) {
|
|
if block, ok := level.(*Block); ok {
|
|
blocks = append(blocks, block)
|
|
}
|
|
}
|
|
block, ok := last.(*Block)
|
|
// if last == "_" {
|
|
if !ok {
|
|
return &Element{Action: toString(action), Path: blocks, RootExpand: true}, nil
|
|
}
|
|
|
|
return &Element{Action: toString(action), Path: append(blocks, block)}, nil
|
|
}
|
|
UNARY = op:("-")? {
|
|
if op == "-" {
|
|
return "subtract", nil
|
|
}
|
|
return "add", nil
|
|
}
|
|
|
|
NODE = nodename:NODENAME ranges:NODERANGE? {
|
|
name := toString(nodename)
|
|
var arr []*Range
|
|
if val, ok := ranges.([]interface{}); ok {
|
|
for _, a := range val {
|
|
arr = append(arr, a.(*Range))
|
|
}
|
|
}
|
|
return &Node{Name: &name, Ranges: arr}, nil
|
|
}
|
|
/ ranges:NODERANGE {
|
|
var arr []*Range
|
|
if val, ok := ranges.([]interface{}); ok {
|
|
for _, a := range val {
|
|
arr = append(arr, a.(*Range))
|
|
}
|
|
}
|
|
return &Node{Name: nil, Ranges: arr}, nil
|
|
}
|
|
|
|
NODENAME = nodename:(head:[a-z0-9] tail:[a-z_0-9\\-]i* {
|
|
return toString(head) + strings.Join(arrayToStringArray(tail), ""), nil
|
|
} )
|
|
|
|
NODERANGE = "[" ranges:(from:INT to:("-" to:INT {
|
|
return to, nil
|
|
})? __ ","? __ {
|
|
fromValue := toString(from)
|
|
toValue := toString(to)
|
|
return &Range{From: &fromValue, To: &toValue}, nil
|
|
})+ "]" {
|
|
return ranges, nil
|
|
}
|
|
|
|
INT = num:[0-9a-z]i+ {
|
|
return strings.Join(arrayToStringArray(num), ""), nil
|
|
}
|
|
|
|
LEVEL = "(" children:QUERY ")" {
|
|
var arr []*Element
|
|
if val, ok := children.([]interface{}); ok {
|
|
for _, a := range val {
|
|
arr = append(arr, a.(*Element))
|
|
}
|
|
}
|
|
return &Block{IsGroup: true, Children: arr}, nil
|
|
}
|
|
/ "<" block:BLOCK {
|
|
return &Block{ Any: false, Catchall: true, Filter:block.(*Filter)}, nil
|
|
}
|
|
/ "*" block:BLOCK {
|
|
return &Block{ Any: true, Filter:block.(*Filter)}, nil
|
|
}
|
|
/ node:NODE block:BLOCK {
|
|
return &Block{ Any: false, Node: node.(*Node), Filter:block.(*Filter)}, nil
|
|
}
|
|
|
|
BLOCK = limits:LIMIT filter:SEARCH {
|
|
var filters [][]*Expression
|
|
|
|
for _, i := range filter.([]any) {
|
|
var expressions []*Expression
|
|
for _, j := range i.([]any) {
|
|
expressions = append(expressions, j.(*Expression))
|
|
}
|
|
filters = append(filters, expressions)
|
|
}
|
|
|
|
return &Filter{filters}, nil}
|
|
/ filter:SEARCH limits:LIMIT {
|
|
var filters [][]*Expression
|
|
|
|
for _, i := range filter.([]any) {
|
|
var expressions []*Expression
|
|
for _, j := range i.([]any) {
|
|
expressions = append(expressions, j.(*Expression))
|
|
}
|
|
filters = append(filters, expressions)
|
|
}
|
|
|
|
return &Filter{filters}, nil}
|
|
/ filter:SEARCH {
|
|
var filters [][]*Expression
|
|
|
|
for _, i := range filter.([]any) {
|
|
var expressions []*Expression
|
|
for _, j := range i.([]any) {
|
|
expressions = append(expressions, j.(*Expression))
|
|
}
|
|
filters = append(filters, expressions)
|
|
}
|
|
|
|
return &Filter{filters}, nil}
|
|
|
|
LIMIT = "{" sort:SORT limit:NUMBER ".." offset:NUMBER "}" {
|
|
return &Limit{ Sort:sort, Limits: &Limits{ limit.(int), offset.(int) }}, nil }
|
|
/ "{" sort:SORT limit:NUMBER "}" {
|
|
return &Limit{ Sort:sort, Limits: &Limits{ limit.(int) , 0 }}, nil }
|
|
/ "{" sort:SORT "}" {
|
|
return &Limit{ Sort:sort }, nil }
|
|
|
|
SORT = (direction:DIRECTION v:VARIABLE ","? __ {
|
|
return &Direction{toString(direction) ,v }, nil})*
|
|
|
|
DIRECTION = d:("^")? {
|
|
if d == nil {
|
|
return "ASC", nil
|
|
}
|
|
return "DESC", nil
|
|
}
|
|
|
|
SEARCH = filters:("[?" filter:FILTER "?]"{
|
|
return filter, nil
|
|
})* {
|
|
return filters, nil
|
|
}
|
|
|
|
FILTER = (expression:EXPRESSION __ boolOp:("&&" / "||")? __ {
|
|
expressionValue, _ := expression.(*Variable)
|
|
return &Expression{expressionValue, toString(boolOp)}, nil
|
|
})+
|
|
|
|
EXPRESSION = variable:VARIABLE __ op:OP __ evaluation:EVALUATION {
|
|
var arr []string
|
|
for _, i := range variable.([]any) {
|
|
arr = append(arr, toString(i))
|
|
}
|
|
return &Variable{arr, toString(op), toString(evaluation)}, nil
|
|
}
|
|
|
|
OP = "=="
|
|
/ "=~"
|
|
/ "!~"
|
|
/ "<="
|
|
/ ">="
|
|
/ "<"
|
|
/ ">"
|
|
/ "!="
|
|
/ "IN"i
|
|
/ "LIKE"i
|
|
/ "NOT LIKE"i
|
|
|
|
VARIABLE = variable:("object" / "link" / "path" / "@" / "$") attribute:ATTRIBUTE+ {
|
|
return append([]any{variable}, attribute.([]any)...), nil
|
|
}
|
|
|
|
ATTRIBUTE = ("." attrname:ATTRNAME {
|
|
return attrname, nil })
|
|
/ ("[" attrname:(STRING_LITERAL / INT) "]" {
|
|
return attrname, nil })
|
|
|
|
ATTRNAME = attrname:[a-z\\*0-9_\\-]i+ {
|
|
var arr []string
|
|
|
|
for _, a := range attrname.([]any) {
|
|
arr = append(arr, toString(a))
|
|
}
|
|
|
|
return strings.Join(arr, ""), nil
|
|
}
|
|
|
|
STRING_LITERAL = "'" text:("\\'"/[^'])+ "'" {
|
|
return strings.Join(text.([]string), ""), nil
|
|
}
|
|
|
|
BOOL = "true" / "false" / "True" / "False"
|
|
NULL = "null"
|
|
|
|
EVALUATION = LITERAL
|
|
|
|
LITERAL = DBL_LITERAL / SNG_LITERAL / NUMBER / BOOL / NULL / ARR
|
|
|
|
SNG_LITERAL = q1:"'" cc:[^\\']* q2:"'" { return toString(q1) + strings.Join(arrayToStringArray(cc), "") + toString(q2), nil }
|
|
DBL_LITERAL = q1:'"' cc:[^\\"]* q2:'"' { return toString(q1) + strings.Join(arrayToStringArray(cc), "") + toString(q2), nil }
|
|
|
|
NUMBER = num:[0-9]+ tail:('.' [0-9]+)? {
|
|
fmt.Println("AAAAAAaa")
|
|
arr:= tail.([]string)
|
|
var end string
|
|
if len(arr) > 0 {
|
|
// FIXME [][]arr?
|
|
// end = "." + strings.Join(arr[1], "")
|
|
}
|
|
|
|
return strings.Join(num.([]string), "") + end, nil
|
|
}
|
|
|
|
ARR = '[' __
|
|
body:(hd:LITERAL items:(__ ',' __ e:LITERAL { return e, nil })* __ {
|
|
arr := []any{hd}
|
|
return append(arr, items), nil
|
|
})?
|
|
']' {
|
|
bodyArr := body.([]string)
|
|
arr := []string{}
|
|
if len(bodyArr) > 0 {
|
|
arr = append(arr, strings.Join(bodyArr, ","))
|
|
}
|
|
return arr, nil
|
|
}
|
|
|
|
__ = [ ]*
|