@@ -59,70 +59,100 @@ class Gram
59
59
return res .to_s
60
60
end
61
61
62
- # Inline (ie. remove from the concrete grammar) some production
63
- # REQUIRE: no circular production in `prods`
64
- fun inline (prods : Collection [Production ])
62
+ # Check that prod does not depends on itself (no circular dependency).
63
+ fun can_inline (prod : Production ): Bool
65
64
do
66
- for p in self .prods do
67
- for a in p .alts .to_a do
68
- if a .phony then continue
69
- var to_inline = false
70
- for e in a .elems do
71
- if e isa Production and prods .has (e ) then to_inline = true
65
+ for a in prod .alts .to_a do
66
+ for e in a .elems do
67
+ if prod == e then
68
+ return false
72
69
end
73
- if not to_inline then continue
74
-
75
- if a .codes == null then a .make_codes
70
+ end
71
+ end
72
+ return true
73
+ end
76
74
77
- var a0 = new Alternative (p , a .name , new Array [Element ])
78
- a0 .trans = true
79
- a0 .codes = new Array [Code ]
80
- var pool = [a0 ]
81
- var pool2 = new Array [Alternative ]
82
- for e in a .elems do
83
- if not e isa Production or not prods .has (e ) then
84
- for x in pool do
85
- x .elems .add (e )
86
- x .codes .add (new CodePop )
75
+ # Inline `p = A | B` into `{a:} C . p D` produces 2 new alternatives `{a1:} C A D` and `{a2:} C B D`
76
+ # Note that A, B, C and D can contain p and will not be modified.
77
+ # Note also that `old_alt` will be removed form the CST.
78
+ fun inline_element (old_alt : Alternative , pos : Int )
79
+ do
80
+ var production = old_alt .elems [pos ] # The production to inline
81
+ assert production isa Production
82
+ var old_prod = old_alt .prod # It's production
83
+ if old_alt .codes == null then old_alt .make_codes
84
+
85
+ for alt in production .alts do
86
+ # For each alternative of the production to inline, create a new altednative based on the old one
87
+ var name = old_alt .name + "_i " + old_prod .alts .length .to_s
88
+ var new_alt = new Alternative (old_prod , name , new Array [Element ])
89
+ new_alt .trans = true
90
+ old_prod .alts .add (new_alt )
91
+ # All the element are the same, except the production replaced by the selected alternative
92
+ for i in [0 ..old_alt .elems .length [ do
93
+ if i == pos then
94
+ new_alt .elems .add_all (alt .elems )
95
+ else
96
+ var e = old_alt .elems [i ]
97
+ new_alt .elems .add (e )
98
+ end
99
+ end
100
+ # Codes should also be updated
101
+ # code in the old alternative might be shifted to correspond to the new position of the existing element
102
+ # code getting the must be replaced by the whole code of the inlined alternative, also shifted by the right amount
103
+ if alt .codes == null then alt .make_codes
104
+ new_alt .codes = new Array [Code ]
105
+ for code in old_alt .codes .as (not null ) do
106
+ if code isa CodeGet then
107
+ if code .pos == pos then
108
+ for code2 in alt .codes .as (not null ) do
109
+ new_alt .codes .add (code2 .shift (pos ))
87
110
end
88
- continue
111
+ else if code .pos >= pos then
112
+ # some elements are added but one (the inlined production) is removed
113
+ new_alt .codes .add (code .shift (alt .elems .length - 1 ))
114
+ else
115
+ new_alt .codes .add (code )
89
116
end
90
- if p == e then
91
- print "Circular inlining on {p } :: {a } "
92
- abort
93
- end
94
- pool2 .clear
95
- for a2 in e .alts do
96
- if a .phony then continue
97
- if a2 .codes == null then a2 .make_codes
98
- for x in pool do
99
- var name = a .name + "_ " + pool2 .length .to_s
100
- var na = new Alternative (p , name , new Array [Element ])
101
- na .trans = true
102
- pool2 .add (na )
103
- na .elems .add_all (x .elems )
104
- na .elems .add_all (a2 .elems )
105
- na .codes = new Array [Code ]
106
- na .codes .add_all (x .codes .as (not null ))
107
- na .codes .add_all (a2 .codes .as (not null ))
117
+ else
118
+ new_alt .codes .add (code )
119
+ end
120
+ end
121
+ #print "old «{old_alt}» {old_alt.codes or else "?"}"
122
+ #print "inl «{alt}» {alt.codes or else "?"}"
123
+ #print "new «{new_alt}» {new_alt.codes or else "?"}"
124
+ end
125
+ if not old_alt .trans then
126
+ old_prod .ast_alts .add (old_alt )
127
+ end
128
+ old_prod .alts .remove (old_alt )
129
+ end
130
+
131
+ # Inline all occurrences of a production and delete it from the CST.
132
+ # Require `can_inline(prod)`
133
+ fun inline_prod (prod : Production )
134
+ do
135
+ var changed = true
136
+ while changed do
137
+ changed = false
138
+ for p in self .prods do
139
+ for a in p .alts .to_a do
140
+ for i in [0 ..a .elems .length [ do
141
+ var e = a .elems [i ]
142
+ if e != prod then continue
143
+ if p == prod then
144
+ print "circular "
145
+ abort
108
146
end
147
+ inline_element (a , i )
148
+ changed = true
149
+ break
109
150
end
110
- var tmp = pool
111
- pool = pool2
112
- pool2 = tmp
113
- end
114
- for x in pool do
115
- x .codes .add (a .codes .last )
116
151
end
117
- p .ast_alts .add (a )
118
- p .alts .remove (a )
119
- p .alts .add_all (pool )
120
152
end
121
153
end
122
- for p in prods do
123
- self .prods .remove (p )
124
- self .ast_prods .add (p )
125
- end
154
+ self .prods .remove (prod )
155
+ self .ast_prods .add (prod )
126
156
end
127
157
128
158
# The starting production in the augmented grammar
@@ -312,9 +342,9 @@ class Gram
312
342
prod .acname = "Nodes[ {e .acname } ] "
313
343
prods .add (prod )
314
344
var alt1 = prod .new_alt ("{cname } _one " , e )
315
- alt1 .codes = [new CodeNewNodes (alt1 ), new CodePop , new CodeAdd : Code ]
345
+ alt1 .codes = [new CodeNewNodes (alt1 ), new CodeGet ( 0 ) , new CodeAdd : Code ]
316
346
var alt2 = prod .new_alt ("{cname } _more " , prod , e )
317
- alt2 .codes = [new CodePop , new CodePop , new CodeAdd : Code ]
347
+ alt2 .codes = [new CodeGet ( 0 ) , new CodeGet ( 1 ) , new CodeAdd : Code ]
318
348
plusizes [e ] = prod
319
349
return prod
320
350
end
@@ -343,7 +373,7 @@ class Gram
343
373
prod .acname = "nullable {e .acname } "
344
374
prods .add (prod )
345
375
var a1 = prod .new_alt ("{cname } _one " , e )
346
- a1 .codes = [new CodePop ]
376
+ a1 .codes = [new CodeGet ( 0 ) ]
347
377
var a0 = prod .new_alt0 ("{cname } _none " )
348
378
a0 .codes = [new CodeNull ]
349
379
quesizes [e ] = prod
@@ -571,8 +601,10 @@ class Alternative
571
601
if codes != null then return
572
602
var codes = new Array [Code ]
573
603
self .codes = codes
604
+ var i = 0
574
605
for e in elems do
575
- codes .add (new CodePop )
606
+ codes .add (new CodeGet (i ))
607
+ i += 1
576
608
end
577
609
codes .add (new CodeNew (self ))
578
610
end
@@ -581,12 +613,15 @@ end
581
613
# A step in the construction of the AST.
582
614
# Used to model transformations
583
615
interface Code
616
+ # self or a CodeGet increased by `d`. Is used by inlining.
617
+ fun shift (d : Int ): Code do return self
584
618
end
585
619
586
- # Get a element from the stack
587
- class CodePop
620
+ class CodeGet
588
621
super Code
589
- redef fun to_s do return "pop "
622
+ var pos : Int
623
+ redef fun to_s do return "get {pos } "
624
+ redef fun shift (d ) do return new CodeGet (pos +d )
590
625
end
591
626
592
627
# Allocate a new AST node for an alternative using the correct number of popped elements
0 commit comments