sfp 0.3.4 → 0.3.5

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.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
+