sfp 0.3.4 → 0.3.5

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