Skip to content

Commit f082069

Browse files
committed
graphql: implement c-style expressions parser
Needed for tarantool#13
1 parent 2ce2d88 commit f082069

File tree

4 files changed

+618
-0
lines changed

4 files changed

+618
-0
lines changed

graphql/core/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ graphql.types = require(path .. '.types')
77
graphql.schema = require(path .. '.schema')
88
graphql.validate = require(path .. '.validate')
99
graphql.execute = require(path .. '.execute')
10+
graphql.expressions = require(path .. '.expressions')
1011

1112
return graphql

graphql/expressions.lua

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
local lpeg = require('lulpeg')
2+
local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
3+
local C = lpeg.C
4+
5+
-- Some special symbols.
6+
local space_symbol = S(' \t\r\n')
7+
local eof = P(-1)
8+
local some_spaces = space_symbol ^ 0
9+
10+
--
11+
-- Possible identifier patterns:
12+
-- 1) Number.
13+
local digit = R('09')
14+
local integer = (S('-+') ^ -1) * (digit ^ 1)
15+
local decimal = integer * '.' * (digit ^ 1)
16+
local number_pat = decimal + integer
17+
-- 2) Boolean.
18+
local bool_pat = P('false') + P('true')
19+
-- 3) String.
20+
local string_pat = P'\"' * C((P'\\"' + 1 - S'\"') ^ 0) * P'"'
21+
-- 4) Variable.
22+
local variable_name = ('_' + R('az', 'AZ')) *
23+
(('_' + R('09', 'az', 'AZ')) ^ 0)
24+
-- 5) Object's field path.
25+
local field = ('_' + R('az', 'AZ')) * ('_' + R('09', 'az', 'AZ')) ^ 0
26+
local field_path = field * ('.' * field) ^ 0
27+
28+
--
29+
-- Possible logical function patterns:
30+
-- 1) is_null.
31+
local is_null = P('is_null')
32+
-- 2) not_null.
33+
local not_null = P('not_null')
34+
-- 3) regexp.
35+
local regexp = P('regexp')
36+
37+
--
38+
-- Possible unary operator patterns:
39+
-- 1) Logical negation.
40+
local negation = P('!')
41+
42+
-- Possible binary operator patterns:
43+
-- 1) Logical and.
44+
local logic_and = P('&&')
45+
-- 2) logical or.
46+
local logic_or = P('||')
47+
-- 3) +
48+
local addition = P('+')
49+
-- 4) -
50+
local subtraction = P('-')
51+
-- 5) ==
52+
local eq = P('==')
53+
-- 6) !=
54+
local not_eq = P('!=')
55+
-- 7) >
56+
local gt = P('>')
57+
-- 8) >=
58+
local ge = P('>=')
59+
-- 9) <
60+
local lt = P('<')
61+
-- 10) <=
62+
local le = P('<=')
63+
64+
--
65+
-- AST nodes generating functions.
66+
--
67+
local function identical(arg)
68+
return arg
69+
end
70+
71+
local function root_expr_node(expr)
72+
return {
73+
kind = 'root_expression',
74+
entity = expr
75+
}
76+
end
77+
78+
local function bin_op_node(...)
79+
local args = {...}
80+
if select('#', ...) == 1 then
81+
return identical(args[1])
82+
end
83+
local operators, nodes = {}, {}
84+
for i, v in ipairs(args) do
85+
if i % 2 == 0 then
86+
table.insert(operators, v)
87+
else
88+
table.insert(nodes, v)
89+
end
90+
end
91+
return {
92+
kind = 'binary_operations',
93+
operators = operators,
94+
nodes = nodes
95+
}
96+
-- return {
97+
-- kind = 'binary_operation',
98+
-- op = binary_operator,
99+
-- node_1 = operand_1,
100+
-- node_2 = operand_2
101+
-- }
102+
end
103+
104+
local function unary_op_node(unary_operator, operand_1)
105+
return {
106+
kind = 'unary_operation',
107+
op = unary_operator,
108+
node = operand_1
109+
}
110+
end
111+
112+
local function func_node(name, ...)
113+
local args = {...}
114+
return {
115+
kind = 'function',
116+
name = name,
117+
args = args
118+
}
119+
end
120+
121+
local function op_name(string)
122+
return string
123+
end
124+
125+
local function number_node(value)
126+
return {
127+
kind = 'const',
128+
data_class = 'number',
129+
value = value
130+
}
131+
end
132+
133+
local function string_node(value)
134+
return {
135+
kind = 'const',
136+
data_class = 'string',
137+
value = value
138+
}
139+
end
140+
141+
local function bool_node(value)
142+
return {
143+
kind = 'const',
144+
data_class = 'bool',
145+
value = value
146+
}
147+
end
148+
149+
local function variable_node(name)
150+
return {
151+
kind = 'variable',
152+
name = name
153+
}
154+
end
155+
156+
local function path_node(path)
157+
return {
158+
kind = 'object_field',
159+
path = path
160+
}
161+
end
162+
163+
-- Patterns returning corresponding nodes.
164+
local _number = number_pat / number_node
165+
local _bool = bool_pat / bool_node
166+
local _string = string_pat / string_node
167+
local _variable = '$' * C(variable_name) / variable_node
168+
local _field_path = field_path / path_node
169+
170+
local _logic_or = logic_or / op_name
171+
local _logic_and = logic_and / op_name
172+
local _comparison_op = (eq + not_eq + ge + gt + le + lt) / op_name
173+
local _arithmetic_op = (addition + subtraction) / op_name
174+
local _unary_op = negation / op_name
175+
local _functions = (is_null + not_null + regexp) / identical
176+
177+
--
178+
-- Grammar rules for C-style expressions positioned ascending in
179+
-- terms of priority.
180+
--
181+
local expression_grammar = P {
182+
'init_expr',
183+
init_expr = V('expr') * eof / root_expr_node,
184+
expr = some_spaces * V('log_expr_or') * some_spaces / identical,
185+
186+
log_expr_or = V('log_expr_and') * (some_spaces * _logic_or *
187+
some_spaces * V('log_expr_and')) ^ 0 / bin_op_node,
188+
log_expr_and = V('comparison') * (some_spaces * _logic_and *
189+
some_spaces * V('comparison')) ^ 0 / bin_op_node,
190+
comparison = V('arithmetic_expr') * (some_spaces * _comparison_op *
191+
some_spaces * V('arithmetic_expr')) ^ 0 / bin_op_node,
192+
arithmetic_expr = V('unary_expr') * (some_spaces * _arithmetic_op *
193+
some_spaces * V('unary_expr')) ^ 0 / bin_op_node,
194+
195+
unary_expr = (_unary_op * V('first_prio') / unary_op_node) +
196+
(V('first_prio') / identical),
197+
first_prio = (V('_function') + V('identifier') + '(' * some_spaces *
198+
V('expr') * some_spaces * ')') / identical,
199+
_function = _functions * '(' * some_spaces * (V('identifier') *
200+
some_spaces * ',' * some_spaces) ^ 0 * V('identifier')
201+
* some_spaces * ')' / func_node,
202+
identifier = (_bool + _number + _string + _variable + _field_path) /
203+
identical
204+
}
205+
206+
--
207+
--
208+
--
209+
210+
211+
--
212+
-- Parsing function.
213+
--
214+
return function(str)
215+
assert(type(str) == 'string', 'parser expects a string')
216+
return expression_grammar:match(str) or error('syntax error', 2)
217+
end

0 commit comments

Comments
 (0)