@@ -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
583
613
# A step in the construction of the AST.
584
614
# Used to model transformations
585
615
interface Code
616
+ # self or a CodeGet increased by `d`. Is used by inlining.
617
+ fun shift (d : Int ): Code do return self
586
618
end
587
619
588
620
# Get a element from the stack
@@ -595,6 +627,7 @@ class CodeGet
595
627
super Code
596
628
var pos : Int
597
629
redef fun to_s do return "get {pos } "
630
+ redef fun shift (d ) do return new CodeGet (pos +d )
598
631
end
599
632
600
633
# Allocate a new AST node for an alternative using the correct number of popped elements
0 commit comments