diff --git a/contrib/nitcc/src/grammar.nit b/contrib/nitcc/src/grammar.nit index f4b724b791..50010b636a 100644 --- a/contrib/nitcc/src/grammar.nit +++ b/contrib/nitcc/src/grammar.nit @@ -125,10 +125,14 @@ class Gram end end - # Compute a LR automaton - fun lr0: LRAutomaton + # The starting production in the augmented grammar + var start: nullable Production = null + + # Extends the grammar and compute information on productions + fun prepare_for_automaton do var start = new Production("_start") + self.start = start start.accept = true var eof = new Token("Eof") tokens.add(eof) @@ -136,80 +140,6 @@ class Gram prods.add(start) analyse - - var first = new LRState - first.number = 0 - for i in start.start_state do first.add(i) - - var automaton = new LRAutomaton(self) - - var todo = new List[LRState] - todo.add first - var seen = new HashSet[LRState] - seen.add first - - while not todo.is_empty do - var state = todo.shift - - #print state - automaton.states.add(state) - # Extends the core - for i in state.items.to_a do - state.extends(i) - end - - var nexts = new HashMap[Element, LRState] - for i in state.items do - var e = i.next - if e == null then continue - if nexts.has_key(e) then - nexts[e].add(i.avance) - else - var next = new LRState - next.prev = state - next.prefix.add_all(state.prefix) - next.prefix.add(e) - nexts[e] = next - next.add(i.avance) - end - end - - for e, next in nexts do - - #print "#states: {seen.length}" - - # Look for an existing LR0 state in the automaton - var new_state = true - for n in seen do - if n == next then - if next.prefix.length < n.prefix.length then - n.prefix = next.prefix - n.prev = next.prev - end - next = n - new_state = false - break - end - end - - # If not, add it to the pool and the todo-list - if new_state then - next.number = seen.length - assert not seen.has(next) - seen.add(next) - todo.add(next) - end - - # Add the transition - var t = new LRTransition(state, next, e) - state.outs.add t - next.ins.add t - end - end - for state in automaton.states do - state.analysis - end - return automaton end fun compute_sample_length @@ -449,9 +379,6 @@ class Production end return res end - - # States in the LR automaton that has a outgoing transition on self - var gotos = new ArraySet[LRState] end # An alternative of a production @@ -466,7 +393,7 @@ class Alternative var elems: Array[Element] # The first item of the alternative - var first_item = new Item(self, 0) + fun first_item: Item do return new Item(self, 0) # The name of the elements var elems_names = new Array[nullable String] @@ -585,849 +512,20 @@ end # A terminal element class Token super Element - # States of the LR automaton that shift on self - var shifts = new ArraySet[LRState] - # States of the LR automaton that reduce on self in the lookahead(1) - var reduces = new ArraySet[LRState] - redef fun sample_to_s do return to_s end end -# - -# A LR automaton -class LRAutomaton - # The grammar of the automaton - var grammar: Gram - - # The set of states - var states = new Array[LRState] - - # Dump of the automaton - fun pretty: String - do - var res = new Array[String] - res.add "* LRAutomaton: {states.length} states\n" - for s in states do - res.add "STATE {s}\n" - res.add "\tCORE\n" - for i in s.core do - res.add "\t\t{i}\n" - if i.next != null then continue - for i2 in s.lookahead(i) do - res.add "\t\t\t{i2}\n" - end - end - res.add "\tOTHER ITEMS\n" - for i in s.items do - if s.core.has(i) then continue - res.add "\t\t{i}\n" - if i.next != null then continue - for i2 in s.lookahead(i) do - res.add "\t\t\t{i2}\n" - end - end - var engine = new LREngine - engine.start(s) - res.add "\tPOSSIBLE EXIT {engine.find_exit}\n" - res.add "\tTRANSITIONS {s.outs.length}\n" - for t in s.outs do - res.add "\t\t{t.elem} |-> s{t.to.number}\n" - end - res.add "\tACTIONS\n" - if s.is_lr0 then - res.add "\t\tSTATE LR0\n" - else - res.add "\t\tSTATE SLR\n" - for t, a in s.guarded_reduce do - if a.length > 1 then - res.add "\t\t/!\\ REDUCE/REDUCE CONFLICT\n" - break - else if s.shifts.has(t) then - res.add "\t\t/!\\ SHIFT/REDUCE CONFLICT\n" - break - end - end - end - if not s.shifts.is_empty then - res.add "\t\tSHIFT {s.shifts.join(" ")}\n" - end - for r in s.reduces do - res.add "\t\tREDUCE {r}\n" - end - end - return res.join - end - - # Generate a graphviz file of the automaton - # This generate a simple executable LR0 without much information - fun to_dot_lr0(path: String) - do - var f = new FileWriter.open(path) - f.write("digraph \{\n") - f.write("rankdir=TB;\n") - f.write("node[shape=box,style=rounded];\n") - - f.write("entry [style=invis];\nentry -> s{states.first.number}\n") - for s in states do - f.write "s{s.number} [label=\"" - for a in s.reduces do - if a.prod.accept then - f.write "ACCEPT\\n" - else - f.write "REDUCE {a.prod.name.escape_to_dot} WITH {a.elems.length} ELEMENTS\\n" - end - end - if s.shifts.length > 0 then - f.write "SHIFT\\n" - end - f.write "\"" - if not s.is_lr0 then f.write ",color=red" - f.write "];\n" - for t in s.outs do - f.write "s{s.number} -> s{t.to.number} [label=\"{t.elem.to_s.escape_to_dot}\"];\n" - end - end - f.write("\}\n") - f.close - end - - # Generate a graphviz file of the automaton - fun to_dot(path: String) - do - var f = new FileWriter.open(path) - f.write("digraph g \{\n") - f.write("rankdir=LR;\n") - f.write("node[shape=Mrecord,height=0];\n") - - for s in states do - f.write "s{s.number} [label=\"{s.to_s.escape_to_dot}|" - for i in s.core do - f.write "{i.to_s.escape_to_dot}\\l" - end - f.write("|") - for i in s.items do - if s.core.has(i) then continue - f.write "{i.to_s.escape_to_dot}\\l" - end - f.write "\"" - if not s.is_lr0 then - var conflict = false - for t, a in s.guarded_reduce do - if a.length > 1 then - f.write ",color=red" - conflict = true - break - else if s.shifts.has(t) then - f.write ",color=orange" - conflict = true - break - end - end - if not conflict then - f.write ",color=blue" - end - end - f.write "];\n" - for t in s.outs do - f.write "s{s.number} -> s{t.to.number} [label=\"{t.elem.to_s.escape_to_dot}\"];\n" - end - end - f.write("\}\n") - f.close - end - - # Generate the parser of the automaton - fun gen_to_nit(filepath: String, name: String) - do - var gen = new Generator - gen.gen_to_nit(self, name) - var f = new FileWriter.open(filepath) - for s in gen.out do - f.write(s) - f.write("\n") - end - f.close - end -end - -private class Generator - var out = new Array[String] - fun add(s: String) do out.add(s) - fun gen_to_nit(autom: LRAutomaton, name: String) - do - var states = autom.states - var gram = autom.grammar - - add "# Parser generated by nitcc for the grammar {name}" - add "module {name}_parser is generated, no_warning(\"missing-doc\",\"old-init\")" - add "import nitcc_runtime" - - add "class Parser_{name}" - add "\tsuper Parser" - add "\tredef fun start_state do return state_{states.first.number}" - add "end" - - for s in states do - add "private fun state_{s.number}: LRState{s.number} do return once new LRState{s.number}" - end - for p in gram.prods do - add "private fun goto_{p.cname}: Goto_{p.cname} do return once new Goto_{p.cname}" - for a in p.alts do - add "private fun reduce_{a.cname}(parser: Parser) do" - gen_reduce_to_nit(a) - add "end" - end - end - - add "redef class NToken" - for s in states do - if not s.need_guard then continue - add "\t# guarded action for state {s}" - add "\t# {s.shifts.length} shift(s) and {s.reduces.length} reduce(s)" - add "\tprivate fun action_s{s.number}(parser: Parser) do" - if s.reduces.length != 1 then - add "\t\tparser.parse_error" - else - add "\t\treduce_{s.reduces.first.cname}(parser)" - #gen_reduce_to_nit(s.reduces.first) - end - add "\tend" - end - add "end" - - for t in gram.tokens do - if t.name == "Eof" then - add "redef class {t.cname}" - else - add "class {t.cname}" - end - add "\tsuper NToken" - for s in t.shifts do - if not s.need_guard then continue - add "\tredef fun action_s{s.number}(parser) do" - gen_shift_to_nit(s, t) - add "\tend" - end - for s in t.reduces do - if not s.need_guard then continue - if s.reduces.length <= 1 then continue - add "\tredef fun action_s{s.number}(parser) do" - add "\t\treduce_{s.guarded_reduce[t].first.alt.cname}(parser)" - #gen_reduce_to_nit(s.guarded_reduce[t].first.alt) - add "\tend" - end - add "\tredef fun node_name do return \"{t.name.escape_to_nit}\"" - add "end" - end - - add "redef class LRGoto" - for s in states do - if s.gotos.length <= 1 then continue - add "\tprivate fun goto_s{s.number}(parser: Parser) do abort" - end - add "end" - - for p in gram.prods do - add "class Goto_{p.cname}" - add "\tsuper LRGoto" - for s in p.gotos do - if s.gotos.length <= 1 then continue - add "\tredef fun goto_s{s.number}(parser) do" - gen_goto_to_nit(s, p) - add "\tend" - end - add "end" - end - - var ps = gram.prods.to_a - ps.add_all(gram.ast_prods) - for p in ps do - if p.spe == null and not p.altone then - if p.name.has_suffix("?") or p.name.has_suffix("+") then continue - add "class {p.acname}" - add "\tsuper NProd" - add "\tredef fun node_name do return \"{p.name.escape_to_nit}\"" - add "end" - end - - var als = p.alts.to_a - als.add_all(p.ast_alts) - for a in als do - if a.trans then continue - add "class {a.cname}" - if p.altone then - add "\tsuper NProd" - else - add "\tsuper {p.acname}" - end - add "\tredef fun node_name do return \"{a.name.escape_to_nit}\"" - var initarg = new Array[String] - for i in [0..a.elems.length[ do - add "\tvar n_{a.elemname(i)}: {a.elems[i].acname}" - initarg.add("n_{a.elemname(i)}: {a.elems[i].acname}") - end - if initarg.is_empty then - add "\tinit do end" - else - add "\tinit({initarg.join(", ")}) do" - for i in [0..a.elems.length[ do - add "\t\tself.n_{a.elemname(i)} = n_{a.elemname(i)}" - end - add "\tend" - end - add "\tredef fun number_of_children do return {a.elems.length}" - add "\tredef fun child(i) do" - for i in [0..a.elems.length[ do - add "\t\tif i == {i} then return n_{a.elemname(i)}" - end - add "\t\tabort" - add "\tend" - add "end" - end - end - - for s in states do - add "# State {s}" - add "private class LRState{s.number}" - add "\tsuper LRState" - - add "\tredef fun to_s do return \"{s.to_s.escape_to_nit}\"" - - var err = new Array[String] - for t in s.outs do - var e = t.elem - if e isa Production then err.add e.name - end - if err.is_empty then for t in s.outs do - var e = t.elem - if e isa Token then err.add e.name - end - - add "\tredef fun error_msg do return \"{err.join(", ").escape_to_nit}\"" - - add "\tredef fun action(parser) do" - if s.need_guard then - add "\t\tparser.peek_token.action_s{s.number}(parser)" - else if s.reduces.length == 1 then - add "\t\treduce_{s.reduces.first.cname}(parser)" - #gen_reduce_to_nit(s.reduces.first) - else - abort - end - add "\tend" - - if not s.gotos.is_empty then - add "\tredef fun goto(parser, goto) do" - if s.gotos.length > 1 then - add "\t\tgoto.goto_s{s.number}(parser)" - else - gen_goto_to_nit(s, s.gotos.first) - end - add "\tend" - end - - add "end" - end - - - end - - fun gen_shift_to_nit(s: LRState, t: Token) - do - var dest = s.trans(t) - add "\t\tparser.shift(state_{dest.number})" - - end - - fun gen_goto_to_nit(s: LRState, p: Production) - do - var dest = s.trans(p) - add "\t\tparser.push(state_{dest.number})" - end - - fun gen_reduce_to_nit(alt: Alternative) - do - add "\t\t# REDUCE {alt}" - var i = alt.elems.length - 1 - for e in alt.elems.to_a.reversed do - add "\t\tvar n{i} = parser.pop.as({e.acname})" - i -= 1 - end - - alt.make_codes - var cpt = 0 - i = 0 - var st = new Array[String] - for c in alt.codes.as(not null) do - if c isa CodePop then - st.add "n{i}" - i += 1 - else if c isa CodeNull then - st.add "null" - else if c isa CodeNew then - var calt = c.alt - cpt += 1 - var from = st.length - calt.elems.length - var args = new List[String] - for j in [from..st.length[ do - args.unshift(st.pop) - end - - if args.is_empty then - add "\t\tvar p{cpt} = new {calt.cname}" - else - add "\t\tvar p{cpt} = new {calt.cname}({args.join(", ")})" - end - #var x = 0 - #for j in [from..st.length[ do - #if st[j] == "null" then continue - #add "\t\tp{cpt}.n_{calt.elemname(x)} = {st[j]}" - #x += 1 - #end - st.add("p{cpt}") - else if c isa CodeNewNodes then - cpt += 1 - add "\t\tvar p{cpt} = new {c.alt.prod.acname}" - st.add("p{cpt}") - else if c isa CodeAdd then - var a1 = st.pop - var a0 = st.last - add "\t\t{a0}.children.add({a1})" - end - end - assert st.length == 1 - add "\t\tvar prod = {st.first}" - - add "\t\tparser.node_stack.push prod" - if alt.prod.accept then - add "\t\tparser.stop = true" - else - add "\t\tparser.goto(goto_{alt.prod.cname})" - end - end -end - -# A state in a LR automaton -class LRState - # Shortest prefix to go to this state - # Is empty for the start state - var prefix = new Array[Element] - - # The previous node according to the prefix - # Is null for the start state - var prev: nullable LRState = null - - # Number - var number = -1 - - # Set of all items - var items = new HashSet[Item] - - # Set of items only in the core - var core = new HashSet[Item] - - # Outgoing transitions - var ins = new Array[LRTransition] - - # Ingoing transitions - var outs = new Array[LRTransition] - - # Trans function - fun trans(e: Element): nullable LRState - do - for t in outs do if t.elem == e then return t.to - return null - end - - redef fun ==(o) do return o isa LRState and core == o.core - redef fun hash do return items.length - - redef fun to_s do return "{number} {prefix.join(" ")}" - - # Add and item in the core - fun add(i: Item): Bool - do - if items.has(i) then return false - - items.add(i) - if i.pos > 0 or i.alt.prod.accept then core.add(i) - return true - end - - # Recursively extends item outside the core - fun extends(i: Item) - do - var e = i.next - if e == null then return - if not e isa Production then return - for i2 in e.start_state do - if add(i2) then extends(i2) - end - end - - # SLR lookahead - fun lookahead(i: Item): Set[Item] - do - return i.alt.prod.afters - end - - # Set of all reductions - var reduces = new ArraySet[Alternative] - # Set of all shifts - var shifts = new ArraySet[Token] - # Set of all goto - var gotos = new ArraySet[Production] - # Reduction guarded by tokens - var guarded_reduce = new HashMap[Token, Set[Item]] - # Shifts guarded by tokens - var guarded_shift = new HashMap[Token, Set[Item]] - - # Does the state need a guard to perform an action? - fun need_guard: Bool do return not shifts.is_empty or reduces.length > 1 - - # Is the state LR0? - fun is_lr0: Bool do return reduces.length <= 1 and shifts.is_empty or reduces.is_empty - - # Compute guards and conflicts - fun analysis - do - # Collect action and conflicts - for i in items do - var n = i.next - if n == null then - reduces.add(i.alt) - for i2 in lookahead(i) do - var t = i2.next - assert t isa Token - t.reduces.add(self) - if not guarded_reduce.has_key(t) then - guarded_reduce[t] = new ArraySet[Item] - end - guarded_reduce[t].add(i) - end - else if n isa Token then - shifts.add(n) - n.shifts.add(self) - if not guarded_shift.has_key(n) then - guarded_shift[n] = new ArraySet[Item] - end - guarded_shift[n].add(i) - else if n isa Production then - gotos.add(n) - n.gotos.add(self) - else - abort - end - end - # Token to remove as reduction guard to solve S/R conflicts - var removed_reduces = new Array[Token] - for t, a in guarded_reduce do - if a.length > 1 then - print "---" - print "REDUCE/REDUCE Conflict on state {self} for token {t}:" - print "A possible past: {prefix}" - conflicting_items.add_all a - var worst_exit = null - for i in a do - var engine = new LREngine - engine.start(self) - engine.reduce(i.alt) - var exit = engine.find_exit - if worst_exit == null or exit.length > worst_exit.length then worst_exit = exit - end - var amb = 0 - for i in a do - var engine = new LREngine - engine.start(self) - engine.reduce(i.alt) - for e in worst_exit.as(not null) do - if not engine.try_shift(e) then break - end - print "REDUCE on item: {i}" - var exit = engine.find_exit - print "A possible future: {exit}" - print engine.tree.dump - if exit == worst_exit then amb += 1 - end - if amb > 1 then - print "AMBIGUITY detected: same elements, different trees" - end - end - if guarded_shift.has_key(t) then - var ri = a.first - var confs = new Array[Item] - var ress = new Array[String] - var g = guarded_shift[t] - for si in lookahead(ri) do - if si.next != t then continue - if not g.has(si) then - confs.add(si) - continue - end - var p = ri.alt.prod - var csi: nullable Item = null - for bsi in back_expand(si) do - if bsi.alt.prod == p then - csi = bsi - break - end - end - if csi == null then - confs.add(si) - continue - end - ress.add "\tshift: {si}" - if si != csi then - ress.add "\tcore shift: {csi}" - end - end - if confs.is_empty then - print "---" - print "Automatic Dangling on state {self} for token {t}:" - print "\treduce: {ri}" - for r in ress do print r - removed_reduces.add t - else - print "---" - print "SHIFT/REDUCE Conflict on state {self} for token {t}:" - print "A possible past: {prefix}" - removed_reduces.add t - conflicting_items.add_all a - conflicting_items.add_all guarded_shift[t] - - var worst_exit = null - for i in guarded_shift[t] do - print "SHIFT on item: {i}" - var engine = new LREngine - engine.start(self) - for e in i.future do engine.shift(e) - var exit = engine.find_exit - print "A possible future: {exit}" - print engine.tree.dump - if worst_exit == null or exit.length < worst_exit.length then - worst_exit = exit - end - end - var engine = new LREngine - engine.start(self) - engine.reduce(ri.alt) - for e in worst_exit.as(not null) do - if not engine.try_shift(e) then break - end - var reduce_exit = engine.find_exit - print "REDUCE on item: {ri}" - var exit = engine.find_exit - print "A possible future: {exit}" - print engine.tree.dump - if exit == worst_exit then - print "AMBIGUITY detected: same elements, different trees" - end - end - end - end - for t in removed_reduces do - guarded_reduce.keys.remove(t) - t.reduces.remove(self) - end - end - - # Items within a reduce/reduce or a shift/reduce conflict. - # - # Is computed by `analysis` - var conflicting_items = new ArraySet[Item] - - # Return `i` and all other items of the state that expands, directly or indirectly, to `i` - fun back_expand(i: Item): Set[Item] - do - var res = new ArraySet[Item] - var todo = [i] - res.add(i) - while not todo.is_empty do - var x = todo.pop - if x.pos > 0 then continue - var p = x.alt.prod - for y in items do - if y.next != p then continue - if res.has(y) then continue - res.add(y) - todo.add(y) - end - end - return res - end -end - -# Execution engine simulator on a LR automaton. -# It has a stack of LR states and AST nodes. -class LREngine - # The stack of stale, starts with start state - var state_stack = new Array[nullable LRState] - - # A stack of AST node, reduced production and shifted token are pushed onto - var node_stack = new Array[CSTNode] - - # The sequence of elements shifted for the first state - var past = new Array[Element] - - # The sequence of elements shifted since the first state - var future = new Array[Element] - - # The current state (top of the stack) - fun state: nullable LRState do return state_stack.last - - # Initialize the engine on a given `state`. - # A consistent `past` is created to reach `state`. - # Subsequent shifts will be added to the `future`. - fun start(state: LRState) - do - var start = state - loop - var prev = start.prev - if prev == null then break - start = prev - end - state_stack.add(start) - for e in state.prefix do shift(e) - assert self.state == state - - var tmp = future - future = past - past = tmp - end - - # Perform a shift on `e` for the current `state` and add it to `future`. - # Both tokens and productions can be shifted. - # If the shift is impossible, the current state become null, - fun shift(e: Element) - do - state_stack.add state.trans(e) - future.add(e) - node_stack.add(new CSTNode(e)) - end - - # Perform a reduction on an alternative `a` on the current state. - fun reduce(a: Alternative) - do - assert can_reduce(a) - var len = a.elems.length - for i in [0..len[ do state_stack.pop - var node = new CSTNode(a.prod) - for i in [0..len[ do node.children.unshift(node_stack.pop) - node_stack.add(node) - - # TODO something smart when accepting - if a.prod.accept then - state_stack.add null - return - end - - state_stack.add state.trans(a.prod) - end - - # Return true if the elements in the stack are compatible with the reduction. - fun can_reduce(alternative: Alternative): Bool - do - var idx = node_stack.length - alternative.elems.length - if idx < 0 then return false - for i in [0..alternative.elems.length[ do - if alternative.elems[i] != node_stack[idx+i].element then return false - end - return true - end - - # Try to shift on the current state. - # If not doable, try to reduce something, then shift. - # Return true is a shift was done. - fun try_shift(e: Element): Bool - do - var s = state.trans(e) - if s == null then - if not try_reduce then - return false - end - return try_shift(e) - end - shift(e) - return true - end - - # Try to reduce something on the current state. - fun try_reduce: Bool - do - for i in state.core do - # Filter out items that are not reduction - if i.next != null then continue - if can_reduce(i.alt) then - reduce(i.alt) - return true - end - end - return false - end - - fun tree: CSTNode do return node_stack.last - - fun find_exit: Array[Element] - do - var set = new HashSet[LRState] - loop - var state = self.state - if state == null then break - if set.has(state) then - # We are looping, just abort - break - end - set.add(state) - # Heuristic, the first item is an accepting one or something that exit without looping - var item = state.core.first - for e in item.future do shift(e) - reduce(item.alt) - end - return future - end -end - -# A CST node of the LREngine -class CSTNode - var element: Element - var children = new Array[CSTNode] - - fun dump(prefix: nullable String): String - do - if prefix == null then prefix = "" - var res = element.to_s + "\n" - if children.length == 0 then return res - var p2 = prefix + "│ " - for c in [0..children.length-1[ do - res += prefix + "├╴" - res += children[c].dump(p2) - end - res += prefix + "└╴" - res += children.last.dump(prefix + " ") - return res - end -end - -# A transition in a LR automaton -class LRTransition - # The origin state - var from: LRState - # The destination state - var to: LRState - # The element labeling the transition - var elem: Element -end - # A alternative with a cursor (dot) before an element class Item # The alternative var alt: Alternative # The dot index (0 means before the first element) var pos: Int + # The lookahead at the end of the item + var after = new ArraySet[Item] redef fun ==(o) do return o isa Item and alt == o.alt and pos == o.pos redef fun hash do return alt.hash + pos @@ -1435,16 +533,33 @@ class Item redef fun to_s do var b = new FlatBuffer - b.append("{alt.prod.name}::{alt.name}=") + b.append("{alt.prod.name}→") for i in [0..alt.elems.length[ do - if pos == i then b.append(".") else b.append(" ") + if pos == i then b.append("·") else b.append(" ") b.append(alt.elems[i].to_s) end - if pos == alt.elems.length then b.append(".") + if pos == alt.elems.length then b.append("·") + if not after.is_empty then + b.append(", ") + var toks = new ArraySet[Token] + for a in after do toks.add(a.next.as(Token)) + b.append(toks.join("/")) + end return b.to_s end + fun add_after(items: Collection[Item]): Bool + do + var res = false + for i in items do + if after.has(i) then continue + after.add i + res = true + end + return res + end + # The element that follows the dot, null if the dot is at the end fun next: nullable Element do @@ -1452,29 +567,44 @@ class Item return alt.elems[pos] end - # SLR lookahead - fun lookahead: Set[Token] + # LALR and LR1 lookahead. Return what follows the next production + fun lookahead_of_production: Set[Item] do - var res = new HashSet[Token] - var p = pos + 1 + assert next isa Production + var res = new HashSet[Item] + var item = self + var p = pos while p < alt.elems.length do - var e = alt.elems[p] - if e isa Token then - res.add(e) + p = p + 1 + + if p == alt.elems.length then + # We are at the end + res.add_all(after) break - else if e isa Production then - #res.add_all(e.firsts) - if not e.is_nullable then break end - p += 1 + + var next = alt.elems[p] + if next isa Token then + item = new Item(alt, p) + res.add(item) + break + else if next isa Production then + res.add_all(next.firsts) + if not next.is_nullable then break + else + abort # impossible + end end + #print "lookahead {self} -> {res}" return res end # The item that advanced once fun avance: Item do + assert pos < alt.elems.length var res = new Item(alt, pos+1) + res.add_after after return res end diff --git a/contrib/nitcc/src/lrautomaton.nit b/contrib/nitcc/src/lrautomaton.nit new file mode 100644 index 0000000000..60c6a7d1fe --- /dev/null +++ b/contrib/nitcc/src/lrautomaton.nit @@ -0,0 +1,1010 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import grammar + +redef class Production + # States in the LR automaton that has a outgoing transition on self + var gotos = new ArraySet[LRState] +end + +redef class Token + # States of the LR automaton that shift on self + var shifts = new ArraySet[LRState] + # States of the LR automaton that reduce on self in the lookahead(1) + var reduces = new ArraySet[LRState] +end + +# A LR automaton +class LRAutomaton + # The grammar of the automaton + var grammar: Gram + + # The set of states + var states = new Array[LRState] + + fun build + do + var start = grammar.start + + var first = new LRState + first.number = 0 + for i in start.start_state do first.add_item(i) + var todo = new List[LRState] + todo.add first + states.add first + + while not todo.is_empty do + # In LALR, we can "recompute" a state to propagate new lookaheads + # This is equivalent of analysing a new LR(1) state, except that in LALR we reanalyse an existing one that was updated. + var state = todo.shift + + # Extends the core + for i in state.items.to_a do + extends_state(state, i) + end + + # Compute a new proto-state for each outgoing transitions + var nexts = new HashMap[Element, LRState] + for i in state.items do + var e = i.next + if e == null then continue + if nexts.has_key(e) then + nexts[e].add_item(i.avance) + else + var next = new LRState + next.prev = state + next.prefix.add_all(state.prefix) + next.prefix.add(e) + nexts[e] = next + next.add_item(i.avance) + end + end + + # Process the protostate + # Either it already exists and we connect to it (and update it in LALR) + # Or its a new state, we will add it to the todo list. + for e, next in nexts do + #print "#states: {states.length}" + + # Look for an existing LR0 state in the automaton + var new_state = true + for n in states do + if same_state(n, next) then + var changed = merge_state(n, next) + if changed then todo.add(n) + next = n + new_state = false + break + end + end + + # If not, add it to the pool and the todo-list + if new_state then + next.number = states.length + assert not states.has(next) + states.add(next) + todo.add(next) + end + + # Add the transition if needed + var n = state.trans(e) + if n == null then + var t = new LRTransition(state, next, e) + state.outs.add t + next.ins.add t + else + assert n == next + end + end + end + for state in states do + state.analysis + end + end + + # Says if the two state should be merged into one + fun same_state(s1, s2: LRState): Bool + do + if s1.core.length != s2.core.length then return false + var count = 0 + for i1 in s1.core do + for i2 in s2.core do + if same_state_item(i1, i2) then + count += 1 + break + end + end + end + return count == s1.core.length + end + + # Used by `same_state` to compare two items + fun same_state_item(i1, i2: Item): Bool + do + return i1 == i2 + end + + # Merge information of new_stat into old_state. + # new_state will be discarded. + # Return `true` if `old_state` need to be processed again (LALR only) + fun merge_state(old_state, new_state: LRState): Bool + do + # Update to a shorter prefix if possible + if new_state.prefix.length < old_state.prefix.length then + new_state.prefix = old_state.prefix + new_state.prev = old_state.prev + end + + return false + end + + # Recursively extends item outside the core + fun extends_state(state: LRState, i: Item) + do + var e = i.next + if e == null then return + if not e isa Production then return + for i2 in e.start_state do + i2.add_after i.lookahead_of_production + if state.add_item(i2) then extends_state(state, i2) + end + end + + # Dump of the automaton + fun pretty: String + do + var res = new Array[String] + res.add "* LRAutomaton: {states.length} states\n" + for s in states do + res.add "STATE {s}\n" + res.add "\tCORE\n" + for i in s.core do + res.add "\t\t{i}\n" + if i.next != null then continue + for i2 in s.lookahead(i) do + res.add "\t\t\t{i2}\n" + end + end + res.add "\tOTHER ITEMS\n" + for i in s.items do + if s.core.has(i) then continue + res.add "\t\t{i}\n" + if i.next != null then continue + for i2 in s.lookahead(i) do + res.add "\t\t\t{i2}\n" + end + end + var engine = new LREngine + engine.start(s) + res.add "\tPOSSIBLE EXIT {engine.find_exit}\n" + res.add "\tTRANSITIONS {s.outs.length}\n" + for t in s.outs do + res.add "\t\t{t.elem} |-> s{t.to.number}\n" + end + res.add "\tACTIONS\n" + if s.is_lr0 then + res.add "\t\tSTATE LR0\n" + else + res.add "\t\tSTATE SLR\n" + for t, a in s.guarded_reduce do + if a.length > 1 then + res.add "\t\t/!\\ REDUCE/REDUCE CONFLICT\n" + break + else if s.shifts.has(t) then + res.add "\t\t/!\\ SHIFT/REDUCE CONFLICT\n" + break + end + end + end + if not s.shifts.is_empty then + res.add "\t\tSHIFT {s.shifts.join(" ")}\n" + end + for r in s.reduces do + res.add "\t\tREDUCE {r}\n" + end + end + return res.join + end + + # Generate a graphviz file of the automaton + # This generate a simple executable LR0 without much information + fun to_dot_lr0(path: String) + do + var f = new FileWriter.open(path) + f.write("digraph \{\n") + f.write("rankdir=TB;\n") + f.write("node[shape=box,style=rounded];\n") + + f.write("entry [style=invis];\nentry -> s{states.first.number}\n") + for s in states do + f.write "s{s.number} [label=\"" + for a in s.reduces do + if a.prod.accept then + f.write "ACCEPT\\n" + else + f.write "REDUCE {a.prod.name.escape_to_dot} WITH {a.elems.length} ELEMENTS\\n" + end + end + if s.shifts.length > 0 then + f.write "SHIFT\\n" + end + f.write "\"" + if not s.is_lr0 then f.write ",color=red" + f.write "];\n" + for t in s.outs do + f.write "s{s.number} -> s{t.to.number} [label=\"{t.elem.to_s.escape_to_dot}\"];\n" + end + end + f.write("\}\n") + f.close + end + + # Generate a graphviz file of the automaton + fun to_dot(path: String) + do + var f = new FileWriter.open(path) + f.write("digraph g \{\n") + f.write("rankdir=LR;\n") + f.write("node[shape=Mrecord,height=0];\n") + + for s in states do + f.write "s{s.number} [label=\"{s.to_s.escape_to_dot}|" + for i in s.core do + f.write "{i.to_s.escape_to_dot}\\l" + end + f.write("|") + for i in s.items do + if s.core.has(i) then continue + f.write "{i.to_s.escape_to_dot}\\l" + end + f.write "\"" + if not s.is_lr0 then + var conflict = false + for t, a in s.guarded_reduce do + if a.length > 1 then + f.write ",color=red" + conflict = true + break + else if s.shifts.has(t) then + f.write ",color=orange" + conflict = true + break + end + end + if not conflict then + f.write ",color=blue" + end + end + f.write "];\n" + for t in s.outs do + f.write "s{s.number} -> s{t.to.number} [label=\"{t.elem.to_s.escape_to_dot}\"];\n" + end + end + f.write("\}\n") + f.close + end + + # Generate the parser of the automaton + fun gen_to_nit(filepath: String, name: String) + do + var gen = new Generator + gen.gen_to_nit(self, name) + var f = new FileWriter.open(filepath) + for s in gen.out do + f.write(s) + f.write("\n") + end + f.close + end +end + +class LR1Automaton + super LRAutomaton + + redef fun same_state_item(i1, i2) + do + return i1 == i2 and i1.after == i2.after + end +end + +class LR0Automaton + super LRAutomaton +end + +class LALR1Automaton + super LRAutomaton + redef fun merge_state(old_state, new_state) + do + var changed = super + # Merge the nexts of the items + for i1 in new_state.items do + for i2 in old_state.items do + if i1 == i2 then + if i2.add_after(i1.after) then + changed = true + end + end + end + end + return changed + end +end + +private class Generator + var out = new Array[String] + fun add(s: String) do out.add(s) + fun gen_to_nit(autom: LRAutomaton, name: String) + do + var states = autom.states + var gram = autom.grammar + + add "# Parser generated by nitcc for the grammar {name}" + add "module {name}_parser is generated, no_warning(\"missing-doc\",\"old-init\")" + add "import nitcc_runtime" + + add "class Parser_{name}" + add "\tsuper Parser" + add "\tredef fun start_state do return state_{states.first.number}" + add "end" + + for s in states do + add "private fun state_{s.number}: LRState{s.number} do return once new LRState{s.number}" + end + for p in gram.prods do + add "private fun goto_{p.cname}: Goto_{p.cname} do return once new Goto_{p.cname}" + for a in p.alts do + add "private fun reduce_{a.cname}(parser: Parser) do" + gen_reduce_to_nit(a) + add "end" + end + end + + add "redef class NToken" + for s in states do + if not s.need_guard then continue + add "\t# guarded action for state {s}" + add "\t# {s.shifts.length} shift(s) and {s.reduces.length} reduce(s)" + add "\tprivate fun action_s{s.number}(parser: Parser) do" + if s.reduces.length != 1 then + add "\t\tparser.parse_error" + else + add "\t\treduce_{s.reduces.first.cname}(parser)" + #gen_reduce_to_nit(s.reduces.first) + end + add "\tend" + end + add "end" + + for t in gram.tokens do + if t.name == "Eof" then + add "redef class {t.cname}" + else + add "class {t.cname}" + end + add "\tsuper NToken" + for s in t.shifts do + if not s.need_guard then continue + add "\tredef fun action_s{s.number}(parser) do" + gen_shift_to_nit(s, t) + add "\tend" + end + for s in t.reduces do + if not s.need_guard then continue + if s.reduces.length <= 1 then continue + add "\tredef fun action_s{s.number}(parser) do" + add "\t\treduce_{s.guarded_reduce[t].first.alt.cname}(parser)" + #gen_reduce_to_nit(s.guarded_reduce[t].first.alt) + add "\tend" + end + add "\tredef fun node_name do return \"{t.name.escape_to_nit}\"" + add "end" + end + + add "redef class LRGoto" + for s in states do + if s.gotos.length <= 1 then continue + add "\tprivate fun goto_s{s.number}(parser: Parser) do abort" + end + add "end" + + for p in gram.prods do + add "class Goto_{p.cname}" + add "\tsuper LRGoto" + for s in p.gotos do + if s.gotos.length <= 1 then continue + add "\tredef fun goto_s{s.number}(parser) do" + gen_goto_to_nit(s, p) + add "\tend" + end + add "end" + end + + var ps = gram.prods.to_a + ps.add_all(gram.ast_prods) + for p in ps do + if p.spe == null and not p.altone then + if p.name.has_suffix("?") or p.name.has_suffix("+") then continue + add "class {p.acname}" + add "\tsuper NProd" + add "\tredef fun node_name do return \"{p.name.escape_to_nit}\"" + add "end" + end + + var als = p.alts.to_a + als.add_all(p.ast_alts) + for a in als do + if a.trans then continue + add "class {a.cname}" + if p.altone then + add "\tsuper NProd" + else + add "\tsuper {p.acname}" + end + add "\tredef fun node_name do return \"{a.name.escape_to_nit}\"" + var initarg = new Array[String] + for i in [0..a.elems.length[ do + add "\tvar n_{a.elemname(i)}: {a.elems[i].acname}" + initarg.add("n_{a.elemname(i)}: {a.elems[i].acname}") + end + if initarg.is_empty then + add "\tinit do end" + else + add "\tinit({initarg.join(", ")}) do" + for i in [0..a.elems.length[ do + add "\t\tself.n_{a.elemname(i)} = n_{a.elemname(i)}" + end + add "\tend" + end + add "\tredef fun number_of_children do return {a.elems.length}" + add "\tredef fun child(i) do" + for i in [0..a.elems.length[ do + add "\t\tif i == {i} then return n_{a.elemname(i)}" + end + add "\t\tabort" + add "\tend" + add "end" + end + end + + for s in states do + add "# State {s}" + add "private class LRState{s.number}" + add "\tsuper LRState" + + add "\tredef fun to_s do return \"{s.to_s.escape_to_nit}\"" + + var err = new Array[String] + for t in s.outs do + var e = t.elem + if e isa Production then err.add e.name + end + if err.is_empty then for t in s.outs do + var e = t.elem + if e isa Token then err.add e.name + end + + add "\tredef fun error_msg do return \"{err.join(", ").escape_to_nit}\"" + + add "\tredef fun action(parser) do" + if s.need_guard then + add "\t\tparser.peek_token.action_s{s.number}(parser)" + else if s.reduces.length == 1 then + add "\t\treduce_{s.reduces.first.cname}(parser)" + #gen_reduce_to_nit(s.reduces.first) + else + abort + end + add "\tend" + + if not s.gotos.is_empty then + add "\tredef fun goto(parser, goto) do" + if s.gotos.length > 1 then + add "\t\tgoto.goto_s{s.number}(parser)" + else + gen_goto_to_nit(s, s.gotos.first) + end + add "\tend" + end + + add "end" + end + + + end + + fun gen_shift_to_nit(s: LRState, t: Token) + do + var dest = s.trans(t) + add "\t\tparser.shift(state_{dest.number})" + + end + + fun gen_goto_to_nit(s: LRState, p: Production) + do + var dest = s.trans(p) + add "\t\tparser.push(state_{dest.number})" + end + + fun gen_reduce_to_nit(alt: Alternative) + do + add "\t\t# REDUCE {alt}" + var i = alt.elems.length - 1 + for e in alt.elems.to_a.reversed do + add "\t\tvar n{i} = parser.pop.as({e.acname})" + i -= 1 + end + + alt.make_codes + var cpt = 0 + i = 0 + var st = new Array[String] + for c in alt.codes.as(not null) do + if c isa CodePop then + st.add "n{i}" + i += 1 + else if c isa CodeNull then + st.add "null" + else if c isa CodeNew then + var calt = c.alt + cpt += 1 + var from = st.length - calt.elems.length + var args = new List[String] + for j in [from..st.length[ do + args.unshift(st.pop) + end + + if args.is_empty then + add "\t\tvar p{cpt} = new {calt.cname}" + else + add "\t\tvar p{cpt} = new {calt.cname}({args.join(", ")})" + end + #var x = 0 + #for j in [from..st.length[ do + #if st[j] == "null" then continue + #add "\t\tp{cpt}.n_{calt.elemname(x)} = {st[j]}" + #x += 1 + #end + st.add("p{cpt}") + else if c isa CodeNewNodes then + cpt += 1 + add "\t\tvar p{cpt} = new {c.alt.prod.acname}" + st.add("p{cpt}") + else if c isa CodeAdd then + var a1 = st.pop + var a0 = st.last + add "\t\t{a0}.children.add({a1})" + end + end + assert st.length == 1 + add "\t\tvar prod = {st.first}" + + add "\t\tparser.node_stack.push prod" + if alt.prod.accept then + add "\t\tparser.stop = true" + else + add "\t\tparser.goto(goto_{alt.prod.cname})" + end + end +end + +# A state in a LR automaton +class LRState + # Shortest prefix to go to this state + # Is empty for the start state + var prefix = new Array[Element] + + # The previous node according to the prefix + # Is null for the start state + var prev: nullable LRState = null + + # Number + var number = -1 + + # Set of all items + var items = new HashSet[Item] + + # Set of items only in the core + var core = new HashSet[Item] + + # Outgoing transitions + var ins = new Array[LRTransition] + + # Ingoing transitions + var outs = new Array[LRTransition] + + # Trans function + fun trans(e: Element): nullable LRState + do + for t in outs do if t.elem == e then return t.to + return null + end + + redef fun to_s do return "{number} {prefix.join(" ")}" + + # Add and item in the core + # If the item already exists, merge the after + fun add_item(i: Item): Bool + do + if items.has(i) then + for i2 in items do + if i2 != i then continue + return i2.add_after(i.after) + end + return false + end + + items.add(i) + if i.pos > 0 or i.alt.prod.accept then core.add(i) + return true + end + + # Lookahead after the reduction of i + fun lookahead(i: Item): Set[Item] + do + var res = i.after + if res.is_empty then + # no after, it means we are in SLR mode + # So return the global afters of the production + return i.alt.prod.afters + end + return res + end + + # Set of all reductions + var reduces = new ArraySet[Alternative] + # Set of all shifts + var shifts = new ArraySet[Token] + # Set of all goto + var gotos = new ArraySet[Production] + # Reduction guarded by tokens + var guarded_reduce = new HashMap[Token, Set[Item]] + # Shifts guarded by tokens + var guarded_shift = new HashMap[Token, Set[Item]] + + # Does the state need a guard to perform an action? + fun need_guard: Bool do return not shifts.is_empty or reduces.length > 1 + + # Is the state LR0? + fun is_lr0: Bool do return reduces.length <= 1 and shifts.is_empty or reduces.is_empty + + # Compute guards and conflicts + fun analysis + do + # Collect action and conflicts + for i in items do + var n = i.next + if n == null then + reduces.add(i.alt) + for i2 in lookahead(i) do + var t = i2.next + assert t isa Token + t.reduces.add(self) + if not guarded_reduce.has_key(t) then + guarded_reduce[t] = new ArraySet[Item] + end + guarded_reduce[t].add(i) + end + else if n isa Token then + shifts.add(n) + n.shifts.add(self) + if not guarded_shift.has_key(n) then + guarded_shift[n] = new ArraySet[Item] + end + guarded_shift[n].add(i) + else if n isa Production then + gotos.add(n) + n.gotos.add(self) + else + abort + end + end + # Token to remove as reduction guard to solve S/R conflicts + var removed_reduces = new Array[Token] + for t, a in guarded_reduce do + if a.length > 1 then + print "---" + print "REDUCE/REDUCE Conflict on state {self} for token {t}:" + print "A possible past: {prefix}" + conflicting_items.add_all a + var worst_exit = null + for i in a do + var engine = new LREngine + engine.start(self) + engine.reduce(i.alt) + var exit = engine.find_exit + if worst_exit == null or exit.length > worst_exit.length then worst_exit = exit + end + var amb = 0 + for i in a do + var engine = new LREngine + engine.start(self) + engine.reduce(i.alt) + for e in worst_exit.as(not null) do + if not engine.try_shift(e) then break + end + print "REDUCE on item: {i}" + var exit = engine.find_exit + print "A possible future: {exit}" + print engine.tree.dump + if exit == worst_exit then amb += 1 + end + if amb > 1 then + print "AMBIGUITY detected: same elements, different trees" + end + end + if guarded_shift.has_key(t) then + var ri = a.first + var confs = new Array[Item] + var ress = new Array[String] + var g = guarded_shift[t] + for si in lookahead(ri) do + if si.next != t then continue + if not g.has(si) then + confs.add(si) + continue + end + var p = ri.alt.prod + var csi: nullable Item = null + for bsi in back_expand(si) do + if bsi.alt.prod == p then + csi = bsi + break + end + end + if csi == null then + confs.add(si) + continue + end + ress.add "\tshift: {si}" + if si != csi then + ress.add "\tcore shift: {csi}" + end + end + if confs.is_empty then + print "---" + print "Automatic Dangling SHIFT/REDUCE on state {self} for token {t}:" + print "\treduce: {ri}" + for r in ress do print r + removed_reduces.add t + else + print "---" + print "SHIFT/REDUCE Conflict on state {self} for token {t}:" + print "A possible past: {prefix}" + removed_reduces.add t + conflicting_items.add_all a + conflicting_items.add_all guarded_shift[t] + + var worst_exit = null + for i in guarded_shift[t] do + print "SHIFT on item: {i}" + var engine = new LREngine + engine.start(self) + for e in i.future do engine.shift(e) + var exit = engine.find_exit + print "A possible future: {exit}" + print engine.tree.dump + if worst_exit == null or exit.length < worst_exit.length then + worst_exit = exit + end + end + var engine = new LREngine + engine.start(self) + engine.reduce(ri.alt) + for e in worst_exit.as(not null) do + if not engine.try_shift(e) then break + end + var reduce_exit = engine.find_exit + print "REDUCE on item: {ri}" + var exit = engine.find_exit + print "A possible future: {exit}" + print engine.tree.dump + if exit == worst_exit then + print "AMBIGUITY detected: same elements, different trees" + end + end + end + end + for t in removed_reduces do + guarded_reduce.keys.remove(t) + t.reduces.remove(self) + end + end + + # Items within a reduce/reduce or a shift/reduce conflict. + # + # Is computed by `analysis` + var conflicting_items = new ArraySet[Item] + + # Return `i` and all other items of the state that expands, directly or indirectly, to `i` + fun back_expand(i: Item): Set[Item] + do + var res = new ArraySet[Item] + var todo = [i] + res.add(i) + while not todo.is_empty do + var x = todo.pop + if x.pos > 0 then continue + var p = x.alt.prod + for y in items do + if y.next != p then continue + if res.has(y) then continue + res.add(y) + todo.add(y) + end + end + return res + end +end + +# Execution engine simulator on a LR automaton. +# It has a stack of LR states and AST nodes. +class LREngine + # The stack of stale, starts with start state + var state_stack = new Array[nullable LRState] + + # A stack of AST node, reduced production and shifted token are pushed onto + var node_stack = new Array[CSTNode] + + # The sequence of elements shifted for the first state + var past = new Array[Element] + + # The sequence of elements shifted since the first state + var future = new Array[Element] + + # The current state (top of the stack) + fun state: nullable LRState do return state_stack.last + + # Initialize the engine on a given `state`. + # A consistent `past` is created to reach `state`. + # Subsequent shifts will be added to the `future`. + fun start(state: LRState) + do + var start = state + loop + var prev = start.prev + if prev == null then break + start = prev + end + state_stack.add(start) + for e in state.prefix do shift(e) + assert self.state == state + + var tmp = future + future = past + past = tmp + end + + # Perform a shift on `e` for the current `state` and add it to `future`. + # Both tokens and productions can be shifted. + # If the shift is impossible, the current state become null, + fun shift(e: Element) + do + state_stack.add state.trans(e) + future.add(e) + node_stack.add(new CSTNode(e)) + end + + # Perform a reduction on an alternative `a` on the current state. + fun reduce(a: Alternative) + do + assert can_reduce(a) + var len = a.elems.length + for i in [0..len[ do state_stack.pop + var node = new CSTNode(a.prod) + for i in [0..len[ do node.children.unshift(node_stack.pop) + node_stack.add(node) + + # TODO something smart when accepting + if a.prod.accept then + state_stack.add null + return + end + + state_stack.add state.trans(a.prod) + end + + # Return true if the elements in the stack are compatible with the reduction. + fun can_reduce(alternative: Alternative): Bool + do + var idx = node_stack.length - alternative.elems.length + if idx < 0 then return false + for i in [0..alternative.elems.length[ do + if alternative.elems[i] != node_stack[idx+i].element then return false + end + return true + end + + # Try to shift on the current state. + # If not doable, try to reduce something, then shift. + # Return true is a shift was done. + fun try_shift(e: Element): Bool + do + var s = state.trans(e) + if s == null then + if not try_reduce then + return false + end + return try_shift(e) + end + shift(e) + return true + end + + # Try to reduce something on the current state. + fun try_reduce: Bool + do + for i in state.core do + # Filter out items that are not reduction + if i.next != null then continue + if can_reduce(i.alt) then + reduce(i.alt) + return true + end + end + return false + end + + fun tree: CSTNode do return node_stack.last + + fun find_exit: Array[Element] + do + var set = new HashSet[LRState] + loop + var state = self.state + if state == null then break + if set.has(state) then + # We are looping, just abort + break + end + set.add(state) + # Heuristic, the first item is an accepting one or something that exit without looping + var item = state.core.first + for e in item.future do shift(e) + reduce(item.alt) + end + return future + end +end + +# A CST node of the LREngine +class CSTNode + var element: Element + var children = new Array[CSTNode] + + fun dump(prefix: nullable String): String + do + if prefix == null then prefix = "" + var res = element.to_s + "\n" + if children.length == 0 then return res + var p2 = prefix + "│ " + for c in [0..children.length-1[ do + res += prefix + "├╴" + res += children[c].dump(p2) + end + res += prefix + "└╴" + res += children.last.dump(prefix + " ") + return res + end +end + +# A transition in a LR automaton +class LRTransition + # The origin state + var from: LRState + # The destination state + var to: LRState + # The element labeling the transition + var elem: Element +end diff --git a/contrib/nitcc/src/nitcc.nit b/contrib/nitcc/src/nitcc.nit index aa2e98abb2..68332dbb72 100644 --- a/contrib/nitcc/src/nitcc.nit +++ b/contrib/nitcc/src/nitcc.nit @@ -16,6 +16,7 @@ module nitcc import nitcc_semantic +import lrautomaton # Load the grammar file @@ -69,7 +70,9 @@ end # Generate the LR automaton -var lr = gram.lr0 +gram.prepare_for_automaton +var lr = new LALR1Automaton(gram) +lr.build var conflitcs = new ArraySet[Production] for s in lr.states do @@ -80,13 +83,6 @@ if not conflitcs.is_empty then print "Error: there is conflicts" end -if false then loop -if conflitcs.is_empty then break -print "Inline {conflitcs.join(" ")}" -gram.inline(conflitcs) -lr=gram.lr0 -end - # Output concrete grammar and LR automaton var nbalts = 0 diff --git a/contrib/nitcc/src/nitcc_parser_gen.nit b/contrib/nitcc/src/nitcc_parser_gen.nit index bfbdbb5709..f0fe754611 100644 --- a/contrib/nitcc/src/nitcc_parser_gen.nit +++ b/contrib/nitcc/src/nitcc_parser_gen.nit @@ -29,7 +29,7 @@ # module nitcc_parser_gen -import grammar +import lrautomaton var g = new Gram var p_gr = new Production("grammar") @@ -195,7 +195,9 @@ p_pri.new_alt0("priority_left").phony = true p_pri.new_alt0("priority_right").phony = true p_pri.new_alt0("priority_unary").phony = true -var a = g.lr0 +g.prepare_for_automaton +var a = new LALR1Automaton(g) +a.build print "LR automaton: {a.states.length} states (see nitcc0.lr.dot)" a.to_dot("nitcc0.lr.dot") diff --git a/contrib/nitcc/tests/amb_repeat.input b/contrib/nitcc/tests/amb_repeat.input new file mode 100644 index 0000000000..192dc07e71 --- /dev/null +++ b/contrib/nitcc/tests/amb_repeat.input @@ -0,0 +1 @@ +c x diff --git a/contrib/nitcc/tests/amb_repeat.sablecc b/contrib/nitcc/tests/amb_repeat.sablecc new file mode 100644 index 0000000000..06f09c058f --- /dev/null +++ b/contrib/nitcc/tests/amb_repeat.sablecc @@ -0,0 +1,6 @@ +Grammar amb; +Parser +Ignored #10, #32; +s = a | a 'x' ; +a = b | b 'x' ; +b = 'c' ; diff --git a/contrib/nitcc/tests/amb_repeat0.input b/contrib/nitcc/tests/amb_repeat0.input new file mode 100644 index 0000000000..ce01362503 --- /dev/null +++ b/contrib/nitcc/tests/amb_repeat0.input @@ -0,0 +1 @@ +hello diff --git a/contrib/nitcc/tests/amb_repeat0.sablecc b/contrib/nitcc/tests/amb_repeat0.sablecc new file mode 100644 index 0000000000..87f69183c4 --- /dev/null +++ b/contrib/nitcc/tests/amb_repeat0.sablecc @@ -0,0 +1,4 @@ +Grammar amb; +Parser +Ignored #10, #32; +s = 'hello' | 'hello' ; diff --git a/contrib/nitcc/tests/amb_repeat2.input b/contrib/nitcc/tests/amb_repeat2.input new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/nitcc/tests/amb_repeat2.sablecc b/contrib/nitcc/tests/amb_repeat2.sablecc new file mode 100644 index 0000000000..8ad8d096c7 --- /dev/null +++ b/contrib/nitcc/tests/amb_repeat2.sablecc @@ -0,0 +1,6 @@ +Grammar amb; +Parser +Ignored #10, #32; +s = a | Empty ; +a = b | Empty ; +b = 'c' ; diff --git a/contrib/nitcc/tests/calc1.sablecc b/contrib/nitcc/tests/calc1.sablecc new file mode 100644 index 0000000000..6e61381370 --- /dev/null +++ b/contrib/nitcc/tests/calc1.sablecc @@ -0,0 +1,13 @@ +Grammar calc; +Lexer +n = ('0'..'9')+ ; +Parser +Ignored #10, #32; +e + = e '+' a + | e '-' a + ; +a = + | '(' e ')' + | n + ; diff --git a/contrib/nitcc/tests/dangling_else.input b/contrib/nitcc/tests/dangling_else.input new file mode 100644 index 0000000000..a9735477ac --- /dev/null +++ b/contrib/nitcc/tests/dangling_else.input @@ -0,0 +1 @@ +i a i a e a diff --git a/contrib/nitcc/tests/dangling_else.sablecc b/contrib/nitcc/tests/dangling_else.sablecc new file mode 100644 index 0000000000..1909e82efa --- /dev/null +++ b/contrib/nitcc/tests/dangling_else.sablecc @@ -0,0 +1,9 @@ +Grammar dangling; +Parser +Ignored #10, #32; +ss = s*; +s + = 'i' s 'e' s + | 'i' s + | 'a' + ; diff --git a/contrib/nitcc/tests/dangling_else2.input b/contrib/nitcc/tests/dangling_else2.input new file mode 100644 index 0000000000..a9735477ac --- /dev/null +++ b/contrib/nitcc/tests/dangling_else2.input @@ -0,0 +1 @@ +i a i a e a diff --git a/contrib/nitcc/tests/dangling_else2.sablecc b/contrib/nitcc/tests/dangling_else2.sablecc new file mode 100644 index 0000000000..dc4be1210e --- /dev/null +++ b/contrib/nitcc/tests/dangling_else2.sablecc @@ -0,0 +1,14 @@ +Grammar dangling; +Parser +Ignored #10, #32; +ss = s*; + +s + = 'a' + | 'i' se 'e' s + | 'i' s + ; +se + = 'a' + | 'i' se 'e' se + ; diff --git a/contrib/nitcc/tests/dangling_end.input b/contrib/nitcc/tests/dangling_end.input new file mode 100644 index 0000000000..c355025fe1 --- /dev/null +++ b/contrib/nitcc/tests/dangling_end.input @@ -0,0 +1,5 @@ +a id +a id end +a a id end end +a a id end +a a id diff --git a/contrib/nitcc/tests/dangling_end.sablecc b/contrib/nitcc/tests/dangling_end.sablecc new file mode 100644 index 0000000000..fe3db41a50 --- /dev/null +++ b/contrib/nitcc/tests/dangling_end.sablecc @@ -0,0 +1,9 @@ +Grammar dangling; +Parser +Ignored #10, #32; +es = e*; +e + = 'a' e 'end' + | 'a' e + | 'id' + ; diff --git a/contrib/nitcc/tests/dragon_4-55.input b/contrib/nitcc/tests/dragon_4-55.input new file mode 100644 index 0000000000..3c19c181b0 --- /dev/null +++ b/contrib/nitcc/tests/dragon_4-55.input @@ -0,0 +1 @@ +ccdd diff --git a/contrib/nitcc/tests/dragon_4-55.sablecc b/contrib/nitcc/tests/dragon_4-55.sablecc new file mode 100644 index 0000000000..89bbdd016e --- /dev/null +++ b/contrib/nitcc/tests/dragon_4-55.sablecc @@ -0,0 +1,5 @@ +Grammar dragon; +Parser +Ignored #10, #32; +s = c c ; +c = 'c' c | 'd' ; diff --git a/contrib/nitcc/tests/oneliner.input b/contrib/nitcc/tests/oneliner.input new file mode 100644 index 0000000000..ef3a7434cd --- /dev/null +++ b/contrib/nitcc/tests/oneliner.input @@ -0,0 +1,4 @@ +if id then id +if id then id end +if if id then id then id end +if id then if id then id end diff --git a/contrib/nitcc/tests/oneliner.sablecc b/contrib/nitcc/tests/oneliner.sablecc new file mode 100644 index 0000000000..35b0c44df6 --- /dev/null +++ b/contrib/nitcc/tests/oneliner.sablecc @@ -0,0 +1,34 @@ +Grammar oneliner; +Lexer +eol = #10 | #13 ; + +Parser +Ignored #32; + +prog + = stmts + ; +stmt + = if_block stmt_or_end + | if_block stmt_else stmt_or_end + | 'id' + ; +if_block + = 'if' n? stmt no 'then' + ; +stmt_or_end + = stmt 'end'? + | n stmts no 'end' + | no 'end' + ; +stmt_else + = stmt 'else' + | n stmts no 'else' + | 'else' + ; +stmts + = stmts n stmt + | stmt + ; +no = n?; +n = eol+; diff --git a/contrib/nitcc/tests/oneliner2.sablecc b/contrib/nitcc/tests/oneliner2.sablecc new file mode 100644 index 0000000000..5a555c00f4 --- /dev/null +++ b/contrib/nitcc/tests/oneliner2.sablecc @@ -0,0 +1,42 @@ +Grammar oneliner; +Lexer +eol = #10 | #13 ; + +Parser +Ignored #32; + +prog + = stmts + ; +stmt + = if_block stmt_or_end + | if_block stmt_else stmt_or_end + | 'id' + ; +stmt_with_else + = if_block stmt_else stmt_or_end + | 'id' + ; +if_block + = 'if' n? stmt no 'then' + ; +stmt_or_end + = stmt 'end'? + | n stmts no 'end' + | no 'end' + ; +stmt_end + = stmt 'else' + | n stmts no 'else' + | 'else' + ; +stmts_with_else + = stmts_with_else n stmt_with_else + | stmt_with_else + ; +stmts + = stmts n stmt + | stmt + ; +no = n?; +n = eol+; diff --git a/contrib/nitcc/tests/regex0.input b/contrib/nitcc/tests/regex0.input new file mode 100644 index 0000000000..27680d50a4 --- /dev/null +++ b/contrib/nitcc/tests/regex0.input @@ -0,0 +1 @@ +aa|aa* diff --git a/contrib/nitcc/tests/regex0.sablecc b/contrib/nitcc/tests/regex0.sablecc new file mode 100644 index 0000000000..95b7d0fbcc --- /dev/null +++ b/contrib/nitcc/tests/regex0.sablecc @@ -0,0 +1,5 @@ +Grammar regex; +Parser +Ignored #10, #32; +e = a | e '|' e | e e | e '*' ; +a = 'a' ; diff --git a/contrib/nitcc/tests/regex1.input b/contrib/nitcc/tests/regex1.input new file mode 100644 index 0000000000..27680d50a4 --- /dev/null +++ b/contrib/nitcc/tests/regex1.input @@ -0,0 +1 @@ +aa|aa* diff --git a/contrib/nitcc/tests/regex1.sablecc b/contrib/nitcc/tests/regex1.sablecc new file mode 100644 index 0000000000..df85db613f --- /dev/null +++ b/contrib/nitcc/tests/regex1.sablecc @@ -0,0 +1,13 @@ +Grammar regex; +Parser +Ignored #10, #32; +e + = a +Unary + e '*' +Left + e e +Left + e '|' e + ; +a = 'a' ; diff --git a/contrib/nitcc/tests/regex2.input b/contrib/nitcc/tests/regex2.input new file mode 100644 index 0000000000..27680d50a4 --- /dev/null +++ b/contrib/nitcc/tests/regex2.input @@ -0,0 +1 @@ +aa|aa* diff --git a/contrib/nitcc/tests/regex2.sablecc b/contrib/nitcc/tests/regex2.sablecc new file mode 100644 index 0000000000..34f3c98f3e --- /dev/null +++ b/contrib/nitcc/tests/regex2.sablecc @@ -0,0 +1,7 @@ +Grammar regex; +Parser +Ignored #10, #32; +e = e '|' e2 | e2; +e2 = e2 e3 | e3 ; +e3 = e4 '*' | e4 ; +e4 = 'a' ; diff --git a/contrib/nitcc/tests/sav/amb.res b/contrib/nitcc/tests/sav/amb.res index 05b05f7966..56ca3ec16e 100644 --- a/contrib/nitcc/tests/sav/amb.res +++ b/contrib/nitcc/tests/sav/amb.res @@ -1 +1,7 @@ +REDUCE/REDUCE Conflict on state 1 e for token Eof: +REDUCE on item: e→ e·, Eof +REDUCE on item: e→ e·, Eof +SHIFT/REDUCE Conflict on state 1 e for token Eof: +SHIFT on item: _start→ e·Eof +REDUCE on item: e→ e·, Eof Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/amb2.res b/contrib/nitcc/tests/sav/amb2.res index 05b05f7966..1dbd4df199 100644 --- a/contrib/nitcc/tests/sav/amb2.res +++ b/contrib/nitcc/tests/sav/amb2.res @@ -1 +1,4 @@ +REDUCE/REDUCE Conflict on state 7 '-' e for token '+': +REDUCE on item: a→ e·, Eof/'+' +REDUCE on item: b→ e·, '+' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/amb3.res b/contrib/nitcc/tests/sav/amb3.res index 05b05f7966..42bc0041fe 100644 --- a/contrib/nitcc/tests/sav/amb3.res +++ b/contrib/nitcc/tests/sav/amb3.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 3 'a' for token 'b': +SHIFT on item: x→·'b', Eof +REDUCE on item: x→ 'a'·, 'b' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/amb_repeat.input.res b/contrib/nitcc/tests/sav/amb_repeat.input.res new file mode 100644 index 0000000000..46f44847c3 --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat.input.res @@ -0,0 +1,7 @@ +Start + s_0 + a_1 + b + 'c'@(1:1-1:2) + 'x'@(1:3-1:4) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/amb_repeat.res b/contrib/nitcc/tests/sav/amb_repeat.res new file mode 100644 index 0000000000..5e4532fd4b --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat.res @@ -0,0 +1,4 @@ +SHIFT/REDUCE Conflict on state 3 b for token 'x': +SHIFT on item: a→ b·'x', Eof/'x' +REDUCE on item: a→ b·, Eof/'x' +Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/amb_repeat0.input.res b/contrib/nitcc/tests/sav/amb_repeat0.input.res new file mode 100644 index 0000000000..2d7766489f --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat0.input.res @@ -0,0 +1,4 @@ +Start + s_0 + 'hello'@(1:1-1:6) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/amb_repeat0.res b/contrib/nitcc/tests/sav/amb_repeat0.res new file mode 100644 index 0000000000..2ea68e8967 --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat0.res @@ -0,0 +1,4 @@ +REDUCE/REDUCE Conflict on state 2 'hello' for token Eof: +REDUCE on item: s→ 'hello'·, Eof +REDUCE on item: s→ 'hello'·, Eof +Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/amb_repeat2.input.res b/contrib/nitcc/tests/sav/amb_repeat2.input.res new file mode 100644 index 0000000000..79888666d8 --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat2.input.res @@ -0,0 +1,4 @@ +Start + s_0 + a_1 + Eof@(1:1-1:1)='' diff --git a/contrib/nitcc/tests/sav/amb_repeat2.res b/contrib/nitcc/tests/sav/amb_repeat2.res new file mode 100644 index 0000000000..b4991cbf7d --- /dev/null +++ b/contrib/nitcc/tests/sav/amb_repeat2.res @@ -0,0 +1,4 @@ +REDUCE/REDUCE Conflict on state 0 for token Eof: +REDUCE on item: a→·, Eof +REDUCE on item: s→·, Eof +Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/calc0.res b/contrib/nitcc/tests/sav/calc0.res new file mode 100644 index 0000000000..677252ef7e --- /dev/null +++ b/contrib/nitcc/tests/sav/calc0.res @@ -0,0 +1,12 @@ +Automatic Dangling SHIFT/REDUCE on state 8 e '+' e for token '+': + reduce: e→ e '+' e·, Eof/'+'/'*'/')' + shift: e→ e·'+' e +Automatic Dangling SHIFT/REDUCE on state 8 e '+' e for token '*': + reduce: e→ e '+' e·, Eof/'+'/'*'/')' + shift: e→ e·'*' e +Automatic Dangling SHIFT/REDUCE on state 9 e '*' e for token '+': + reduce: e→ e '*' e·, '+'/'*'/Eof/')' + shift: e→ e·'+' e +Automatic Dangling SHIFT/REDUCE on state 9 e '*' e for token '*': + reduce: e→ e '*' e·, '+'/'*'/Eof/')' + shift: e→ e·'*' e diff --git a/contrib/nitcc/tests/sav/calc1.res b/contrib/nitcc/tests/sav/calc1.res new file mode 100644 index 0000000000..9841662c3f --- /dev/null +++ b/contrib/nitcc/tests/sav/calc1.res @@ -0,0 +1 @@ +Runtime error: Aborted (./lrautomaton.nit:507) diff --git a/contrib/nitcc/tests/sav/conflict-bracket.res b/contrib/nitcc/tests/sav/conflict-bracket.res index 05b05f7966..b064ad5df8 100644 --- a/contrib/nitcc/tests/sav/conflict-bracket.res +++ b/contrib/nitcc/tests/sav/conflict-bracket.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 8 '[' e '[' for token '[': +SHIFT on item: e→·'[' e '[', ']'/'[' +REDUCE on item: e→ '[' e '['·, Eof/'['/']' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt1.res b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt1.res index 05b05f7966..539f6109aa 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt1.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt1.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 7 '1' e for token '2': +SHIFT on item: x→·'2', Eof/'2' +REDUCE on item: e→ '1' e·, Eof/'2' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt2.res b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt2.res index 05b05f7966..471906d1e5 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt2.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.alt2.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 7 '1' e for token '2': +SHIFT on item: x→·'2', Eof/'2' +REDUCE on item: y→·, Eof/'2' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.res b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.res index 05b05f7966..a9dbe42c29 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.1alt1.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.1alt1.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 7 '1' e for token '2': +SHIFT on item: e→ '1' e·'2', Eof/'2' +REDUCE on item: e→ '1' e·, Eof/'2' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/conflict-dangling.alt1.res b/contrib/nitcc/tests/sav/conflict-dangling.alt1.res index e69de29bb2..2e6551aecc 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.alt1.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.alt1.res @@ -0,0 +1,4 @@ +Automatic Dangling SHIFT/REDUCE on state 6 '1' e for token '2': + reduce: e→ '1' e·, Eof/'2' + shift: x→·'2' + core shift: e→ '1' e·x, Eof/'2' diff --git a/contrib/nitcc/tests/sav/conflict-dangling.alt2.res b/contrib/nitcc/tests/sav/conflict-dangling.alt2.res index e69de29bb2..00ab6fed31 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.alt2.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.alt2.res @@ -0,0 +1,4 @@ +Automatic Dangling SHIFT/REDUCE on state 6 '1' e for token '2': + reduce: y→·, Eof/'2' + shift: x→·'2' + core shift: y→·x, Eof/'2' diff --git a/contrib/nitcc/tests/sav/conflict-dangling.res b/contrib/nitcc/tests/sav/conflict-dangling.res index e69de29bb2..9628b0248b 100644 --- a/contrib/nitcc/tests/sav/conflict-dangling.res +++ b/contrib/nitcc/tests/sav/conflict-dangling.res @@ -0,0 +1,3 @@ +Automatic Dangling SHIFT/REDUCE on state 6 '1' e for token '2': + reduce: e→ '1' e·, Eof/'2' + shift: e→ '1' e·'2' diff --git a/contrib/nitcc/tests/sav/dangling_else.input.res b/contrib/nitcc/tests/sav/dangling_else.input.res new file mode 100644 index 0000000000..05d5d9768b --- /dev/null +++ b/contrib/nitcc/tests/sav/dangling_else.input.res @@ -0,0 +1,15 @@ +Start + ss + Nodes[Ns] + s_1 + 'i'@(1:1-1:2) + s_2 + 'a'@(1:3-1:4) + s_0 + 'i'@(1:5-1:6) + s_2 + 'a'@(1:7-1:8) + 'e'@(1:9-1:10) + s_2 + 'a'@(1:11-1:12) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/dangling_else.res b/contrib/nitcc/tests/sav/dangling_else.res new file mode 100644 index 0000000000..1bf9a258fa --- /dev/null +++ b/contrib/nitcc/tests/sav/dangling_else.res @@ -0,0 +1,3 @@ +Automatic Dangling SHIFT/REDUCE on state 8 'i' s for token 'e': + reduce: s→ 'i' s·, Eof/'i'/'a'/'e' + shift: s→ 'i' s·'e' s diff --git a/contrib/nitcc/tests/sav/dangling_else2.input.res b/contrib/nitcc/tests/sav/dangling_else2.input.res new file mode 100644 index 0000000000..dfd9952ba4 --- /dev/null +++ b/contrib/nitcc/tests/sav/dangling_else2.input.res @@ -0,0 +1,15 @@ +Start + ss + Nodes[Ns] + s_2 + 'i'@(1:1-1:2) + s_0 + 'a'@(1:3-1:4) + s_1 + 'i'@(1:5-1:6) + se_0 + 'a'@(1:7-1:8) + 'e'@(1:9-1:10) + s_0 + 'a'@(1:11-1:12) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/dangling_end.input.res b/contrib/nitcc/tests/sav/dangling_end.input.res new file mode 100644 index 0000000000..0d7a3c2957 --- /dev/null +++ b/contrib/nitcc/tests/sav/dangling_end.input.res @@ -0,0 +1,34 @@ +Start + es + Nodes[Ne] + e_1 + 'a'@(1:1-1:2) + e_2 + 'id'@(1:3-1:5) + e_0 + 'a'@(2:1-2:2) + e_2 + 'id'@(2:3-2:5) + 'end'@(2:6-2:9) + e_0 + 'a'@(3:1-3:2) + e_0 + 'a'@(3:3-3:4) + e_2 + 'id'@(3:5-3:7) + 'end'@(3:8-3:11) + 'end'@(3:12-3:15) + e_1 + 'a'@(4:1-4:2) + e_0 + 'a'@(4:3-4:4) + e_2 + 'id'@(4:5-4:7) + 'end'@(4:8-4:11) + e_1 + 'a'@(5:1-5:2) + e_1 + 'a'@(5:3-5:4) + e_2 + 'id'@(5:5-5:7) + Eof@(6:1-6:1)='' diff --git a/contrib/nitcc/tests/sav/dangling_end.res b/contrib/nitcc/tests/sav/dangling_end.res new file mode 100644 index 0000000000..79743ba0cb --- /dev/null +++ b/contrib/nitcc/tests/sav/dangling_end.res @@ -0,0 +1,3 @@ +Automatic Dangling SHIFT/REDUCE on state 8 'a' e for token 'end': + reduce: e→ 'a' e·, Eof/'a'/'id'/'end' + shift: e→ 'a' e·'end' diff --git a/contrib/nitcc/tests/sav/dragon_4-55.input.res b/contrib/nitcc/tests/sav/dragon_4-55.input.res new file mode 100644 index 0000000000..9aa242890e --- /dev/null +++ b/contrib/nitcc/tests/sav/dragon_4-55.input.res @@ -0,0 +1,11 @@ +Start + s + c_0 + 'c'@(1:1-1:2) + c_0 + 'c'@(1:2-1:3) + c_1 + 'd'@(1:3-1:4) + c_1 + 'd'@(1:4-1:5) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/eq.res b/contrib/nitcc/tests/sav/eq.res index 05b05f7966..e69de29bb2 100644 --- a/contrib/nitcc/tests/sav/eq.res +++ b/contrib/nitcc/tests/sav/eq.res @@ -1 +0,0 @@ -Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/eq2.res b/contrib/nitcc/tests/sav/eq2.res index 05b05f7966..0a848c9ced 100644 --- a/contrib/nitcc/tests/sav/eq2.res +++ b/contrib/nitcc/tests/sav/eq2.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 2 var for token '=': +SHIFT on item: e→ var·'=' e, Eof/'=' +REDUCE on item: e→ var·, Eof/'=' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/eq3.res b/contrib/nitcc/tests/sav/eq3.res index 05b05f7966..fb928143b3 100644 --- a/contrib/nitcc/tests/sav/eq3.res +++ b/contrib/nitcc/tests/sav/eq3.res @@ -1 +1,10 @@ +SHIFT/REDUCE Conflict on state 2 var for token '=': +SHIFT on item: e→ var·'=' e, Eof/'=' +REDUCE on item: e→ var·, '='/Eof +Automatic Dangling SHIFT/REDUCE on state 8 e '=' e for token '=': + reduce: c→ e '=' e·, Eof/'=' + shift: c→ e·'=' e +SHIFT/REDUCE Conflict on state 9 var '=' e for token '=': +SHIFT on item: c→ e·'=' e, Eof/'=' +REDUCE on item: e→ var '=' e·, Eof/'=' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/if.res b/contrib/nitcc/tests/sav/if.res index e69de29bb2..410744a738 100644 --- a/contrib/nitcc/tests/sav/if.res +++ b/contrib/nitcc/tests/sav/if.res @@ -0,0 +1,7 @@ +Automatic Dangling SHIFT/REDUCE on state 15 'e' 'if' e 'then' e for token 'else': + reduce: e→ 'if' e 'then' e·, Eof/'then'/'else' + shift: else→·'else' e + core shift: e→ 'if' e 'then' e·else, Eof/'then'/'else' +Automatic Dangling SHIFT/REDUCE on state 16 'f' 'if' f 'then' f for token 'else': + reduce: f→ 'if' f 'then' f·, Eof/'then'/'else' + shift: f→ 'if' f 'then' f·'else' f diff --git a/contrib/nitcc/tests/sav/inf5000-05-grammaire-arithmetique.res b/contrib/nitcc/tests/sav/inf5000-05-grammaire-arithmetique.res index e69de29bb2..ab6ab10894 100644 --- a/contrib/nitcc/tests/sav/inf5000-05-grammaire-arithmetique.res +++ b/contrib/nitcc/tests/sav/inf5000-05-grammaire-arithmetique.res @@ -0,0 +1,27 @@ +Automatic Dangling SHIFT/REDUCE on state 12 exp '+' exp for token '+': + reduce: exp→ exp '+' exp·, Eof/'+'/'-'/'*'/int/'('/')' + shift: exp→ exp·'+' exp +Automatic Dangling SHIFT/REDUCE on state 12 exp '+' exp for token '-': + reduce: exp→ exp '+' exp·, Eof/'+'/'-'/'*'/int/'('/')' + shift: exp→ exp·'-' exp +Automatic Dangling SHIFT/REDUCE on state 12 exp '+' exp for token '*': + reduce: exp→ exp '+' exp·, Eof/'+'/'-'/'*'/int/'('/')' + shift: exp→ exp·'*' exp +Automatic Dangling SHIFT/REDUCE on state 13 exp '-' exp for token '+': + reduce: exp→ exp '-' exp·, '+'/'*'/'-'/Eof/int/'('/')' + shift: exp→ exp·'+' exp +Automatic Dangling SHIFT/REDUCE on state 13 exp '-' exp for token '*': + reduce: exp→ exp '-' exp·, '+'/'*'/'-'/Eof/int/'('/')' + shift: exp→ exp·'*' exp +Automatic Dangling SHIFT/REDUCE on state 13 exp '-' exp for token '-': + reduce: exp→ exp '-' exp·, '+'/'*'/'-'/Eof/int/'('/')' + shift: exp→ exp·'-' exp +Automatic Dangling SHIFT/REDUCE on state 14 exp '*' exp for token '+': + reduce: exp→ exp '*' exp·, '+'/'-'/'*'/Eof/int/'('/')' + shift: exp→ exp·'+' exp +Automatic Dangling SHIFT/REDUCE on state 14 exp '*' exp for token '-': + reduce: exp→ exp '*' exp·, '+'/'-'/'*'/Eof/int/'('/')' + shift: exp→ exp·'-' exp +Automatic Dangling SHIFT/REDUCE on state 14 exp '*' exp for token '*': + reduce: exp→ exp '*' exp·, '+'/'-'/'*'/Eof/int/'('/')' + shift: exp→ exp·'*' exp diff --git a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-grammaire2.input.res b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-grammaire2.input.res index 78e60f926f..f9dc48e425 100644 --- a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-grammaire2.input.res +++ b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-grammaire2.input.res @@ -1,41 +1,95 @@ -T_Id@1,1:prods -TAnonymous@1,7:= -T_Altid@1,9:{many:} -T_Id@1,17:prods -T_Id@1,23:prod -TAnonymous@1,29:| -T_Altid@1,31:{one:} -T_Id@1,38:prod -TAnonymous@1,43:; -T_Id@2,1:prod -TAnonymous@2,6:= -T_Id@2,8:id -T_Str@2,11:'=' -T_Id@2,15:alts -T_Str@2,20:';' -TAnonymous@2,24:; -T_Id@3,1:alts -TAnonymous@3,6:= -T_Altid@3,8:{many:} -T_Id@3,16:alts -T_Str@3,21:'|' -T_Id@3,25:alt -TAnonymous@3,29:| -T_Altid@3,31:{one:} -T_Id@3,38:alt -TAnonymous@3,42:; -T_Id@4,1:alt -TAnonymous@4,5:= -T_Id@4,7:altid -T_Id@4,13:atoms -TAnonymous@4,19:| -T_Id@4,21:atoms -TAnonymous@4,27:; -T_Id@5,1:atoms -TAnonymous@5,7:= -T_Altid@5,9:{many:} -T_Id@5,17:atoms -T_Id@5,23:atom -TAnonymous@5,28:| -T_Altid@5,30:{none:} -5,38. Syntax error: Unexpected character 'E'. +NLexerError@(5:38-5:38)='E' +Nodes[Node] + Nodes[Nprod] + prod + id@(1:1-1:6)='prods' + '='@(1:7-1:8) + Nodes[N_group1] + _group1_single + alt + altid@(1:9-1:16)='{many:}' + Nodes[Natom] + atom_id + id@(1:17-1:22)='prods' + atom_id + id@(1:23-1:27)='prod' + '|'@(1:29-1:30) + _group0_single + alt + altid@(1:31-1:37)='{one:}' + Nodes[Natom] + atom_id + id@(1:38-1:42)='prod' + ';'@(1:43-1:44) + prod + id@(2:1-2:5)='prod' + '='@(2:6-2:7) + Nodes[N_group1] + _group0_single + alt + Nodes[Natom] + atom_id + id@(2:8-2:10)='id' + atom_str + str@(2:11-2:14)='\'=\'' + atom_id + id@(2:15-2:19)='alts' + atom_str + str@(2:20-2:23)='\';\'' + ';'@(2:24-2:25) + prod + id@(3:1-3:5)='alts' + '='@(3:6-3:7) + Nodes[N_group1] + _group1_single + alt + altid@(3:8-3:15)='{many:}' + Nodes[Natom] + atom_id + id@(3:16-3:20)='alts' + atom_str + str@(3:21-3:24)='\'|\'' + atom_id + id@(3:25-3:28)='alt' + '|'@(3:29-3:30) + _group0_single + alt + altid@(3:31-3:37)='{one:}' + Nodes[Natom] + atom_id + id@(3:38-3:41)='alt' + ';'@(3:42-3:43) + prod + id@(4:1-4:4)='alt' + '='@(4:5-4:6) + Nodes[N_group1] + _group1_single + alt + Nodes[Natom] + atom_id + id@(4:7-4:12)='altid' + atom_id + id@(4:13-4:18)='atoms' + '|'@(4:19-4:20) + _group0_single + alt + Nodes[Natom] + atom_id + id@(4:21-4:26)='atoms' + ';'@(4:27-4:28) + id@(5:1-5:6)='atoms' + '='@(5:7-5:8) + Nodes[N_group1] + _group1_single + alt + altid@(5:9-5:16)='{many:}' + Nodes[Natom] + atom_id + id@(5:17-5:22)='atoms' + atom_id + id@(5:23-5:27)='atom' + '|'@(5:28-5:29) + _group0_single + alt + altid@(5:30-5:37)='{none:}' + NLexerError@(5:38-5:38)='E' diff --git a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-instructions.alt3.input.res b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-instructions.alt3.input.res index 664d77291c..8c1b7d4d0d 100644 --- a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-instructions.alt3.input.res +++ b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-instructions.alt3.input.res @@ -1,18 +1,32 @@ -T_Id@1,1:x -TAnonymous@1,3:= -T_Id@1,5:y -TAnonymous@1,6:; -TAnonymous@2,1:while -TAnonymous@2,7:( -T_Id@2,8:z -TAnonymous@2,9:) -TAnonymous@2,11:{ -TAnonymous@2,13:print -T_Id@2,19:t -TAnonymous@2,20:; -T_Id@2,22:k -TAnonymous@2,23:= -T_Id@2,24:u -TAnonymous@2,25:; -TAnonymous@2,27:} -TEnd@3,1: +Start + prog + Nodes[Nstmt] + stmt_assign + id@(1:1-1:2)='x' + '='@(1:3-1:4) + expr + id@(1:5-1:6)='y' + ';'@(1:6-1:7) + stmt_while + 'while'@(2:1-2:6) + '('@(2:7-2:8) + expr + id@(2:8-2:9)='z' + ')'@(2:9-2:10) + '{'@(2:11-2:12) + Nodes[Nstmt] + stmt_print + 'print'@(2:13-2:18) + '('@(2:18-2:19) + expr + id@(2:19-2:20)='t' + ')'@(2:20-2:21) + ';'@(2:21-2:22) + stmt_assign + id@(2:23-2:24)='k' + '='@(2:24-2:25) + expr + id@(2:25-2:26)='u' + ';'@(2:26-2:27) + '}'@(2:28-2:29) + Eof@(3:1-3:1)='' diff --git a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-polygone.input.res b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-polygone.input.res index 4beb812048..e7b2771fc9 100644 --- a/contrib/nitcc/tests/sav/inf5000-06-grammaire2-polygone.input.res +++ b/contrib/nitcc/tests/sav/inf5000-06-grammaire2-polygone.input.res @@ -1,10 +1,34 @@ -TAnonymous@1,1:( -TAnonymous@1,2:( -T_Num@1,3:0 -TAnonymous@1,4:x -TAnonymous@1,5:, -T_Num@1,6:0 -TAnonymous@1,7:y -TAnonymous@1,8:) -TAnonymous@1,9:, -1,10. Syntax error: Unexpected character ' '. +Start + polygone + '('@(1:1-1:2) + Nodes[N_group1] + _group1_single + point_cart + '('@(1:2-1:3) + num@(1:3-1:4)='0' + 'x'@(1:4-1:5) + ','@(1:5-1:6) + num@(1:6-1:7)='0' + 'y'@(1:7-1:8) + ')'@(1:8-1:9) + ','@(1:9-1:10) + _group1_single + point_pol + '('@(1:11-1:12) + num@(1:12-1:14)='10' + ','@(1:14-1:15) + num@(1:16-1:17)='0' + 'deg'@(1:17-1:20) + ')'@(1:20-1:21) + ','@(1:21-1:22) + _group0_single + point_cart + '('@(1:23-1:24) + num@(1:24-1:25)='0' + 'x'@(1:25-1:26) + ','@(1:26-1:27) + num@(1:28-1:30)='10' + 'y'@(1:30-1:31) + ')'@(1:31-1:32) + ')'@(1:32-1:33) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/lg.res b/contrib/nitcc/tests/sav/lg.res index 05b05f7966..6515a9f189 100644 --- a/contrib/nitcc/tests/sav/lg.res +++ b/contrib/nitcc/tests/sav/lg.res @@ -1 +1,4 @@ +SHIFT/REDUCE Conflict on state 12 '<' e '>' for token '<': +SHIFT on item: a→·'<' e '>', '<'/'>' +REDUCE on item: a→ '<' e '>'·, '<'/'>'/Eof Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/lg2.res b/contrib/nitcc/tests/sav/lg2.res index 05b05f7966..43905114f4 100644 --- a/contrib/nitcc/tests/sav/lg2.res +++ b/contrib/nitcc/tests/sav/lg2.res @@ -1 +1,16 @@ +Automatic Dangling SHIFT/REDUCE on state 9 e '<' e for token '<': + reduce: e→ e '<' e·, Eof/'<'/'>' + shift: e→ e·'<' e +SHIFT/REDUCE Conflict on state 9 e '<' e for token '>': +SHIFT on item: e→ e·'>' e, '<'/'>'/Eof +REDUCE on item: e→ e '<' e·, Eof/'<'/'>' +Automatic Dangling SHIFT/REDUCE on state 10 e '>' e for token '<': + reduce: e→ e '>' e·, '<'/'>'/Eof + shift: e→ e·'<' e +SHIFT/REDUCE Conflict on state 10 e '>' e for token '>': +SHIFT on item: e→ e·'>' e, '<'/'>'/Eof +REDUCE on item: e→ e '>' e·, '<'/'>'/Eof +SHIFT/REDUCE Conflict on state 11 '<' e '>' for token '<': +SHIFT on item: e→·'<' e '>', '>'/'<' +REDUCE on item: e→ '<' e '>'·, '<'/'>'/Eof Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/lr1.res b/contrib/nitcc/tests/sav/lr1.res index 05b05f7966..37519c4791 100644 --- a/contrib/nitcc/tests/sav/lr1.res +++ b/contrib/nitcc/tests/sav/lr1.res @@ -1 +1,4 @@ +REDUCE/REDUCE Conflict on state 3 'x' for token 'a': +REDUCE on item: q→ 'x'·, 'a' +REDUCE on item: r→ 'x'·, 'a' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/nit_expr.res b/contrib/nitcc/tests/sav/nit_expr.res index 05b05f7966..d062a19988 100644 --- a/contrib/nitcc/tests/sav/nit_expr.res +++ b/contrib/nitcc/tests/sav/nit_expr.res @@ -1 +1,14 @@ +SHIFT/REDUCE Conflict on state 3 'id' for token '[': +SHIFT on item: e→·'[' e ']', '['/Eof/']'/'..' +SHIFT on item: e→·'[' e '..' e '[', '['/Eof/']'/'..' +SHIFT on item: e→·'[' e '..' e ']', '['/Eof/']'/'..' +REDUCE on item: e→ 'id'·, '['/Eof/']'/'..' +SHIFT/REDUCE Conflict on state 7 'id' e for token '[': +SHIFT on item: e→ e·'[' e ']', '['/Eof/']'/'..' +REDUCE on item: e→ 'id' e·, '['/Eof/']'/'..' +SHIFT/REDUCE Conflict on state 13 '[' e '..' e '[' for token '[': +SHIFT on item: e→·'[' e ']', '['/']' +SHIFT on item: e→·'[' e '..' e '[', '['/']' +SHIFT on item: e→·'[' e '..' e ']', '['/']' +REDUCE on item: e→ '[' e '..' e '['·, '['/Eof/']'/'..' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/not_lalr.res b/contrib/nitcc/tests/sav/not_lalr.res index 05b05f7966..57fe0eab52 100644 --- a/contrib/nitcc/tests/sav/not_lalr.res +++ b/contrib/nitcc/tests/sav/not_lalr.res @@ -1 +1,7 @@ +REDUCE/REDUCE Conflict on state 7 'a' 'a' for token 'a': +REDUCE on item: x→ 'a'·, 'a'/'b' +REDUCE on item: y→ 'a'·, 'b'/'a' +REDUCE/REDUCE Conflict on state 7 'a' 'a' for token 'b': +REDUCE on item: x→ 'a'·, 'a'/'b' +REDUCE on item: y→ 'a'·, 'b'/'a' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/not_slr.res b/contrib/nitcc/tests/sav/not_slr.res index 05b05f7966..e69de29bb2 100644 --- a/contrib/nitcc/tests/sav/not_slr.res +++ b/contrib/nitcc/tests/sav/not_slr.res @@ -1 +0,0 @@ -Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/oneliner.input.res b/contrib/nitcc/tests/sav/oneliner.input.res new file mode 100644 index 0000000000..83d628a8d3 --- /dev/null +++ b/contrib/nitcc/tests/sav/oneliner.input.res @@ -0,0 +1,78 @@ +NParserError@(5:1-5:1)='' +Nodes[Node] + stmts_0 + stmts_0 + stmts_0 + stmts_1 + stmt_0 + if_block + 'if'@(1:1-1:3) + stmt_2 + 'id'@(1:4-1:6) + no + 'then'@(1:7-1:11) + stmt_or_end_0 + stmt_2 + 'id'@(1:12-1:14) + n + Nodes[Neol] + eol@(1:14-2:1)='\n' + stmt_0 + if_block + 'if'@(2:1-2:3) + stmt_2 + 'id'@(2:4-2:6) + no + 'then'@(2:7-2:11) + stmt_or_end_0 + stmt_2 + 'id'@(2:12-2:14) + 'end'@(2:15-2:18) + n + Nodes[Neol] + eol@(2:18-3:1)='\n' + stmt_0 + if_block + 'if'@(3:1-3:3) + stmt_0 + if_block + 'if'@(3:4-3:6) + stmt_2 + 'id'@(3:7-3:9) + no + 'then'@(3:10-3:14) + stmt_or_end_0 + stmt_2 + 'id'@(3:15-3:17) + no + 'then'@(3:18-3:22) + stmt_or_end_0 + stmt_2 + 'id'@(3:23-3:25) + 'end'@(3:26-3:29) + n + Nodes[Neol] + eol@(3:29-4:1)='\n' + stmt_0 + if_block + 'if'@(4:1-4:3) + stmt_2 + 'id'@(4:4-4:6) + no + 'then'@(4:7-4:11) + stmt_or_end_0 + stmt_0 + if_block + 'if'@(4:12-4:14) + stmt_2 + 'id'@(4:15-4:17) + no + 'then'@(4:18-4:22) + stmt_or_end_0 + stmt_2 + 'id'@(4:23-4:25) + 'end'@(4:26-4:29) + n + Nodes[Neol] + eol@(4:29-5:1)='\n' + Eof@(5:1-5:1)='' diff --git a/contrib/nitcc/tests/sav/oneliner.res b/contrib/nitcc/tests/sav/oneliner.res new file mode 100644 index 0000000000..935bf37d51 --- /dev/null +++ b/contrib/nitcc/tests/sav/oneliner.res @@ -0,0 +1,10 @@ +SHIFT/REDUCE Conflict on state 15 if_block stmt for token 'end': +SHIFT on item: stmt_or_end→ stmt·'end', eol/Eof/'end'/'else'/'then' +REDUCE on item: stmt_or_end→ stmt·, eol/Eof/'end'/'else'/'then' +SHIFT/REDUCE Conflict on state 15 if_block stmt for token 'else': +SHIFT on item: stmt_else→ stmt·'else', 'end'/'id'/'if'/eol/Eof/'else'/'then' +REDUCE on item: stmt_or_end→ stmt·, eol/Eof/'end'/'else'/'then' +SHIFT/REDUCE Conflict on state 23 if_block stmt_else stmt for token 'end': +SHIFT on item: stmt_or_end→ stmt·'end', eol/Eof/'end'/'else'/'then' +REDUCE on item: stmt_or_end→ stmt·, eol/Eof/'end'/'else'/'then' +Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/oneliner2.res b/contrib/nitcc/tests/sav/oneliner2.res new file mode 100644 index 0000000000..11461b905b --- /dev/null +++ b/contrib/nitcc/tests/sav/oneliner2.res @@ -0,0 +1 @@ +13:13-13:22 Error: unknown name stmt_else diff --git a/contrib/nitcc/tests/sav/pri.res b/contrib/nitcc/tests/sav/pri.res index 05b05f7966..fa1c36cc90 100644 --- a/contrib/nitcc/tests/sav/pri.res +++ b/contrib/nitcc/tests/sav/pri.res @@ -1 +1,10 @@ +REDUCE/REDUCE Conflict on state 4 'r' for token Eof: +REDUCE on item: a→ 'r'·, Eof +REDUCE on item: r→·, Eof +Automatic Dangling SHIFT/REDUCE on state 14 'q' q q for token 'q': + reduce: q→ q q·, Eof/'q' + shift: q→·'q' +Automatic Dangling SHIFT/REDUCE on state 15 'p' p '+' p for token '+': + reduce: p→ p '+' p·, Eof/'+' + shift: p→ p·'+' p Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/priority.res b/contrib/nitcc/tests/sav/priority.res index e69de29bb2..ec23ed9aba 100644 --- a/contrib/nitcc/tests/sav/priority.res +++ b/contrib/nitcc/tests/sav/priority.res @@ -0,0 +1,27 @@ +Automatic Dangling SHIFT/REDUCE on state 7 e '+' e for token '+': + reduce: e→ e '+' e·, Eof/'+'/'-'/'*' + shift: e→ e·'+' e +Automatic Dangling SHIFT/REDUCE on state 7 e '+' e for token '-': + reduce: e→ e '+' e·, Eof/'+'/'-'/'*' + shift: e→ e·'-' e +Automatic Dangling SHIFT/REDUCE on state 7 e '+' e for token '*': + reduce: e→ e '+' e·, Eof/'+'/'-'/'*' + shift: e→ e·'*' e +Automatic Dangling SHIFT/REDUCE on state 8 e '-' e for token '+': + reduce: e→ e '-' e·, '+'/'*'/'-'/Eof + shift: e→ e·'+' e +Automatic Dangling SHIFT/REDUCE on state 8 e '-' e for token '*': + reduce: e→ e '-' e·, '+'/'*'/'-'/Eof + shift: e→ e·'*' e +Automatic Dangling SHIFT/REDUCE on state 8 e '-' e for token '-': + reduce: e→ e '-' e·, '+'/'*'/'-'/Eof + shift: e→ e·'-' e +Automatic Dangling SHIFT/REDUCE on state 9 e '*' e for token '+': + reduce: e→ e '*' e·, '+'/'-'/'*'/Eof + shift: e→ e·'+' e +Automatic Dangling SHIFT/REDUCE on state 9 e '*' e for token '-': + reduce: e→ e '*' e·, '+'/'-'/'*'/Eof + shift: e→ e·'-' e +Automatic Dangling SHIFT/REDUCE on state 9 e '*' e for token '*': + reduce: e→ e '*' e·, '+'/'-'/'*'/Eof + shift: e→ e·'*' e diff --git a/contrib/nitcc/tests/sav/qualified.res b/contrib/nitcc/tests/sav/qualified.res index 05b05f7966..b8392dd664 100644 --- a/contrib/nitcc/tests/sav/qualified.res +++ b/contrib/nitcc/tests/sav/qualified.res @@ -1 +1,8 @@ +Automatic Dangling SHIFT/REDUCE on state 2 r for token 'id': + reduce: q→·, 'id' + shift: id→·'id' + core shift: q→·qe+, 'id' +SHIFT/REDUCE Conflict on state 6 r qe+ for token 'id': +SHIFT on item: id→·'id', '::' +REDUCE on item: q→ qe+·, 'id' Error: there is conflicts diff --git a/contrib/nitcc/tests/sav/regex0.input.res b/contrib/nitcc/tests/sav/regex0.input.res new file mode 100644 index 0000000000..8fa8ca2802 --- /dev/null +++ b/contrib/nitcc/tests/sav/regex0.input.res @@ -0,0 +1,20 @@ +Start + e_2 + e_0 + a + 'a'@(1:1-1:2) + e_1 + e_0 + a + 'a'@(1:2-1:3) + '|'@(1:3-1:4) + e_2 + e_0 + a + 'a'@(1:4-1:5) + e_3 + e_0 + a + 'a'@(1:5-1:6) + '*'@(1:6-1:7) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/regex0.res b/contrib/nitcc/tests/sav/regex0.res new file mode 100644 index 0000000000..33d06a4324 --- /dev/null +++ b/contrib/nitcc/tests/sav/regex0.res @@ -0,0 +1,20 @@ +Automatic Dangling SHIFT/REDUCE on state 6 e e for token '|': + reduce: e→ e e·, '|'/'*'/'a'/Eof + shift: e→ e·'|' e +Automatic Dangling SHIFT/REDUCE on state 6 e e for token '*': + reduce: e→ e e·, '|'/'*'/'a'/Eof + shift: e→ e·'*' +Automatic Dangling SHIFT/REDUCE on state 6 e e for token 'a': + reduce: e→ e e·, '|'/'*'/'a'/Eof + shift: a→·'a' + core shift: e→·a, '|'/'a'/'*'/Eof +Automatic Dangling SHIFT/REDUCE on state 8 e '|' e for token '|': + reduce: e→ e '|' e·, Eof/'|'/'a'/'*' + shift: e→ e·'|' e +Automatic Dangling SHIFT/REDUCE on state 8 e '|' e for token 'a': + reduce: e→ e '|' e·, Eof/'|'/'a'/'*' + shift: a→·'a' + core shift: e→·a, '|'/'a'/'*'/Eof +Automatic Dangling SHIFT/REDUCE on state 8 e '|' e for token '*': + reduce: e→ e '|' e·, Eof/'|'/'a'/'*' + shift: e→ e·'*' diff --git a/contrib/nitcc/tests/sav/regex1.input.res b/contrib/nitcc/tests/sav/regex1.input.res new file mode 100644 index 0000000000..b6f6996368 --- /dev/null +++ b/contrib/nitcc/tests/sav/regex1.input.res @@ -0,0 +1,20 @@ +Start + e_0 + e$1_0 + e$0_0 + a + 'a'@(1:1-1:2) + e$0_0 + a + 'a'@(1:2-1:3) + '|'@(1:3-1:4) + e$1_0 + e$0_0 + a + 'a'@(1:4-1:5) + e$2_0 + e$0_0 + a + 'a'@(1:5-1:6) + '*'@(1:6-1:7) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/regex2.input.res b/contrib/nitcc/tests/sav/regex2.input.res new file mode 100644 index 0000000000..378cce4128 --- /dev/null +++ b/contrib/nitcc/tests/sav/regex2.input.res @@ -0,0 +1,22 @@ +Start + e_0 + e_1 + e2_0 + e2_1 + e3_1 + e4 + 'a'@(1:1-1:2) + e3_1 + e4 + 'a'@(1:2-1:3) + '|'@(1:3-1:4) + e2_0 + e2_1 + e3_1 + e4 + 'a'@(1:4-1:5) + e3_0 + e4 + 'a'@(1:5-1:6) + '*'@(1:6-1:7) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/unary.input.res b/contrib/nitcc/tests/sav/unary.input.res new file mode 100644 index 0000000000..be17f96445 --- /dev/null +++ b/contrib/nitcc/tests/sav/unary.input.res @@ -0,0 +1,12 @@ +Start + e_0 + '-'@(1:1-1:2) + e_0 + '-'@(1:2-1:3) + e_1 + e_1 + e_2 + '0'@(1:3-1:4) + '+'@(1:4-1:5) + '+'@(1:5-1:6) + Eof@(2:1-2:1)='' diff --git a/contrib/nitcc/tests/sav/unary.res b/contrib/nitcc/tests/sav/unary.res new file mode 100644 index 0000000000..5e7e20e3b0 --- /dev/null +++ b/contrib/nitcc/tests/sav/unary.res @@ -0,0 +1,3 @@ +Automatic Dangling SHIFT/REDUCE on state 6 '-' e for token '+': + reduce: e→ '-' e·, Eof/'+' + shift: e→ e·'+' diff --git a/contrib/nitcc/tests/t b/contrib/nitcc/tests/t index 37922f6a32..e019cca51f 100755 --- a/contrib/nitcc/tests/t +++ b/contrib/nitcc/tests/t @@ -110,7 +110,7 @@ do fi fi - grep -i "error" "out/$bn.nitcc.log" > "out/$res" + grep -i 'error\|shift\|reduce' "out/$bn.nitcc.log" > "out/$res" name="$a" differ $res || err=1 diff --git a/contrib/nitcc/tests/unary.input b/contrib/nitcc/tests/unary.input new file mode 100644 index 0000000000..d1a3e546f8 --- /dev/null +++ b/contrib/nitcc/tests/unary.input @@ -0,0 +1 @@ +--0++ diff --git a/contrib/nitcc/tests/unary.sablecc b/contrib/nitcc/tests/unary.sablecc new file mode 100644 index 0000000000..97b7b24478 --- /dev/null +++ b/contrib/nitcc/tests/unary.sablecc @@ -0,0 +1,8 @@ +Grammar unary; +Parser +Ignored #10, #32; +e + = '-' e + | e '+' + | '0' + ;