Skip to content

Commit 9615dac

Browse files
committed
nitcc: new inline method
Signed-off-by: Jean Privat <[email protected]>
1 parent b212f35 commit 9615dac

File tree

2 files changed

+89
-56
lines changed

2 files changed

+89
-56
lines changed

contrib/nitcc/src/grammar.nit

+87-54
Original file line numberDiff line numberDiff line change
@@ -59,70 +59,100 @@ class Gram
5959
return res.to_s
6060
end
6161

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
6564
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
7269
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
7674

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))
87110
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)
89116
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
108146
end
147+
inline_element(a, i)
148+
changed = true
149+
break
109150
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)
116151
end
117-
p.ast_alts.add(a)
118-
p.alts.remove(a)
119-
p.alts.add_all(pool)
120152
end
121153
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)
126156
end
127157

128158
# The starting production in the augmented grammar
@@ -583,6 +613,8 @@ end
583613
# A step in the construction of the AST.
584614
# Used to model transformations
585615
interface Code
616+
# self or a CodeGet increased by `d`. Is used by inlining.
617+
fun shift(d: Int): Code do return self
586618
end
587619

588620
# Get a element from the stack
@@ -595,6 +627,7 @@ class CodeGet
595627
super Code
596628
var pos: Int
597629
redef fun to_s do return "get{pos}"
630+
redef fun shift(d) do return new CodeGet(pos+d)
598631
end
599632

600633
# Allocate a new AST node for an alternative using the correct number of popped elements

contrib/nitcc/src/nitcc_semantic.nit

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ class CollectNameVisitor
5959
v2.enter_visit(n)
6060

6161
# Inline all the `?`
62-
gram.inline(v2.gram.quesizes.values)
62+
for p in v2.gram.quesizes.values do gram.inline_prod(p)
6363
# Inline all the prods suffixed by '_inline' #TODO use a real keyword
6464
for p in gram.prods do
6565
if not p.name.has_suffix("_inline") then continue
6666
print "inline {p}"
67-
gram.inline([p])
67+
gram.inline_prod(p)
6868
end
6969

7070
# Build the NFA automaton

0 commit comments

Comments
 (0)