sfp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/SfpLang.g ADDED
@@ -0,0 +1,1005 @@
1
+ grammar SfpLang;
2
+
3
+ options {
4
+ language = Ruby;
5
+ output = AST;
6
+ }
7
+
8
+ @headers {
9
+ =begin
10
+ Depends:
11
+ - sfplibs.rb
12
+ - ext.rb
13
+
14
+ Features:
15
+ - reference
16
+ - object namespace
17
+ - SET abstract data-type and membership operators (in, not in, add, remove)
18
+ - numeric comparators (>, >=, <, <=) and mutation operators (+, -, *, /)
19
+ - new object
20
+ - procedure's cost
21
+ - constraint/mutation iterator for Set of values or objects of particular class
22
+ - Set of all-objects of particular class as procedure's parameter
23
+ - include file
24
+ - constraint namespace
25
+ - total-constraint
26
+ - ARRAY abstract data-type, index operator
27
+
28
+ TODO:
29
+ - ARRAY enumerator operator
30
+ - provenance
31
+ - state-dependency (supports multiple conditions using 'or' keyword)
32
+ =end
33
+
34
+ }
35
+
36
+ @members {
37
+ include Sfp::SfpLangHelper
38
+ }
39
+
40
+ sfp
41
+ : { self.init }
42
+ NL* include* header*
43
+ { self.expand_classes }
44
+ (state | composite | constraint | goal_constraint)*
45
+ ;
46
+
47
+ include
48
+ : 'include' include_file NL+
49
+ ;
50
+
51
+ include_file
52
+ : STRING
53
+ { self.process_file($STRING.text[1,$STRING.text.length-2]) }
54
+ ;
55
+
56
+ header
57
+ : class_definition
58
+ | procedure
59
+ ;
60
+
61
+ state
62
+ : ID ('state')?
63
+ {
64
+ @now[$ID.text] = { '_self' => $ID.text,
65
+ '_context' => 'state',
66
+ '_parent' => @now
67
+ }
68
+ @now = @now[$ID.text]
69
+ }
70
+ '{' NL*
71
+ attribute*
72
+ '}' NL*
73
+ { self.goto_parent(true) }
74
+ ;
75
+
76
+ composite
77
+ : 'composite' ID
78
+ {
79
+ @now[$ID.text] = { '_self' => $ID.text,
80
+ '_context' => 'composite',
81
+ '_parent' => @now
82
+ }
83
+ @now = @now[$ID.text]
84
+ }
85
+ '{' NL* ( attribute | constraint )* '}' NL*
86
+ { self.goto_parent(true) }
87
+ ;
88
+
89
+ class_definition
90
+ : 'class' ID
91
+ {
92
+ @now[$ID.text] = { '_self' => $ID.text,
93
+ '_context' => 'class',
94
+ '_parent' => @now
95
+ }
96
+ @now = @now[$ID.text]
97
+ }
98
+ (extends_class
99
+ {
100
+ @now['_extends'] = $extends_class.val
101
+ }
102
+ )?
103
+ ('{' NL* ( attribute | procedure )* '}')? NL*
104
+ { self.goto_parent() }
105
+ ;
106
+
107
+ extends_class returns [val]
108
+ : 'extends' path
109
+ {
110
+ $val = self.to_ref($path.text)
111
+ @unexpanded_classes.push(@now)
112
+ }
113
+ ;
114
+
115
+ attribute
116
+ : ID equals_op value NL+
117
+ {
118
+ if @now.has_key?($ID.text) and @now[$ID.text].is_a?(Hash) and
119
+ @now[$ID.text].isset and $value.type == 'Set'
120
+ $value.val.each { |v| @now[$ID.text]['_values'].push(v) }
121
+ else
122
+ @now[$ID.text] = $value.val
123
+ end
124
+ }
125
+ | ID reference_type NL+
126
+ { @now[$ID.text] = $reference_type.val }
127
+ | ID set_type NL+
128
+ { @now[$ID.text] = $set_type.val }
129
+ | ID probability_op set_value NL+
130
+ {
131
+ @conformant = true
132
+ @now[$ID.text] = { '_self' => $ID.text,
133
+ '_context' => 'either',
134
+ '_parent' => @now,
135
+ '_values' => $set_value.val
136
+ }
137
+ }
138
+ | object_def NL+
139
+ ;
140
+
141
+ object_def
142
+ : ID
143
+ {
144
+ @now[$ID.text] = { '_self' => $ID.text,
145
+ '_context' => 'object',
146
+ '_parent' => @now,
147
+ '_isa' => '$.Object'
148
+ }
149
+ @now = @now[$ID.text]
150
+ @now['_is_array'] = false
151
+ }
152
+ ('isa' path('[' NUMBER { @now['_is_array'] = true } ']')?
153
+ {
154
+ @now['_isa'] = self.to_ref($path.text)
155
+ self.expand_object(@now)
156
+ }
157
+ )?
158
+ object_body?
159
+ {
160
+ if @now['_is_array']
161
+ @now.delete('_is_array')
162
+ obj = self.goto_parent()
163
+ total = $NUMBER.to_s.to_i
164
+ @arrays[obj.ref] = total
165
+ for i in 0..(total-1)
166
+ id = obj['_self'] + "[#{i}]"
167
+ @now[id] = deep_clone(obj)
168
+ @now[id]['_self'] = id
169
+ @now[id]['_classes'] = obj['_classes']
170
+ #puts 'is_array: ' + $ID.text + '[' + i.to_s + ']'
171
+ end
172
+ @now.delete(obj['_self'])
173
+ else
174
+ self.goto_parent()
175
+ end
176
+ }
177
+ ;
178
+
179
+ object_body
180
+ : '{' NL* ( object_attribute | state_dependency | operator )* '}'
181
+ ;
182
+
183
+ object_attribute
184
+ : attribute
185
+ | ID equals_op NULL NL+
186
+ { @now[$ID.text] = self.null_value }
187
+ ;
188
+
189
+ state_dependency
190
+ : 'if'
191
+ dep_effect NL* 'then' NL* '{'
192
+ NL* constraint_body
193
+ '}'
194
+ ( NL* 'or' NL* '{'
195
+ NL* constraint_body
196
+ '}')*
197
+ NL+
198
+ ;
199
+
200
+ dep_effect
201
+ : reference equals_op
202
+ ( value
203
+ | NULL
204
+ )
205
+ ;
206
+
207
+ operator
208
+ : 'operator' ID '{' NL*
209
+ {
210
+ @now[$ID.text] = { '_self' => $ID.text,
211
+ '_context' => 'operator',
212
+ '_parent' => @now,
213
+ '_cost' => 1,
214
+ '_condition' => { '_context' => 'constraint' },
215
+ '_effect' => { '_context' => 'mutation' }
216
+ }
217
+ @now = @now[$ID.text]
218
+ }
219
+ ( 'cost' equals_op NUMBER NL+
220
+ { @now['_cost'] = $NUMBER.text.to_i }
221
+ )?
222
+ /*op_param**/
223
+ op_conditions? op_effects
224
+ '}' NL+
225
+ { self.goto_parent() }
226
+ ;
227
+
228
+ op_param
229
+ : ID equals_op reference NL+
230
+ { @now[$ID.text] = $reference.val }
231
+ ;
232
+
233
+ op_conditions
234
+ : 'conditions' '{' NL*
235
+ {
236
+ @now['_condition']['_parent'] = @now
237
+ @now = @now['_condition']
238
+ }
239
+ op_statement*
240
+ '}' NL+
241
+ { self.goto_parent() }
242
+ ;
243
+
244
+ op_effects
245
+ : 'effects' '{' NL*
246
+ {
247
+ @now['_effect']['_parent'] = @now
248
+ @now = @now['_effect']
249
+ }
250
+ op_statement*
251
+ '}' NL+
252
+ { self.goto_parent() }
253
+ ;
254
+
255
+ op_statement
256
+ : reference equals_op value NL+
257
+ { @now[$reference.val] = $value.val }
258
+ ;
259
+
260
+ procedure
261
+ : 'procedure' ID
262
+ {
263
+ @now[$ID.text] = { '_self' => $ID.text,
264
+ '_context' => 'procedure',
265
+ '_parent' => @now,
266
+ '_cost' => 1,
267
+ '_condition' => { '_context' => 'constraint', '_type' => 'and' },
268
+ '_effect' => { '_context' => 'mutation', '_type' => 'and' }
269
+ }
270
+ @now = @now[$ID.text]
271
+ }
272
+ parameters? '{' NL*
273
+ ( 'cost' equals_op NUMBER
274
+ { @now['_cost'] = $NUMBER.text.to_i }
275
+ NL+
276
+ )?
277
+ conditions? effects '}' NL+
278
+ { self.goto_parent() }
279
+ ;
280
+
281
+ parameters
282
+ : '(' parameter (',' NL* parameter)* ')'
283
+ ;
284
+
285
+ parameter
286
+ : ID reference_type
287
+ { @now[$ID.text] = $reference_type.val }
288
+ | ID 'areall' path
289
+ {
290
+ @now[$ID.text] = { '_context' => 'all',
291
+ '_isa' => self.to_ref($path.text),
292
+ '_value' => nil
293
+ }
294
+ }
295
+ | ID 'isset' path
296
+ {
297
+ @now[$ID.text] = { '_context' => 'set',
298
+ '_isa' => self.to_ref($path.text),
299
+ '_values' => []
300
+ }
301
+ }
302
+ ;
303
+
304
+ conditions
305
+ : 'conditions'
306
+ {
307
+ @now['_condition']['_parent'] = @now
308
+ @now = @now['_condition']
309
+ }
310
+ '{' NL* constraint_body '}' NL+
311
+
312
+ { self.goto_parent() }
313
+ ;
314
+
315
+ effects
316
+ : 'effects'
317
+ {
318
+ @now['_effect']['_parent'] = @now
319
+ @now = @now['_effect']
320
+ }
321
+ '{' NL*
322
+ mutation_body
323
+ '}' NL+
324
+ { self.goto_parent() }
325
+ ;
326
+
327
+ goal_constraint
328
+ : 'goal' 'constraint'? NL*
329
+ {
330
+ @now['goal'] = { '_self' => 'goal',
331
+ '_context' => 'constraint',
332
+ '_type' => 'and',
333
+ '_parent' => @now
334
+ }
335
+ @now = @now['goal']
336
+ }
337
+ '{' NL* goal_body* '}' NL+
338
+ { self.goto_parent() }
339
+ ;
340
+
341
+ goal_body
342
+ : (
343
+ ( constraint_statement
344
+ {
345
+ @now[$constraint_statement.key] = $constraint_statement.val
346
+ }
347
+ | constraint_namespace
348
+ | constraint_iterator
349
+ | constraint_class_quantification
350
+ )
351
+ NL+)
352
+ | 'always' NL*
353
+ {
354
+ @now['always'] = self.create_constraint('always', 'and') if
355
+ not @now.has_key?('always')
356
+ @now = @now['always']
357
+ }
358
+ '{' NL* constraint_body '}' NL+
359
+ { self.goto_parent() }
360
+ | 'sometime' NL*
361
+ {
362
+ @now['sometime'] = self.create_constraint('sometime', 'or') if
363
+ not @now.has_key?('sometime')
364
+ @now = @now['sometime']
365
+ id = self.next_id.to_s
366
+ @now[id] = self.create_constraint(id, 'and')
367
+ @now = @now[id]
368
+ }
369
+ '{' NL* constraint_body '}' NL+
370
+ { self.goto_parent() }
371
+ { self.goto_parent() }
372
+ | 'within' NUMBER NL*
373
+ {
374
+ id = self.next_id.to_s
375
+ @now[id] = self.create_constraint(id, 'within')
376
+ @now = @now[id]
377
+ @now['deadline'] = $NUMBER.text.to_s.to_i
378
+ }
379
+ '{' NL* constraint_body '}' NL+
380
+ { self.goto_parent() }
381
+ | 'after' NL*
382
+ {
383
+ @now['sometime-after'] = self.create_constraint('sometime-after', 'or') if
384
+ not @now.has_key?('sometime-after')
385
+ @now = @now['sometime-after']
386
+
387
+ id = self.next_id.to_s
388
+ @now[id] = self.create_constraint(id, 'sometime-after')
389
+ @now = @now[id]
390
+ @now['after'] = self.create_constraint('after')
391
+ @now['deadline'] = -1
392
+ @now = @now['after']
393
+ }
394
+ '{' NL* constraint_body '}' NL*
395
+ { self.goto_parent() }
396
+ ( 'then'
397
+ | 'within' NUMBER
398
+ { @now['deadline'] = $NUMBER.text.to_s.to_i }
399
+ ) NL*
400
+ {
401
+ @now['then'] = self.create_constraint('then')
402
+ @now = @now['then']
403
+ }
404
+ '{' NL* constraint_body '}' NL+
405
+ { self.goto_parent() }
406
+ { self.goto_parent() }
407
+ { self.goto_parent() }
408
+ | 'before' NL*
409
+ {
410
+ id = self.next_id.to_s
411
+ @now[id] = self.create_constraint(id, 'sometime-before')
412
+ @now = @now[id]
413
+ @now['before'] = self.create_constraint('before')
414
+ @now = @now['before']
415
+ }
416
+ '{' NL* constraint_body '}' NL*
417
+ { self.goto_parent() }
418
+ 'then' NL*
419
+ {
420
+ @now['then'] = self.create_constraint('then')
421
+ @now = @now['then']
422
+ }
423
+ '{' NL* constraint_body '}' NL+
424
+ { self.goto_parent() }
425
+ { self.goto_parent() }
426
+ ;
427
+
428
+ nested_constraint
429
+ : '{' NL* constraint_body '}'
430
+ ;
431
+
432
+ constraint
433
+ : ID 'constraint'
434
+ {
435
+ @now[$ID.text] = { '_self' => $ID.text,
436
+ '_context' => 'constraint',
437
+ '_type' => 'and',
438
+ '_parent' => @now
439
+ }
440
+ @now = @now[$ID.text]
441
+ }
442
+ '{' NL* constraint_body '}' NL+
443
+ { self.goto_parent() }
444
+ ;
445
+
446
+ constraint_body
447
+ : (
448
+ ( constraint_statement
449
+ {
450
+ @now[$constraint_statement.key] = $constraint_statement.val
451
+ }
452
+ | constraint_namespace
453
+ | constraint_iterator
454
+ | constraint_class_quantification
455
+ )
456
+ NL+)*
457
+ ;
458
+
459
+ constraint_namespace
460
+ : path NL* '{' NL* (constraint_statement
461
+ {
462
+ key = self.to_ref($path.text + '.' + $constraint_statement.key[2,$constraint_statement.key.length])
463
+ @now[key] = $constraint_statement.val
464
+ }
465
+ NL+)* '}'
466
+ ;
467
+
468
+ constraint_iterator
469
+ : 'foreach' '(' path 'as' ID ')' NL* '{' NL+
470
+ {
471
+ id = self.next_id.to_s
472
+ @now[id] = { '_parent' => @now,
473
+ '_context' => 'constraint',
474
+ '_type' => 'iterator',
475
+ '_self' => id,
476
+ '_value' => '$.' + $path.text,
477
+ '_variable' => $ID.text
478
+ }
479
+ @now = @now[id]
480
+
481
+ id = '_template'
482
+ @now[id] = { '_parent' => @now,
483
+ '_context' => 'constraint',
484
+ '_type' => 'and',
485
+ '_self' => id,
486
+ }
487
+ @now = @now[id]
488
+ }
489
+ (constraint_statement
490
+ {
491
+ @now[$constraint_statement.key] = $constraint_statement.val
492
+ }
493
+ NL+)*
494
+ '}'
495
+ {
496
+ self.goto_parent()
497
+ self.goto_parent()
498
+ }
499
+ ;
500
+
501
+ quantification_keyword
502
+ : 'forall'
503
+ | 'exist'
504
+ | 'forsome'
505
+ ;
506
+
507
+ constraint_class_quantification
508
+ : quantification_keyword '(' path 'as' ID ')'
509
+ {
510
+ id = self.next_id.to_s
511
+ @now[id] = { '_parent' => @now,
512
+ '_context' => 'constraint',
513
+ '_type' => $quantification_keyword.text,
514
+ '_self' => id,
515
+ '_class' => $path.text,
516
+ '_variable' => $ID.text
517
+ }
518
+ @now = @now[id]
519
+
520
+ id = '_template'
521
+ @now[id] = { '_parent' => @now,
522
+ '_context' => 'constraint',
523
+ '_type' => 'and',
524
+ '_self' => id
525
+ }
526
+ @now = @now[id]
527
+ }
528
+ ( ( binary_comp
529
+ { @now['_count_operator'] = $binary_comp.text }
530
+ | '='
531
+ { @now['_count_operator'] = '=' }
532
+ )
533
+ NUMBER
534
+ { @now['_count_value'] = $NUMBER.text.to_i }
535
+ )?
536
+ NL* '{' NL+
537
+ ( constraint_statement
538
+ { @now[$constraint_statement.key] = $constraint_statement.val }
539
+ NL+
540
+ | constraint_different NL+
541
+ | constraint_iterator NL+
542
+ )* '}'
543
+ { self.goto_parent() }
544
+ { self.goto_parent() }
545
+ ;
546
+
547
+ constraint_different
548
+ : ':different' '(' path ')'
549
+ {
550
+ id = self.next_id.to_s
551
+ @now[id] = { '_parent' => @now,
552
+ '_context' => 'constraint',
553
+ '_type' => 'different',
554
+ '_self' => id,
555
+ '_path' => $path.text
556
+ }
557
+ }
558
+ ;
559
+
560
+ constraint_statement returns [key, val]
561
+ : reference
562
+ {
563
+ $key = $reference.val
564
+ $val = { '_context' => 'constraint', '_type' => 'equals', '_value' => true }
565
+ }
566
+ | 'not' reference
567
+ {
568
+ $key = $reference.val
569
+ $val = { '_context' => 'constraint', '_type' => 'equals', '_value' => false }
570
+ }
571
+ | reference equals_op value
572
+ {
573
+ $key = $reference.val
574
+ $val = { '_context' => 'constraint', '_type' => 'equals', '_value' => $value.val }
575
+ }
576
+ | reference equals_op NULL
577
+ {
578
+ $key = $reference.val
579
+ $val = { '_context' => 'constraint', '_type' => 'equals', '_value' => self.null_value }
580
+ }
581
+ | reference not_equals_op value
582
+ {
583
+ $key = $reference.val
584
+ $val = { '_context' => 'constraint', '_type' => 'not-equals', '_value' => $value.val }
585
+ }
586
+ | reference not_equals_op NULL
587
+ {
588
+ $key = $reference.val
589
+ $val = { '_context' => 'constraint', '_type' => 'not-equals', '_value' => self.null_value }
590
+ }
591
+ | conditional_constraint
592
+ {
593
+ $key = $conditional_constraint.key
594
+ $val = $conditional_constraint.val
595
+ }
596
+ | reference 'is'? 'in' set_value
597
+ {
598
+ c_or = { '_context' => 'constraint', '_type' => 'or', '_parent' => @now }
599
+ $set_value.val.each { |v|
600
+ id = self.next_id.to_s
601
+ item = { '_context' => 'constraint', '_type' => 'and', '_parent' => c_or }
602
+ item[$reference.val] = { '_context' => 'constraint', '_type' => 'equals', '_value' => v }
603
+ c_or[id] = item
604
+ }
605
+ $key = self.next_id.to_s
606
+ $val = c_or
607
+ }
608
+ | reference ('isnot'|'isnt'|'not') 'in' set_value
609
+ {
610
+ c_and = { '_context'=>'constraint', '_type'=>'and', '_parent'=>@now }
611
+ $set_value.val.each { |v|
612
+ id = self.next_id.to_s
613
+ item = { '_context'=>'constraint', '_type'=>'and'}
614
+ item[$reference.val] = { '_context'=>'constraint', '_type'=>'not-equals', '_value'=>v }
615
+ c_and[id] = item
616
+ }
617
+ $key = self.next_id.to_s
618
+ $val = c_and
619
+
620
+ #$key = $reference.val
621
+ #$val = { '_context' => 'constraint', '_type' => 'not-in', '_value' => $set_value.val }
622
+ }
623
+ | reference 'has' value
624
+ {
625
+ c_has = { '_context' => 'constraint',
626
+ '_type' => 'has',
627
+ '_parent' => @now,
628
+ '_owner' => $reference.val,
629
+ '_value' => $value.val
630
+ }
631
+ }
632
+ | reference binary_comp comp_value
633
+ {
634
+ $key = $reference.val
635
+ $val = { '_context' => 'constraint', '_type' => $binary_comp.text, '_value' => $comp_value.val }
636
+ }
637
+ | total_constraint
638
+ ;
639
+
640
+ total_constraint
641
+ : 'total(' total_statement ')' binary_comp NUMBER
642
+ ;
643
+
644
+ total_statement
645
+ : reference equals_op value
646
+ ;
647
+
648
+ comp_value returns [val]
649
+ : NUMBER
650
+ { $val = $NUMBER.text.to_i }
651
+ | reference
652
+ { $val = $reference.val }
653
+ ;
654
+
655
+ conditional_constraint returns [key, val]
656
+ : 'if'
657
+ {
658
+ $key = id = self.next_id.to_s
659
+ @now[id] = { '_parent' => @now,
660
+ '_context' => 'constraint',
661
+ '_type' => 'or'
662
+ }
663
+ @now = @now[id]
664
+ }
665
+ conditional_constraint_if_part
666
+ conditional_constraint_then_part
667
+ { $val = self.goto_parent() }
668
+ ;
669
+
670
+ conditional_constraint_if_part
671
+ : constraint_statement NL*
672
+ {
673
+ id = self.next_id
674
+ @now[id] = { '_parent' => @now,
675
+ '_context' => 'constraint',
676
+ '_type' => 'not'
677
+ }
678
+ @now[id][$constraint_statement.key] = $constraint_statement.val
679
+ }
680
+ | '{'
681
+ {
682
+ id = self.next_id
683
+ @now[id] = self.create_constraint(id, 'not')
684
+ @now = @now[id]
685
+ }
686
+ NL+ constraint_body
687
+ '}' NL*
688
+ { self.goto_parent() }
689
+ ;
690
+
691
+ conditional_constraint_then_part
692
+ : 'then' constraint_statement
693
+ {
694
+ id = self.next_id
695
+ @now[id] = { '_parent' => @now,
696
+ '_context' => 'constraint',
697
+ '_type' => 'and'
698
+ }
699
+ @now[id][$constraint_statement.key] = $constraint_statement.val
700
+ }
701
+ | 'then'
702
+ {
703
+ id = self.next_id
704
+ @now[id] = { '_parent' => @now,
705
+ '_context' => 'constraint',
706
+ '_type' => 'and'
707
+ }
708
+ @now = @now[id]
709
+ }
710
+ '{' NL+ constraint_body '}'
711
+ { self.goto_parent() }
712
+ ;
713
+
714
+ mutation_body
715
+ : (
716
+ ( mutation_statement
717
+ { @now[$mutation_statement.key] = $mutation_statement.val }
718
+ | mutation_iterator
719
+ )
720
+ NL+)*
721
+ ;
722
+
723
+ mutation_iterator
724
+ : 'foreach' path 'as' ID NL* '{' NL+
725
+ {
726
+ id = self.to_ref($path.text)
727
+ @now[id] = { '_parent' => @now,
728
+ '_context' => 'iterator',
729
+ '_self' => id,
730
+ '_variable' => $ID.text
731
+ }
732
+ @now = @now[id]
733
+ }
734
+ (mutation_statement
735
+ { @now[$mutation_statement.key] = $mutation_statement.val }
736
+ NL+)*
737
+ '}'
738
+ { self.goto_parent() }
739
+ ;
740
+
741
+ mutation_statement returns [key, val]
742
+ : reference equals_op value
743
+ {
744
+ $key = $reference.val
745
+ $val = { '_context' => 'mutation',
746
+ '_type' => 'equals',
747
+ '_value' => $value.val
748
+ }
749
+ }
750
+ | reference equals_op NULL
751
+ {
752
+ $key = $reference.val
753
+ $val = { '_context' => 'mutation',
754
+ '_type' => 'equals',
755
+ '_value' => self.null_value
756
+ }
757
+ }
758
+ | reference binary_op NUMBER
759
+ {
760
+ $key = $reference.val
761
+ $val = { '_context' => 'mutation',
762
+ '_type' => $binary_op.text,
763
+ '_value' => $NUMBER.text.to_i
764
+ }
765
+ }
766
+ | reference 'is' 'new' path
767
+ {
768
+ id = '_' + self.next_id
769
+ @now[id] = { '_self' => id,
770
+ '_context' => 'object',
771
+ '_isa' => self.to_ref($path.text),
772
+ '_parent' => @now
773
+ }
774
+ @now = @now[id]
775
+ }
776
+ object_body?
777
+ {
778
+ n = self.goto_parent()
779
+ @now.delete(n['_self'])
780
+ $key = $reference.val
781
+ $val = n
782
+ }
783
+ | 'delete' path
784
+ {
785
+ id = '_' + self.next_id
786
+ @now[id] = { '_self' => id,
787
+ '_context' => 'mutation',
788
+ '_type' => 'delete',
789
+ '_value' => self.to_ref($path.text)
790
+ }
791
+ }
792
+ | reference 'add(' value ')'
793
+ {
794
+ $key = $reference.val
795
+ $val = { '_context' => 'mutation',
796
+ '_type' => 'add',
797
+ '_value' => $value.val
798
+ }
799
+ }
800
+ | reference 'remove(' value ')'
801
+ {
802
+ $key = $reference.val
803
+ $val = { '_context' => 'mutation',
804
+ '_type' => 'remove',
805
+ '_value' => $value.val
806
+ }
807
+ }
808
+ ;
809
+
810
+ set_value returns [val]
811
+ : '('
812
+ { @set = Array.new }
813
+ (set_item (',' NL* set_item)*)?
814
+ { $val = @set }
815
+ ')'
816
+ ;
817
+
818
+ set_item
819
+ : value
820
+ { @set.push($value.val) }
821
+ ;
822
+
823
+ value returns [val, type]
824
+ : primitive_value
825
+ {
826
+ $val = $primitive_value.val
827
+ $type = $primitive_value.type
828
+ }
829
+ | reference
830
+ {
831
+ $val = $reference.val
832
+ $type = 'Reference'
833
+ }
834
+ | set_value
835
+ {
836
+ $val = $set_value.val
837
+ $type = 'Set'
838
+ }
839
+ ;
840
+
841
+ primitive_value returns [val, type]
842
+ : BOOLEAN
843
+ {
844
+ if $BOOLEAN.text == 'true' or $BOOLEAN.text == 'on' or $BOOLEAN.text == 'yes'
845
+ $val = true
846
+ else # 'false', 'no', 'off'
847
+ $val = false
848
+ end
849
+ $type = 'Boolean'
850
+ }
851
+ | NUMBER
852
+ {
853
+ $val = $NUMBER.text.to_i
854
+ $type = 'Number'
855
+ }
856
+ | STRING
857
+ {
858
+ $val = $STRING.text[1,$STRING.text.length-2]
859
+ $type = 'String'
860
+ }
861
+ | MULTILINE_STRING
862
+ {
863
+ $val = $MULTILINE_STRING.text[2, $MULTILINE_STRING.text.length-2]
864
+ $type = 'String'
865
+ }
866
+ ;
867
+
868
+ path
869
+ : ID('.'ID)*
870
+ ;
871
+
872
+ path_with_index
873
+ : id_ref('.'id_ref)*
874
+ ;
875
+
876
+ id_ref
877
+ : ID('[' NUMBER ']')?
878
+ ;
879
+
880
+ reference returns [val]
881
+ : path_with_index
882
+ { $val = self.to_ref($path_with_index.text) }
883
+ ;
884
+
885
+ reference_type returns [val]
886
+ : 'isref' path
887
+ {
888
+ $val = { '_context' => 'null',
889
+ '_isa' => self.to_ref($path.text)
890
+ }
891
+ }
892
+ ;
893
+
894
+ set_type returns [val]
895
+ : 'isset' path
896
+ {
897
+ $val = { '_context' => 'set',
898
+ '_isa' => self.to_ref($path.text),
899
+ '_values' => []
900
+ }
901
+ }
902
+ ;
903
+
904
+ probability_op
905
+ : 'either'
906
+ ;
907
+
908
+ equals_op
909
+ : '='
910
+ | 'is'
911
+ ;
912
+
913
+ not_equals_op
914
+ : '!='
915
+ | 'isnt'
916
+ | 'isnot'
917
+ ;
918
+
919
+ binary_op
920
+ : '+='
921
+ | '-='
922
+ | '*='
923
+ | '/='
924
+ ;
925
+
926
+ binary_comp
927
+ : '>'
928
+ | '>='
929
+ | '<'
930
+ | '<='
931
+ ;
932
+
933
+ NULL
934
+ : 'null'
935
+ | 'nil'
936
+ ;
937
+
938
+ BOOLEAN
939
+ : 'true'
940
+ | 'false'
941
+ | 'off'
942
+ | 'on'
943
+ | 'yes'
944
+ | 'no'
945
+ ;
946
+
947
+ ID : ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-')*
948
+ ;
949
+
950
+ NUMBER
951
+ : '-'?('0'..'9')+
952
+ ;
953
+
954
+ /*NUMBER
955
+ : '-'?('0'..'9')+
956
+ | '-'?('0'..'9')+'.'('0'..'9')* EXPONENT?
957
+ | '-'?'.'('0'..'9')+ EXPONENT?
958
+ | '-'?('0'..'9')+ EXPONENT
959
+ ;*/
960
+
961
+ COMMENT
962
+ : '//' ~('\n'|'\r')* {$channel=HIDDEN;}
963
+ | '#' ~('\n'|'\r')* {$channel=HIDDEN;}
964
+ | '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
965
+ ;
966
+
967
+ MULTILINE_STRING
968
+ : 'r"' ( options {greedy=false;} : .)* '"'
969
+ ;
970
+
971
+ NL : '\r'? '\n'
972
+ ;
973
+
974
+ WS : ( ' ' | '\t' ) {$channel=HIDDEN;}
975
+ ;
976
+
977
+ STRING
978
+ : '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
979
+ ;
980
+
981
+ fragment
982
+ EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
983
+
984
+ fragment
985
+ HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
986
+
987
+ fragment
988
+ ESC_SEQ
989
+ : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
990
+ | UNICODE_ESC
991
+ | OCTAL_ESC
992
+ ;
993
+
994
+ fragment
995
+ OCTAL_ESC
996
+ : '\\' ('0'..'3') ('0'..'7') ('0'..'7')
997
+ | '\\' ('0'..'7') ('0'..'7')
998
+ | '\\' ('0'..'7')
999
+ ;
1000
+
1001
+ fragment
1002
+ UNICODE_ESC
1003
+ : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
1004
+ ;
1005
+