1
1
local lpeg = require (' lulpeg' )
2
+ local utils = require (' graphql.utils' )
3
+ local rex , is_pcre2 = utils .optional_require_rex ()
4
+ local bit = require (' bit' )
5
+
6
+ --- NOTE: Functions that worth moving out into other modules:
7
+ --- 1) Regexp implementation (also lies inside of
8
+ --- accessor_general.lua)
9
+ --- 2) Vararg iterator.
10
+
11
+ --- TODO:
12
+ --- 1) Validation.
13
+ --- 2) Big numbers.
14
+ --- 3) Absence of variable in context.variables is equal
15
+ --- to the situation when context.variables.var == nil.
16
+ --- 4) In case you change string inside of an expression
17
+ --- object you need to manually change ast in object.
2
18
3
19
local expressions = {}
4
20
local P , R , S , V = lpeg .P , lpeg .R , lpeg .S , lpeg .V
@@ -220,6 +236,26 @@ local expression_grammar = P {
220
236
value_terminal = (_literal + _variable + _field_path ) / identical_node
221
237
}
222
238
239
+ -- Add one number to another.
240
+ --
241
+ -- It may be changed after introduction of "big ints".
242
+ --
243
+ -- @param operand_1
244
+ -- @param operand_2
245
+ --
246
+ local function sum (operand_1 , operand_2 )
247
+ return operand_1 + operand_2
248
+ end
249
+
250
+ -- Subtract one number from another.
251
+ --
252
+ -- It may be changed after introduction of "big ints".
253
+ -- @param operand_1
254
+ -- @param operand_2
255
+ local function subtract (operand_1 , operand_2 )
256
+ return operand_1 - operand_2
257
+ end
258
+
223
259
--- Parse given string which supposed to be a c-style expression.
224
260
---
225
261
--- @tparam str string representation of expression.
@@ -230,4 +266,207 @@ function expressions.parse(str)
230
266
return expression_grammar :match (str ) or error (' syntax error' )
231
267
end
232
268
269
+ -- function expressions.validate(context)
270
+ --
271
+ -- end
272
+
273
+ --- Recursively execute the syntax subtree. Of course it can be
274
+ --- syntax tree itself.
275
+ ---
276
+ --- @tparam node node to be executed.
277
+ ---
278
+ --- @tparam context table containing information useful for
279
+ --- execution (see @{expressions.new}).
280
+ ---
281
+ --- @treturn subtree value.
282
+ function expressions .execute (node , context )
283
+ if node .kind == ' const' then
284
+ if node .value_class == ' string' then
285
+ return node .value
286
+ end
287
+
288
+ if node .value_class == ' bool' then
289
+ if node .value == ' false' then
290
+ return false
291
+ end
292
+ return true
293
+ end
294
+
295
+ if node .value_class == ' number' then
296
+ return tonumber (node .value )
297
+ end
298
+ end
299
+
300
+ if node .kind == ' variable' then
301
+ local name = node .name
302
+ return context .variables [name ]
303
+ end
304
+
305
+ if node .kind == ' object_field' then
306
+ local path = node .path
307
+ local field = context .object
308
+ local table_path = (path :split (' .' ))
309
+ for i = 1 , # table_path do
310
+ field = field [table_path [i ]]
311
+ end
312
+ return field
313
+ end
314
+
315
+ if node .kind == ' func' then
316
+ -- regexp() implementation.
317
+ if node .name == ' regexp' then
318
+ local flags = rex .flags ()
319
+ local cfg = 0
320
+ local pattern = expressions .execute (node .args [1 ], context )
321
+ local str = expressions .execute (node .args [2 ], context )
322
+ if not is_pcre2 then
323
+ local cnt
324
+ pattern , cnt = pattern :gsub (' ^%(%?i%)' , ' ' )
325
+ if cnt > 0 then
326
+ cfg = bit .bor (cfg , flags .CASELESS )
327
+ end
328
+ end
329
+ if is_pcre2 then
330
+ cfg = bit .bor (cfg , flags .UTF )
331
+ cfg = bit .bor (cfg , flags .UCP )
332
+ else
333
+ cfg = bit .bor (cfg , flags .UTF8 )
334
+ cfg = bit .bor (cfg , flags .UCP )
335
+ end
336
+ local pattern = rex .new (pattern , cfg )
337
+ if not pattern :match (str ) then
338
+ return false
339
+ end
340
+ return true
341
+ end
342
+
343
+ -- is_null() implementation.
344
+ if node .name == ' is_null' then
345
+ return expressions .execute (node .args [1 ], context ) == nil
346
+ end
347
+
348
+ -- not_null() implementation.
349
+ if node .name == ' not_null' then
350
+ return expressions .execute (node .args [1 ], context ) ~= nil
351
+ end
352
+ end
353
+
354
+ if node .kind == ' unary_operation' then
355
+ -- Negation.
356
+ if node .op == ' !' then
357
+ return not expressions .execute (node .node , context )
358
+ end
359
+
360
+ -- Unary '+'.
361
+ if node .op == ' +' then
362
+ return expressions .execute (node .node , context )
363
+ end
364
+
365
+ -- Unary '-'.
366
+ if node .op == ' -' then
367
+ return - expressions .execute (node .node , context )
368
+ end
369
+ end
370
+
371
+ if node .kind == ' binary_operations' then
372
+ local prev = expressions .execute (node .operands [1 ], context )
373
+ for i , op in ipairs (node .operators ) do
374
+ local second_operand = expressions .execute (node .operands [i + 1 ],
375
+ context )
376
+ -- Sum.
377
+ if op == ' +' then
378
+ prev = sum (prev , second_operand )
379
+ end
380
+
381
+ -- Subtraction.
382
+ if op == ' -' then
383
+ prev = subtract (prev , second_operand )
384
+ end
385
+
386
+ -- Logical and.
387
+ if op == ' &&' then
388
+ prev = prev and second_operand
389
+ end
390
+
391
+ -- Logical or.
392
+ if op == ' ||' then
393
+ prev = prev or second_operand
394
+ end
395
+
396
+ -- Equal.
397
+ if op == ' ==' then
398
+ prev = prev == second_operand
399
+ end
400
+
401
+ -- Not equal.
402
+ if op == ' !=' then
403
+ prev = prev ~= second_operand
404
+ end
405
+
406
+ -- Greater than.
407
+ if op == ' >' then
408
+ prev = prev > second_operand
409
+ end
410
+
411
+ -- Greater or equal.
412
+ if op == ' >=' then
413
+ prev = prev >= second_operand
414
+ end
415
+
416
+ -- Lower than.
417
+ if op == ' <' then
418
+ prev = prev < second_operand
419
+ end
420
+
421
+ -- Lower or equal.
422
+ if op == ' &&' then
423
+ prev = prev and second_operand
424
+ end
425
+ end
426
+ return prev
427
+ end
428
+
429
+ if node .kind == ' root_expression' then
430
+ return expressions .execute (node .expr , context )
431
+ end
432
+ end
433
+
434
+
435
+ --- Compile and execute given string that represents a c-style
436
+ --- expression.
437
+ ---
438
+ --- @tparam str string representation of expression.
439
+ --- @tparam object object considered inside of an expression.
440
+ --- @tparam variables list of variables.
441
+ ---
442
+ --- @treturn expression value.
443
+ function expressions .compile_and_execute (str , object , variables )
444
+ local context = expressions .new (str , object , variables )
445
+ return expressions .execute (context .ast , context )
446
+ end
447
+
448
+ --- Create a new c-style expression object.
449
+ ---
450
+ --- @tparam str string representation of expression.
451
+ --- @tparam object object considered inside of an expression.
452
+ --- @tparam variables list of variables.
453
+ ---
454
+ --- @treturn expression object.
455
+ function expressions .new (str , object , variables )
456
+ return setmetatable ({
457
+ string = str ,
458
+ object = object ,
459
+ variables = variables ,
460
+ }, {
461
+ __index = function (self , key )
462
+ if key == ' ast' then
463
+ return expressions .parse (self .string )
464
+ end
465
+ if key == ' execute' then
466
+ return expressions .execute (self .ast , self )
467
+ end
468
+ end
469
+ })
470
+ end
471
+
233
472
return expressions
0 commit comments