sfp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+