dmn 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,654 @@
1
+ grammar SpotFeel
2
+
3
+ rule start
4
+ expression_or_tests
5
+ end
6
+
7
+ rule expression_or_tests
8
+ simple_expression /
9
+ simple_unary_tests
10
+ end
11
+
12
+ #
13
+ # 1. expression =
14
+ # 1.a textual expression |
15
+ # 1.b boxed expression ;
16
+ #
17
+ rule expression
18
+ simple_expression
19
+ end
20
+
21
+ rule bracketed_expression
22
+ "(" __ expression __ ")" {
23
+ def eval(context={})
24
+ expression.eval(context)
25
+ end
26
+ }
27
+ end
28
+
29
+ #
30
+ # 2. textual expression =
31
+ # 2.a function definition | for expression | if expression | quantified expression |
32
+ # 2.b disjunction |
33
+ # 2.c conjunction |
34
+ # 2.d comparison |
35
+ # 2.e arithmetic expression |
36
+ # 2.f instance of |
37
+ # 2.g path expression |
38
+ # 2.h filter expression | function invocation |
39
+ # 2.i literal | simple positive unary test | name | "(" , textual expression , ")" ;
40
+ #
41
+
42
+ #
43
+ # 3. textual expressions = textual expression , { "," , textual expression } ;
44
+ #
45
+
46
+ #
47
+ # 4. arithmetic expression =
48
+ # 4.a addition | subtraction |
49
+ # 4.b multiplication | division |
50
+ # 4.c exponentiation |
51
+ # 4.d arithmetic negation ;
52
+ #
53
+ rule arithmetic_expression
54
+ addition /
55
+ subtraction /
56
+ multiplication /
57
+ division /
58
+ exponentiation /
59
+ arithmetic_negation /
60
+ bracketed_arithmetic_expression
61
+ end
62
+
63
+ rule bracketed_arithmetic_expression
64
+ "(" __ arithmetic_expression __ ")" {
65
+ def eval(context={})
66
+ arithmetic_expression.eval(context)
67
+ end
68
+ }
69
+ end
70
+
71
+ #
72
+ # 5. simple expression = arithmetic expression | simple value ;
73
+ #
74
+ # Note: added boxed_expression here to support list and context
75
+ #
76
+ rule simple_expression
77
+ expanded_simple_expression /
78
+ arithmetic_expression /
79
+ comparison /
80
+ simple_value /
81
+ bracketed_expression
82
+ end
83
+
84
+ #
85
+ # NOTE: these rules go beyond S-FEEL spec
86
+ #
87
+ rule expanded_simple_expression
88
+ if_expression /
89
+ comparison /
90
+ boxed_expression
91
+ end
92
+
93
+ #
94
+ # 6. simple expressions = simple expression , { "," , simple expression } ;
95
+ #
96
+ rule simple_expressions
97
+ expr:simple_expression more_exprs:(__ "," __ simple_expression)* <SimpleExpressions>
98
+ end
99
+
100
+ #
101
+ # 7. simple positive unary test =
102
+ # 7.a [ "<" | "<=" | ">" | ">=" ] , endpoint |
103
+ # 7.b interval ;
104
+ #
105
+ rule simple_positive_unary_test
106
+ head:((unary_operator / "not") __)? tail:endpoint <SimplePositiveUnaryTest> /
107
+ interval
108
+ end
109
+
110
+ rule unary_operator
111
+ "<=" /
112
+ ">=" /
113
+ "<" /
114
+ ">"
115
+ end
116
+
117
+ #
118
+ # 8. interval = ( open interval start | closed interval start ) , endpoint , ".." , endpoint , ( open interval end | closed interval end ) ;
119
+ #
120
+ rule interval
121
+ start_token:("(" / "]") __ first:endpoint __ ".." __ second:endpoint __ end_token:(")" / "[") <Interval> /
122
+ start_token:"[" __ first:endpoint __ ".." __ second:endpoint __ end_token:"]" <Interval>
123
+ end
124
+
125
+ #
126
+ # 9. open interval start = "(" | "]" ;
127
+ #
128
+ rule open_interval_start
129
+ "(" <OpenIntervalStart> /
130
+ "]" <OpenIntervalStart>
131
+ end
132
+
133
+ #
134
+ # 10. closed interval start = "[" ;
135
+ #
136
+ rule closed_interval_start
137
+ "[" <ClosedIntervalStart>
138
+ end
139
+
140
+ #
141
+ # 11. open interval end = ")" | "[" ;
142
+ #
143
+ rule open_interval_end
144
+ ")" <OpenIntervalEnd> /
145
+ "[" <OpenIntervalEnd>
146
+ end
147
+
148
+ #
149
+ # 12. closed interval end = "]" ;
150
+ #
151
+ rule closed_interval_end
152
+ "]" <ClosedIntervalEnd>
153
+ end
154
+
155
+ #
156
+ # 13. simple positive unary tests = simple positive unary test , { "," , simple positive unary test } ;
157
+ #
158
+ rule simple_positive_unary_tests
159
+ test:simple_positive_unary_test more_tests:(__ "," __ simple_positive_unary_test)* <SimplePositiveUnaryTests>
160
+ end
161
+
162
+ #
163
+ # 14. simple unary tests =
164
+ # 14.a simple positive unary tests |
165
+ # 14.b "not", "(", simple positive unary tests, ")" |
166
+ # 14.c "-";
167
+ #
168
+ rule simple_unary_tests
169
+ expr:simple_positive_unary_tests <SimpleUnaryTests> /
170
+ negate:"not" __ "(" __ expr:simple_positive_unary_tests __ ")" <SimpleUnaryTests> /
171
+ "-" <SimpleUnaryTests>
172
+ end
173
+
174
+ #
175
+ # 15. positive unary test = simple positive unary test | "null" ;
176
+ #
177
+ rule positive_unary_test
178
+ simple_positive_unary_test /
179
+ null_literal
180
+ end
181
+
182
+ #
183
+ # 16. positive unary tests = positive unary test , { "," , positive unary test } ;
184
+ #
185
+ rule positive_unary_tests
186
+ test:positive_unary_test more_tests:(__ "," __ positive_unary_test)* <PositiveUnaryTests>
187
+ end
188
+
189
+ #
190
+ # 17. unary tests =
191
+ # 17.a positive unary tests |
192
+ # 17.b "not", " (", positive unary tests, ")" |
193
+ # 17.c "-"
194
+ #
195
+
196
+ #
197
+ # 18. endpoint = simple value ;
198
+ #
199
+ rule endpoint
200
+ arithmetic_expression /
201
+ simple_value
202
+ end
203
+
204
+ #
205
+ # 19. simple value = qualified name | simple literal ;
206
+ #
207
+ # Note: dmn-eval = simple literal | qualified name | function invocation
208
+ #
209
+ rule simple_value
210
+ literal /
211
+ function_invocation /
212
+ qualified_name
213
+ end
214
+
215
+ #
216
+ # 20. qualified name = name , { "." , name } ;
217
+ #
218
+ rule qualified_name
219
+ head:name tail:(__ "." __ name)* <QualifiedName>
220
+ end
221
+
222
+ #
223
+ # 21. addition = expression , "+" , expression ;
224
+ #
225
+ rule addition
226
+ head:non_recursive_simple_expression_for_arithmetic_expression __ "+" __ tail:expression <Addition>
227
+ end
228
+
229
+ rule non_recursive_simple_expression_for_arithmetic_expression
230
+ bracketed_arithmetic_expression /
231
+ simple_value
232
+ end
233
+
234
+ rule non_recursive_simple_expression_for_comparison
235
+ arithmetic_expression /
236
+ simple_value
237
+ end
238
+
239
+ #
240
+ # 22. subtraction = expression , "-" , expression ;
241
+ #
242
+ rule subtraction
243
+ head:non_recursive_simple_expression_for_arithmetic_expression __ "-" __ tail:expression <Subtraction>
244
+ end
245
+
246
+ #
247
+ # 23. multiplication = expression , "\*" , expression ;
248
+ #
249
+ rule multiplication
250
+ head:non_recursive_simple_expression_for_arithmetic_expression __ "*" __ tail:expression <Multiplication>
251
+ end
252
+
253
+ #
254
+ # 24. division = expression , "/" , expression ;
255
+ #
256
+ rule division
257
+ head:non_recursive_simple_expression_for_arithmetic_expression __ "/" __ tail:expression <Division>
258
+ end
259
+
260
+ #
261
+ # 25. exponentiation = expression, "\*\*", expression ;
262
+ #
263
+ rule exponentiation
264
+ head:non_recursive_simple_expression_for_arithmetic_expression __ "**" __ tail:expression <Exponentiation>
265
+ end
266
+
267
+ #
268
+ # 26. arithmetic negation = "-" , expression ;
269
+ #
270
+ rule arithmetic_negation
271
+ "-" __ "(" __ expr:expression __ ")" {
272
+ def eval(context={})
273
+ -expr.eval(context)
274
+ end
275
+ }
276
+ end
277
+
278
+ #
279
+ # 27. name = name start , { name part | additional name symbols } ;
280
+ #
281
+ rule name
282
+ head:name_start tail:(__ name_part)* <Name>
283
+ end
284
+
285
+ rule reserved_word
286
+ keyword /
287
+ date_time_keyword /
288
+ null_literal /
289
+ boolean_literal
290
+ end
291
+
292
+ #
293
+ # 28. name start = name start char, { name part char } ;
294
+ #
295
+ rule name_start
296
+ head:name_start_char tail:(name_part_char)* {
297
+ def eval(context={})
298
+ head + tail.map{|t| t[1]}.join("")
299
+ end
300
+ }
301
+ end
302
+
303
+ #
304
+ # 29. name part = name part char , { name part char } ;
305
+ #
306
+ rule name_part
307
+ head:name_part_char tail:(__ name_part_char)* {
308
+ def eval(context={})
309
+ head + tail.map{|t| t[1]}.join("")
310
+ end
311
+ }
312
+ end
313
+
314
+ #
315
+ # 30. name start char = "?" | [A-Z] | "\_" | [a-z] | [\uC0-\uD6] | [\uD8-\uF6] | [\uF8-\u2FF] | [\u370-\u37D] | [\u37F-\u1FFF] | [\u200C-\u200D] | [\u2070-\u218F] | [\u2C00-\u2FEF] | [\u3001-\uD7FF] | [\uF900-\uFDCF] | [\uFDF0-\uFFFD] | [\u10000-\uEFFFF] ;
316
+ #
317
+ rule name_start_char
318
+ [A-Z] / [a-z] / "_" / name_start_unicode_char
319
+ end
320
+
321
+ rule name_start_unicode_char
322
+ [\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]
323
+ end
324
+
325
+ #
326
+ # 31. name part char = name start char | digit | \uB7 | [\u0300-\u036F] | [\u203F-\u2040] ;
327
+ #
328
+ rule name_part_char
329
+ name_start_char /
330
+ digit /
331
+ name_part_unicode_char /
332
+ "'"
333
+ end
334
+
335
+ rule name_part_unicode_char
336
+ [\u0B70\u0300-\u036F\u203F-\u2040]
337
+ end
338
+
339
+ #
340
+ # 32. additional name symbols = "." | "/" | "-" | "’" | "+" | "\*" ;
341
+ #
342
+ rule additional_name_symbols
343
+ "." / "/" / "-" / "'" / "+" / "*"
344
+ end
345
+
346
+ #
347
+ # 33. literal = simple literal | "null" ;
348
+ #
349
+ rule literal
350
+ simple_literal /
351
+ null_literal
352
+ end
353
+
354
+ #
355
+ # 34. simple literal = numeric literal | string literal | boolean literal | date time literal ;
356
+ #
357
+ rule simple_literal
358
+ numeric_literal /
359
+ string_literal /
360
+ boolean_literal /
361
+ date_time_literal
362
+ end
363
+
364
+ #
365
+ # 35. string literal = '"' , { character – ('"' | vertical space) }, '"' ;
366
+ #
367
+ rule string_literal
368
+ '"' chars:double_string_character* '"' <StringLiteral> /
369
+ "'" chars:single_string_character* "'" <StringLiteral>
370
+ end
371
+
372
+ rule double_string_character
373
+ !('"' / "\\" / line_terminator) source_character /
374
+ "\\" escape_sequence /
375
+ line_continuation
376
+ end
377
+
378
+ rule single_string_character
379
+ !("'" / "\\" / line_terminator) source_character /
380
+ "\\" escape_sequence /
381
+ line_continuation
382
+ end
383
+
384
+ rule source_character
385
+ .
386
+ end
387
+
388
+ rule line_continuation
389
+ "\\" __ line_terminator_sequence __
390
+ end
391
+
392
+ rule escape_sequence
393
+ character_escape_sequence
394
+ end
395
+
396
+ rule character_escape_sequence
397
+ single_escape_character
398
+ end
399
+
400
+ rule single_escape_character
401
+ "'" / '"' / "\\"
402
+ end
403
+
404
+ rule line_terminator
405
+ line_terminator_sequence
406
+ end
407
+
408
+ rule line_terminator_sequence
409
+ "\n" / "\r" / "\r\n"
410
+ end
411
+
412
+ #
413
+ # 36. Boolean literal = "true" | "false" ;
414
+ #
415
+ rule boolean_literal
416
+ "true" <BooleanLiteral> /
417
+ "false" <BooleanLiteral>
418
+ end
419
+
420
+ #
421
+ # 37. numeric literal = [ "-" ] , ( digits , [ ".", digits ] | "." , digits ) ;
422
+ #
423
+ rule numeric_literal
424
+ '-'? digits ('.' digits)? <NumericLiteral>
425
+ end
426
+
427
+ rule null_literal
428
+ "null" <NullLiteral>
429
+ end
430
+
431
+ #
432
+ # 38. digit = [0-9] ;
433
+ #
434
+ rule digit
435
+ [0-9]
436
+ end
437
+
438
+ #
439
+ # 39. digits = digit , {digit} ;
440
+ #
441
+ rule digits
442
+ digit+
443
+ end
444
+
445
+ #
446
+ # 40. function invocation = expression , parameters ;
447
+ #
448
+ rule function_invocation
449
+ fn_name:(!reserved_word qualified_name) __ "(" __ params:(positional_parameters)? __ ")" <FunctionInvocation>
450
+ end
451
+
452
+ #
453
+ # 41. parameters = "(" , ( named parameters | positional parameters ) , ")" ;
454
+ #
455
+
456
+ #
457
+ # 42. named parameters = parameter name , ":" , expression , { "," , parameter name , ":" , expression } ;
458
+ #
459
+
460
+ #
461
+ # 43. parameter name = name ;
462
+ #
463
+
464
+ #
465
+ # 44. positional parameters = [ expression , { "," , expression } ] ;
466
+ #
467
+ rule positional_parameters
468
+ expression __ more_expressions:(__ "," __ expression)* <PositionalParameters>
469
+ end
470
+
471
+ #
472
+ # 45. path expression = expression , "." , name ;
473
+ #
474
+ rule path_expression
475
+ expression __ "." __ name:name <PathExpression>
476
+ end
477
+
478
+ #
479
+ # 46. for expression = "for" , name , "in" , expression { "," , name , "in" , expression } , "return" , expression ;
480
+ #
481
+ rule for_expression
482
+ "for" __ name:name __ "in" __ expression __ more_for_expressions:(__ "," __ name:name __ "in" __ expression)* __ "return" __ return_expression:expression <ForExpression>
483
+ end
484
+
485
+ #
486
+ # 47. if expression = "if" , expression , "then" , expression , "else" expression ;
487
+ #
488
+ rule if_expression
489
+ "if" __ condition:expression __ "then" __ true_case:expression __ "else" __ false_case:expression <IfExpression>
490
+ end
491
+
492
+ #
493
+ # 48. quantified expression = ("some" | "every") , name , "in" , expression , { name , "in" , expression } , "satisfies" , expression ;
494
+ #
495
+ rule quantified_expression
496
+ quantifier:("some" / "every") __ name:name __ "in" __ expression __ more_quantifiers:(__ name:name __ "in" __ expression)* __ "satisfies" __ satisfies:expression <QuantifiedExpression>
497
+ end
498
+
499
+ #
500
+ # 49. disjunction = expression , "or" , expression ;
501
+ #
502
+ rule disjunction
503
+ head:expression tail:(__ "or" __ expression)+ <Disjunction>
504
+ end
505
+
506
+ #
507
+ # 50. conjunction = expression , "and" , expression ;
508
+ #
509
+ rule conjunction
510
+ head:expression tail:(__ "and" __ expression)+ <Conjunction>
511
+ end
512
+
513
+ #
514
+ # 51. comparison =
515
+ # 51.a expression , ( "=" | "!=" | "<" | "<=" | ">" | ">=" ) , expression |
516
+ # 51.b expression , "between" , expression , "and" , expression |
517
+ # 51.c expression , "in" , positive unary test ;
518
+ # 51.d expression , "in" , " (", positive unary tests, ")" ;
519
+ #
520
+ rule comparison
521
+ left:non_recursive_simple_expression_for_comparison __ operator:comparision_operator __ right:expression <Comparison>
522
+ end
523
+
524
+ rule non_recursive_simple_expression_for_comparison
525
+ arithmetic_expression /
526
+ simple_value
527
+ end
528
+
529
+ #
530
+ # 52. filter expression = expression , "[" , expression , "]" ;
531
+ #
532
+ rule filter_expression
533
+ expression __ "[" __ filter:expression __ "]" <FilterExpression>
534
+ end
535
+
536
+ rule comparision_operator
537
+ "=" / "!=" / "<=" / ">=" / "<" / ">"
538
+ end
539
+
540
+ #
541
+ # 53. instance of = expression , "instance" , "of" , type ;
542
+ #
543
+ rule instance_of
544
+ expression __ "instance" __ "of" __ type <InstanceOf>
545
+ end
546
+
547
+ #
548
+ # 54. type = qualified name ;
549
+ #
550
+ rule type
551
+ qualified_name
552
+ end
553
+
554
+ #
555
+ # 55. boxed expression = list | function definition | context ;
556
+ #
557
+ # Note: function definition not supported yet
558
+ #
559
+ rule boxed_expression
560
+ list /
561
+ context
562
+ end
563
+
564
+ #
565
+ # 56. list = "[" [ expression , { "," , expression } ] , "]" ;
566
+ #
567
+ rule list
568
+ '[' __ list_entries __ ']' <List>
569
+ /
570
+ '[]' <List>
571
+ end
572
+
573
+ rule list_entries
574
+ expression more_expressions:(__ ',' __ expression)* <ListEntries>
575
+ end
576
+
577
+ #
578
+ # 57. function definition = "function" , "(" , [ formal parameter { "," , formal parameter } ] , ")" , [ "external" ] , expression ;
579
+ #
580
+ rule function_definition
581
+ "function" __ "(" __ formal_parameters:(formal_parameter_list)? __ ")" __ external:(__ "external")? __ expression <FunctionDefinition>
582
+ end
583
+
584
+ #
585
+ # 58. formal parameter = parameter name ;
586
+ #
587
+ rule formal_parameter
588
+ parameter_name:name <FormalParameter>
589
+ end
590
+
591
+ #
592
+ # 59. context = "{" , [context entry , { "," , context entry } ] , "}" ;
593
+ #
594
+ rule context
595
+ '{' __ entries:(context_entry_list)? __ '}' <Context>
596
+ /
597
+ '{}' <Context>
598
+ end
599
+
600
+ rule context_entry_list
601
+ context_entry tail:(__ ',' __ context_entry)* ','? <ContextEntryList>
602
+ end
603
+
604
+ #
605
+ # 60. context entry = key , ":" , expression ;
606
+ #
607
+ rule context_entry
608
+ context_key:expression __ ':' __ context_value:expression <ContextEntry>
609
+ end
610
+
611
+ #
612
+ # 61. key = name | string literal ;
613
+ #
614
+
615
+ #
616
+ # 62. date time literal = ( "date" | "time" | "date and time" | "duration" ) , "(" , string literal , ")" ;
617
+ #
618
+ rule date_time_literal
619
+ keyword:date_time_keyword __ "(" __ head:expression __ tail:("," __ expression)* __ ")" <DateTimeLiteral>
620
+ end
621
+
622
+ rule date_time_keyword
623
+ "date and time" /
624
+ "time" /
625
+ "date" /
626
+ "duration"
627
+ end
628
+
629
+ rule keyword
630
+ true_token /
631
+ false_token /
632
+ null_token
633
+ end
634
+
635
+ rule true_token
636
+ "true" / "TRUE" / "True" !name_part_char
637
+ end
638
+
639
+ rule false_token
640
+ "false" / "FALSE" / "False" !name_part_char
641
+ end
642
+
643
+ rule null_token
644
+ "null" !name_part_char
645
+ end
646
+
647
+ rule __
648
+ white_space*
649
+ end
650
+
651
+ rule white_space
652
+ " " / "\t" / "\n" / "\r"
653
+ end
654
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpotFeel
4
+ VERSION = '0.0.1'
5
+ end
data/lib/spot_feel.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spot_feel/version"
4
+
5
+ require "awesome_print"
6
+
7
+ require "active_support"
8
+ require "active_support/duration"
9
+ require "active_support/time"
10
+ require "active_support/core_ext/hash"
11
+ require "active_support/core_ext/object/json"
12
+ require "active_support/configurable"
13
+
14
+ require "treetop"
15
+ require "xmlhasher"
16
+
17
+ require "spot_feel/configuration"
18
+ require "spot_feel/nodes"
19
+ require "spot_feel/parser"
20
+
21
+ require "spot_feel/dmn"
22
+
23
+ module SpotFeel
24
+ class SyntaxError < StandardError; end
25
+ class EvaluationError < StandardError; end
26
+
27
+ def self.evaluate(expression_text, variables: {})
28
+ literal_expression = Dmn::LiteralExpression.new(text: expression_text)
29
+ raise SyntaxError, "Expression is not valid" unless literal_expression.valid?
30
+ literal_expression.evaluate(variables)
31
+ end
32
+
33
+ def self.test(input, unary_tests_text, variables: {})
34
+ unary_tests = Dmn::UnaryTests.new(text: unary_tests_text)
35
+ raise SyntaxError, "Unary tests are not valid" unless unary_tests.valid?
36
+ unary_tests.test(input, variables)
37
+ end
38
+
39
+ def self.decide(decision_id, definitions: nil, definitions_json: nil, definitions_xml: nil, variables: {})
40
+ if definitions_xml.present?
41
+ definitions = Dmn::Definitions.from_xml(definitions_xml)
42
+ elsif definitions_json.present?
43
+ definitions = Dmn::Definitions.from_json(definitions_json)
44
+ end
45
+ definitions.evaluate(decision_id, variables: variables)
46
+ end
47
+
48
+ def self.definitions_from_xml(xml)
49
+ Dmn::Definitions.from_xml(xml)
50
+ end
51
+
52
+ def self.definitions_from_json(json)
53
+ Dmn::Definitions.from_json(json)
54
+ end
55
+
56
+ def self.config
57
+ @config ||= Configuration.new
58
+ end
59
+
60
+ def self.configure
61
+ yield(config)
62
+ end
63
+ end