--- /dev/null
+*.dot
+*_parser.nit
+*_lexer.nit
+*_test_parser
+*.concrete_grammar.txt
+*.lr.txt
+t/alt
+t/out
+t/tap.output
+
+nitcc_parser_gen
+nitcc0
+nitcc1
+nitcc
+examples/calc
+examples/minilang
--- /dev/null
+# 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.
+
+# Finite automaton (NFA & DFA)
+module autom
+
+# For the class Token
+import grammar
+
+# A finite automaton
+class Automaton
+ # The start state
+ var start: State
+
+ # State that are accect states
+ var accept = new Array[State]
+
+ # All states
+ var states = new Array[State]
+
+ # Tokens associated on accept states
+ # use `add_tag` to update
+ var tags = new HashMap[State, Set[Token]]
+
+ # Accept states associated on tokens
+ # use `add_tag` to update
+ var retrotags = new HashMap[Token, Set[State]]
+
+ # Add a token to a state
+ fun add_tag(s: State, t: Token)
+ do
+ if not tags.has_key(s) then
+ var set = new ArraySet[Token]
+ tags[s] = set
+ set.add t
+ else
+ tags[s].add t
+ end
+
+ if not retrotags.has_key(t) then
+ var set = new ArraySet[State]
+ retrotags[t] = set
+ set.add s
+ else
+ retrotags[t].add s
+ end
+ end
+
+ # Remove tokens from conflicting state according the the inclusion of language
+ # REQUIRE: self isa DFA automaton
+ fun solve_token_inclusion
+ do
+ for s, ts in tags do
+ if ts.length <= 1 then continue
+ var losers = new Array[Token]
+ for t1 in ts do
+ for t2 in ts do
+ if t1 == t2 then continue
+ if retrotags[t1].length > retrotags[t2].length and retrotags[t1].has_all(retrotags[t2]) then
+ losers.add(t1)
+ break
+ end
+ end
+ end
+ for t in losers do
+ ts.remove(t)
+ retrotags[t].remove s
+ end
+ end
+ end
+
+ # Initialize a new automaton for the empty language
+ # one state, no accept, no transition
+ init empty
+ do
+ var state = new State
+ start = state
+ states.add state
+ end
+
+ # Initialize a new automaton for the empty-string language
+ # one state, is accept, no transition
+ init epsilon
+ do
+ var state = new State
+ start = state
+ accept.add state
+ states.add state
+ end
+
+ # Initialize a new automation for the language that accepts only a single symbol
+ # Two state, the second is accept, one transition on `symbol`
+ init atom(symbol: Int)
+ do
+ var s = new State
+ var a = new State
+ s.add_trans(a, symbol)
+ start = s
+ accept.add a
+ states.add s
+ states.add a
+ end
+
+ # Initialize a new automation for the language that accepts only a range of symbols
+ # Two state, the second is accept, one transition for `from` to `to`
+ init cla(from, to: Int)
+ do
+ var s = new State
+ var a = new State
+ for symbol in [from..to] do
+ s.add_trans(a, symbol)
+ end
+ start = s
+ accept.add a
+ states.add s
+ states.add a
+ end
+
+ # Contatenate `other` to `self`
+ # other is modified and invalidated.
+ fun concat(other: Automaton)
+ do
+ var s2 = other.start
+ for a1 in accept do
+ a1.add_trans(s2, null)
+ end
+ accept = other.accept
+ states.add_all other.states
+ end
+
+ # `self` become the alternation of `self` and `other`
+ # `other` is modified and invalidated.
+ fun alternate(other: Automaton)
+ do
+ var s = new State
+ var a = new State
+ s.add_trans(start, null)
+ for a1 in accept do
+ a1.add_trans(a, null)
+ end
+ s.add_trans(other.start, null)
+ for a2 in other.accept do
+ a2.add_trans(a, null)
+ accept.add(a2)
+ end
+
+ start = s
+ accept = [a]
+
+ states.add s
+ states.add a
+ states.add_all other.states
+ end
+
+ # Do the Kleene closure (*) on self
+ fun close
+ do
+ for a1 in accept do
+ a1.add_trans(start, null)
+ start.add_trans(a1, null)
+ end
+ end
+
+ # Do the + on self
+ fun plus
+ do
+ for a1 in accept do
+ a1.add_trans(start, null)
+ end
+ end
+
+ # Do the ? on self
+ fun optionnal
+ do
+ alternate(new Automaton.epsilon)
+ end
+
+ # Remove all transitions on a given symbol
+ fun minus_sym(symbol: Int)
+ do
+ for s in states do
+ for t in s.outs.to_a do
+ if t.symbol == symbol then t.delete
+ end
+ end
+ end
+
+ # Fully duplicate an automaton
+ fun dup: Automaton
+ do
+ var res = new Automaton.empty
+ var map = new HashMap[State, State]
+ map[start] = res.start
+ for s in states do
+ if s == start then continue
+ var s2 = new State
+ map[s] = s2
+ res.states.add(s2)
+ end
+ for s in accept do
+ res.accept.add map[s]
+ end
+ for s, ts in tags do for t in ts do
+ res.add_tag(map[s], t)
+ end
+ for s in states do
+ for t in s.outs do
+ map[s].add_trans(map[t.to], t.symbol)
+ end
+ end
+ return res
+ end
+
+ # Produce a graphvis file for the automaton
+ fun to_dot(filepath: String)
+ do
+ var f = new OFStream.open(filepath)
+ f.write("digraph g \{\n")
+
+ for s in states do
+ f.write("s{s.object_id}[")
+ #f.write("label=\"\",")
+ if accept.has(s) then
+ f.write("color=blue")
+ if tags.has_key(s) then
+ f.write(",label=\"")
+ for token in tags[s] do
+ f.write("{token.name.escape_to_c}\\n")
+ end
+ f.write("\"")
+ end
+ end
+ f.write("];\n")
+ var outs = new HashMap[State, Array[nullable Int]]
+ for t in s.outs do
+ var a
+ var s2 = t.to
+ var c = t.symbol
+ if outs.has_key(s2) then
+ a = outs[s2]
+ else
+ a = new Array[nullable Int]
+ outs[s2] = a
+ end
+ a.add(c)
+ end
+ for s2, a in outs do
+ var labe = ""
+ var lastc: nullable Int = null
+ var elip = 0
+ a.add(-1)
+ for c in a do
+ if c == null then
+ if not labe.is_empty then labe += "\n"
+ labe += "''"
+ else if lastc == c - 1 then
+ elip += 1
+ lastc = c
+ else
+ if elip == 1 then
+ assert lastc != null
+ labe += "\n{sym_to_s(lastc)}"
+ else if elip > 1 then
+ assert lastc != null
+ labe += " .. {sym_to_s(lastc)}"
+ end
+ if c == -1 then break
+ if not labe.is_empty then labe += "\n"
+ labe += sym_to_s(c)
+ lastc = c
+ end
+ end
+ f.write("s{s.object_id}->s{s2.object_id} [label=\"{labe.escape_to_c}\"];\n")
+ end
+ end
+ f.write("empty->s{start.object_id}; empty[label=\"\",shape=none];\n")
+
+ f.write("\}\n")
+ f.close
+ end
+
+ # Transform a symbol to a string
+ # Used by `to_dot`
+ private fun sym_to_s(symbol: nullable Int): String
+ do
+ if symbol == null then
+ return "''"
+ else if symbol <= 32 then
+ return "#{symbol}"
+ else
+ return symbol.ascii.to_s
+ end
+ end
+
+ # Transform a NFA to a DFA
+ # note: the DFA is not miminized
+ fun to_dfa: Automaton
+ do
+ var dfa = new Automaton.empty
+ var n2d = new ArrayMap[Set[State], State]
+ var seen = new ArraySet[Set[State]]
+ var ts = new HashSet[Int]
+ var st = eclosure([start])
+ var todo = [st]
+ n2d[st] = dfa.start
+ seen.add(st)
+ while not todo.is_empty do
+ var nfa_states = todo.pop
+ #print "* work on {nfa_states.inspect}={nfa_states} (remains {todo.length}/{seen.length})"
+ var dfa_state = n2d[nfa_states]
+ ts.clear
+ for s in nfa_states do
+ if accept.has(s) then
+ if tags.has_key(s) then
+ for t in tags[s] do
+ dfa.add_tag(dfa_state, t)
+ end
+ end
+ dfa.accept.add(dfa_state)
+ end
+ for t in s.outs do
+ var sym = t.symbol
+ if sym == null or ts.has(sym) then continue
+ ts.add(sym)
+ var nfa_dest = eclosure(trans(nfa_states, sym))
+ #print "{nfa_states} -> {sym} -> {nfa_dest}"
+ var dfa_dest
+ if seen.has(nfa_dest) then
+ #print "* reuse {nfa_dest.inspect}={nfa_dest}"
+ dfa_dest = n2d[nfa_dest]
+ else
+ #print "* new {nfa_dest.inspect}={nfa_dest}"
+ dfa_dest = new State
+ dfa.states.add(dfa_dest)
+ n2d[nfa_dest] = dfa_dest
+ todo.add(nfa_dest)
+ seen.add(nfa_dest)
+ end
+ dfa_state.add_trans(dfa_dest, sym)
+ end
+ end
+ end
+ return dfa
+ end
+
+ # epsilon-closure on a state of states
+ # used by `to_dfa`
+ private fun eclosure(states: Collection[State]): Set[State]
+ do
+ var res = new ArraySet[State]
+ res.add_all(states)
+ var todo = states.to_a
+ while not todo.is_empty do
+ var s = todo.pop
+ for t in s.outs do
+ if t.symbol != null then continue
+ var to = t.to
+ if res.has(to) then continue
+ res.add(to)
+ todo.add(to)
+ end
+ end
+ return res
+ end
+
+ # trans on a set of states
+ # Used by `to_dfa`
+ fun trans(states: Collection[State], symbol: Int): Set[State]
+ do
+ var res = new ArraySet[State]
+ for s in states do
+ for t in s.outs do
+ if t.symbol != symbol then continue
+ var to = t.to
+ if res.has(to) then continue
+ res.add(to)
+ end
+ end
+ return res
+ end
+
+ # Generate the Nit source code of the lexer
+ # `filepath` is the name of the ouptit file
+ # `parser` is the name of the parser module (used to import the token classes)
+ fun gen_to_nit(filepath: String, parser: nullable String)
+ do
+ var gen = new DFAGenerator(filepath, self, parser)
+ gen.gen_to_nit
+ end
+end
+
+# Generate the Nit source code of the lexer
+private class DFAGenerator
+ var filepath: String
+ var automaton: Automaton
+ var parser: nullable String
+
+ var out: OStream
+ init(filepath: String, automaton: Automaton, parser: nullable String) do
+ self.filepath = filepath
+ self.automaton = automaton
+ self.parser = parser
+ self.out = new OFStream.open(filepath)
+ end
+
+ fun add(s: String) do out.write(s)
+
+ fun gen_to_nit
+ do
+ var names = new HashMap[State, String]
+ var i = 0
+ for s in automaton.states do
+ names[s] = i.to_s
+ i += 1
+ end
+
+ add "# Lexer generated by nitcc"
+ add("import nitcc_runtime\n")
+
+ var p = parser
+ if p != null then add("import {p}\n")
+
+ add("class MyLexer\n")
+ add("\tsuper Lexer\n")
+ add("\tredef fun start_state do return dfastate_{names[automaton.start]}\n")
+ add("end\n")
+
+ add("redef class Object\n")
+ for s in automaton.states do
+ var n = names[s]
+ add("\tprivate fun dfastate_{n}: DFAState{n} do return once new DFAState{n}\n")
+ end
+ add("end\n")
+
+ add("class MyNToken\n")
+ add("\tsuper NToken\n")
+ add("end\n")
+
+ for s in automaton.states do
+ var n = names[s]
+ add("class DFAState{n}\n")
+ add("\tsuper DFAState\n")
+ if automaton.accept.has(s) then
+ var token
+ if automaton.tags.has_key(s) then
+ token = automaton.tags[s].first
+ else
+ token = null
+ end
+ add("\tredef fun is_accept do return true\n")
+ add("\tredef fun make_token(position, text) do\n")
+ if token != null and token.name == "Ignored" then
+ add("\t\treturn null\n")
+ else
+ if token == null then
+ add("\t\tvar t = new MyNToken\n")
+ else
+ add("\t\tvar t = new {token.cname}\n")
+ end
+ add("\t\tt.position = position\n")
+ add("\t\tt.text = text\n")
+ add("\t\treturn t\n")
+ end
+ add("\tend\n")
+ end
+ var trans = new ArrayMap[Int, State]
+ for t in s.outs do
+ var sym = t.symbol
+ assert sym != null
+ trans[sym] = t.to
+ end
+ if trans.is_empty then
+ # Do nothing, inherit the trans
+ else if trans.length == 1 then
+ var sym = trans.keys.first
+ var next = trans.values.first
+ add("\tredef fun trans(c) do\n")
+ add("\t\tif c.ascii == {sym} then return dfastate_{names[next]}\n")
+ add("\t\treturn null\n")
+ add("\tend\n")
+ else
+ add("\tredef fun trans(c) do\n")
+ for sym, next in trans do
+ add("\t\tif c.ascii == {sym} then return dfastate_{names[next]}\n")
+ end
+ add("\t\treturn null\n")
+ add("\tend\n")
+ end
+ add("end\n")
+ end
+
+ self.out.close
+ end
+end
+
+# A state in a finite automaton
+class State
+ # Outgoing transitions
+
+ var outs = new Array[Transition]
+ # Ingoing tyransitions
+ var ins = new Array[Transition]
+
+ # Add a transitions to `to` on `symbol` (null means epsilon)
+ fun add_trans(to: State, symbol: nullable Int): Transition
+ do
+ var t = new Transition(self, to, symbol)
+ outs.add(t)
+ to.ins.add(t)
+ return t
+ end
+end
+
+# A transition in a finite automaton
+class Transition
+ # The source state
+ var from: State
+ # The destination state
+ var to: State
+ # The symbol on the transition (null means epsilon)
+ var symbol: nullable Int
+
+ # Remove the transition from the automaton
+ # Detash from `from` and `to`
+ fun delete
+ do
+ from.outs.remove(self)
+ to.ins.remove(self)
+ end
+end
--- /dev/null
+# 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.
+
+# Example of a calculation used nitcc
+# see `calc.sablecc` for the grammar
+module calc
+
+# Reuse the test program to simplify the code
+import calc_test_parser
+
+# The main evaluator, as a visitiopn that stack values
+class Calulator
+ super Visitor
+
+ # The stack of values
+ var stack = new Array[Int]
+
+ redef fun visit(n) do n.accept_calculator(self)
+end
+
+redef class Node
+ # Default caculation is to visit the childrens
+ fun accept_calculator(v: Calulator) do visit_children(v)
+end
+
+redef class Nint
+ redef fun accept_calculator(v) do v.stack.push(text.to_i)
+end
+
+redef class Ne_add
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(v.stack.pop+v.stack.pop)
+ end
+end
+
+redef class Ne_sub
+ redef fun accept_calculator(v) do
+ super
+ var n1 = v.stack.pop
+ v.stack.push(v.stack.pop-n1)
+ end
+end
+
+redef class Ne_neg
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(-v.stack.pop)
+ end
+end
+
+redef class Ne_mul
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(v.stack.pop*v.stack.pop)
+ end
+end
+
+redef class Ne_div
+ redef fun accept_calculator(v) do
+ super
+ var n1 = v.stack.pop
+ v.stack.push(v.stack.pop/n1)
+ end
+end
+
+var t = new MyTest
+var n = t.main
+var v = new Calulator
+v.enter_visit(n)
+print v.stack.pop
--- /dev/null
+/* A simple calculator, see calc.nit */
+Grammar calc;
+
+Lexer
+d = '0'..'9';
+int = d+;
+blank = (' '|'\n')+;
+
+Parser
+Ignored blank;
+e =
+ {add:} e '+' f |
+ {sub:} e '-' f |
+ f {->f};
+f {->e} =
+ {mul:} f '*' a |
+ {div:} f '/' a |
+ a {->a};
+a {->e} =
+ {int:} int |
+ {par:} '(' e ')' |
+ {neg:} '-' a ;
--- /dev/null
+Grammar json;
+
+Lexer
+
+number = int frac? exp;
+int = '-'? d+;
+d = '0'..'9';
+frac = '.' d+;
+exp = e d+;
+e = ('e'|'E') ('+'|'-')?;
+
+string = '"' (ch-'\\'-'"' | '\\'ch)+ '"';
+ch = ' '..'~';
+
+blank = (' '|'\n'|'\t')+;
+
+Parser
+Ignored blank;
+
+value =
+ {object:} '{' members? '}' |
+ {array:} '[' elements? ']' |
+ number |
+ string |
+ 'true' |
+ 'false' |
+ 'null' ;
+
+members = members ',' pair | pair ;
+pair = string ':' value ;
+elements = elements ',' value | value ;
--- /dev/null
+a=1+2;
+a=a*3;
+print(a-4);
+println();
+
+f = 1;
+fn = 0;
+while (f < 500) {
+ t = f;
+ f = f + fn;
+ fn = t;
+}
+print("le plus petit nombre de Fibbonacci > 500 est ");
+print(f);
+println();
+
+print("Plus ou moins ? (hint la reponse est 42)");
+println();
+goal = 42;
+lu = -1;
+while (lu != goal) {
+ print("Votre choix: ");
+ lu = read();
+ if (lu > goal) { print("c'est plus petit."); println(); }
+ if (lu < goal) { print("c'est plus grand."); println(); }
+}
+print("C'est bon.");
+println();
--- /dev/null
+import minilang_test_parser
+
+class Interpretor
+ super Visitor
+ var stack = new Array[Int]
+ var bstack = new Array[Bool]
+ var vars = new HashMap[String, Int]
+
+ redef fun visit(n) do n.accept_calculator(self)
+end
+
+redef class Node
+ fun accept_calculator(v: Interpretor) do visit_children(v)
+end
+
+redef class Nint
+ redef fun accept_calculator(v) do v.stack.push(text.to_i)
+end
+
+redef class Ns_assign
+ redef fun accept_calculator(v) do
+ super
+ v.vars[n_id.text] = v.stack.pop
+ end
+end
+
+redef class Ns_print
+ redef fun accept_calculator(v) do
+ super
+ printn v.stack.pop
+ end
+end
+redef class Ns_print_str
+ redef fun accept_calculator(v) do
+ var text = n_str.text
+ text = text.substring(1, text.length-2)
+ printn text
+ end
+end
+redef class Ns_println
+ redef fun accept_calculator(v) do
+ print ""
+ end
+end
+redef class Ns_if
+ redef fun accept_calculator(v) do
+ v.enter_visit(n_c)
+ if v.bstack.pop then
+ v.enter_visit(n_then)
+ else
+ var nelse = n_else
+ if nelse != null then v.enter_visit(nelse)
+ end
+ end
+end
+redef class Ns_while
+ redef fun accept_calculator(v) do
+ loop
+ v.enter_visit(n_c)
+ if not v.bstack.pop then break
+ v.enter_visit(n_stmts)
+ end
+ end
+end
+
+
+redef class Nc_and
+ redef fun accept_calculator(v) do
+ super
+ var b1 = v.bstack.pop
+ var b2 = v.bstack.pop
+ v.bstack.push(b1 and b2)
+ end
+end
+
+redef class Nc_or
+ redef fun accept_calculator(v) do
+ super
+ var b1 = v.bstack.pop
+ var b2 = v.bstack.pop
+ v.bstack.push(b1 or b2)
+ end
+end
+
+redef class Nc_not
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(not v.bstack.pop)
+ end
+end
+
+redef class Nc_eq
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop == v.stack.pop)
+ end
+end
+
+redef class Nc_ne
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop != v.stack.pop)
+ end
+end
+
+redef class Nc_lt
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop > v.stack.pop)
+ end
+end
+
+redef class Nc_le
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop >= v.stack.pop)
+ end
+end
+
+redef class Nc_gt
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop < v.stack.pop)
+ end
+end
+
+redef class Nc_ge
+ redef fun accept_calculator(v) do
+ super
+ v.bstack.push(v.stack.pop <= v.stack.pop)
+ end
+end
+
+redef class Ne_add
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(v.stack.pop+v.stack.pop)
+ end
+end
+redef class Ne_sub
+ redef fun accept_calculator(v) do
+ super
+ var n1 = v.stack.pop
+ v.stack.push(v.stack.pop-n1)
+ end
+end
+redef class Ne_neg
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(-v.stack.pop)
+ end
+end
+redef class Ne_mul
+ redef fun accept_calculator(v) do
+ super
+ v.stack.push(v.stack.pop*v.stack.pop)
+ end
+end
+redef class Ne_div
+ redef fun accept_calculator(v) do
+ super
+ var n1 = v.stack.pop
+ v.stack.push(v.stack.pop/n1)
+ end
+end
+redef class Ne_var
+ redef fun accept_calculator(v) do
+ v.stack.push v.vars[n_id.text]
+ end
+end
+redef class Ne_read
+ redef fun accept_calculator(v) do
+ var t = gets
+ v.stack.push(t.to_i)
+ end
+end
+
+var t = new MyTest
+var n = t.main
+
+var v = new Interpretor
+v.enter_visit(n)
--- /dev/null
+/* Grammar of a mini procedural programming language */
+Grammar minilang;
+
+Lexer
+l = 'a'..'z'|'A'..'Z'|'_';
+d = '0'..'9';
+id = l (l|d)*;
+int = d+;
+str = '"' (' '..'~' - '"')* '"';
+blank = ' ' | '\n' | '\t';
+
+Parser
+Ignored blank;
+
+prog = s*;
+
+s =
+ {assign:} id '=' e ';' |
+ {print:} 'print' '(' e ')' ';' |
+ {print_str:} 'print' '(' str ')' ';' |
+ {println:} 'println' '(' ')' ';' |
+ {while:} 'while' '(' c ')' stmts |
+ {if:} 'if' '(' c ')' [then:]stmts else? ;
+
+stmts = '{' s* '}' ;
+
+else = 'else' stmts;
+
+e =
+ {add:} [left:]e '+' [right:]e2 |
+ {sub:} [left:]e '-' [right:]e2 |
+ e2 {->e2};
+e2 {->e} =
+ {mul:} [left:]e2 '*' [right:]e3 |
+ {div:} [left:]e2 '/' [right:]e3 |
+ e3 {->e3};
+e3 {->e} =
+ {neg:} '-' e3 |
+ {lit:} int |
+ {par:} '(' e ')' |
+ {var:} id |
+ {read:} 'read' '(' ')' ;
+
+c =
+ {or:} [left:]c '||' [right:]c2 |
+ c2 {->c2};
+c2 {->c} =
+ {and:} [left:]c2 '&&' [right:]c3 |
+ c3 {->c3};
+c3 {->c} =
+ {not:} 'not' [c:]c3 |
+ {eq:} [left:]e '=' [right:]e |
+ {ne:} [left:]e '!=' [right:]e |
+ {lt:} [left:]e '<' [right:]e |
+ {le:} [left:]e '<=' [right:]e |
+ {gt:} [left:]e '>' [right:]e |
+ {ge:} [left:]e '>=' [right:]e ;
--- /dev/null
+set -e
+set -x
+../nitcc minilang.sablecc
+../../../bin/nitc minilang.nit -v -I ..
+./minilang minilang.minilang
--- /dev/null
+# 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.
+
+# A gramar describing a language
+class Gram
+ # The productions (non-terminal) of the conctete grammar
+ var prods = new Array[Production]
+
+ # The additionnal abstract productions of the grammar
+ # TODO clean AST
+ var ast_prods = new Array[Production]
+
+ # The tokens (terminals) of the grammar
+ var tokens = new Array[Token]
+
+ # Dump of the concrete grammar and the transformations
+ fun pretty: String
+ do
+ var res = new Buffer
+ for p in prods do
+ if p.spe != null then
+ res.append("{p.name} \{-> {p.spe.name}\}=\n")
+ else
+ res.append("{p.name} =\n")
+ end
+ var last = p.alts.last
+ for a in p.alts do
+ res.append("\t\{{a.name}:\} {a.elems.join(" ")}")
+ if a.codes == null then a.make_codes
+ res.append("\n\t\t\{-> {a.codes.join(", ")}\}")
+ if a == last then res.append(" ;\n") else res.append(" |\n")
+ end
+ if p.is_nullable then res.append "\t// is nullable\n"
+ if not p.firsts.is_empty then res.append "\t// firsts: {p.firsts.join(" ")}\n"
+ if not p.afters.is_empty then res.append "\t// afters: {p.afters.join(" ")}\n"
+ end
+ return res.to_s
+ end
+
+ # Inline (ie. remove from the conctete grammar) some production
+ # REQUIRE: no circular production in `prods`
+ fun inline(prods: Collection[Production])
+ do
+ for p in self.prods do
+ for a in p.alts.to_a do
+ var to_inline = false
+ for e in a.elems do
+ if e isa Production and prods.has(e) then to_inline = true
+ end
+ if not to_inline then continue
+
+ if a.codes == null then a.make_codes
+
+ var a0 = new Alternative(p, a.name, new Array[Element])
+ a0.trans = true
+ a0.codes = new Array[Code]
+ var pool = [a0]
+ var pool2 = new Array[Alternative]
+ for e in a.elems do
+ if not e isa Production or not prods.has(e) then
+ for x in pool do
+ x.elems.add(e)
+ x.codes.add(new CodePop)
+ end
+ continue
+ end
+ if p == e then
+ print "Circular inlining on {p} :: {a}"
+ abort
+ end
+ pool2.clear
+ for a2 in e.alts do
+ if a2.codes == null then a2.make_codes
+ for x in pool do
+ var name = a.name + "_" + pool2.length.to_s
+ var na = new Alternative(p, name, new Array[Element])
+ na.trans = true
+ pool2.add(na)
+ na.elems.add_all(x.elems)
+ na.elems.add_all(a2.elems)
+ na.codes = new Array[Code]
+ na.codes.add_all(x.codes.as(not null))
+ na.codes.add_all(a2.codes.as(not null))
+ end
+ end
+ var tmp = pool
+ pool = pool2
+ pool2 = tmp
+ end
+ for x in pool do
+ x.codes.add(a.codes.last)
+ end
+ p.ast_alts.add(a)
+ p.alts.remove(a)
+ p.alts.add_all(pool)
+ end
+ end
+ for p in prods do
+ self.prods.remove(p)
+ self.ast_prods.add(p)
+ end
+ end
+
+ # Compute a LR automaton
+ fun lr0: LRAutomaton
+ do
+ analyse
+
+ var start = new Production("_start")
+ start.accept = true
+ var eof = new Token("Eof")
+ tokens.add(eof)
+ start.new_alt("Start", self.prods.first, eof)
+ prods.add(start)
+ var first = new LRState("Start")
+ 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)
+ state.analysis
+
+ 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 name
+ if state == automaton.states.first then
+ name = e.to_s
+ else
+ name = "{state.name} {e}"
+ end
+ var next = new LRState(name)
+ nexts[e] = next
+ next.add(i.avance)
+ end
+ end
+
+ for e, next in nexts do
+
+ #print "#states: {seen.length}"
+
+ var new_state = true
+ for n in seen do
+ if n == next then
+ for i in next.items do
+ if n.add(i) then n.extends(i)
+ end
+ next = n
+ new_state = false
+ break
+ end
+ end
+
+ if new_state then
+ next.number = seen.length
+ assert not seen.has(next)
+ seen.add(next)
+ todo.add(next)
+ end
+
+ var t = new LRTransition(state, next, e)
+ state.outs.add t
+ next.ins.add t
+ end
+ end
+ return automaton
+ end
+
+ # Compute `nullables`, `firsts` and `afters` of productions
+ fun analyse
+ do
+ loop
+ var changed = false
+ for p in prods do
+ if p.is_nullable then continue
+ for a in p.alts do
+ var nullabl = true
+ for e in a.elems do
+ if e isa Token then
+ nullabl = false
+ break
+ else if e isa Production then
+ if not e.is_nullable then nullabl = false
+ break
+ else
+ abort
+ end
+ end
+ if nullabl then
+ p.is_nullable = true
+ changed = true
+ end
+ end
+ end
+ if not changed then break
+ end
+
+ loop
+ var changed = false
+ for p in prods do
+ var fs = p.firsts
+ for a in p.alts do
+ for e in a.elems do
+ if e isa Token then
+ if try_add(fs, e) then changed = true
+ break
+ else if e isa Production then
+ for t in e.firsts do
+ if try_add(fs, t) then changed = true
+ end
+ if not e.is_nullable then break
+ else
+ abort
+ end
+ end
+ end
+ end
+ if not changed then break
+ end
+
+ loop
+ var changed = false
+ for p1 in prods do
+ for a in p1.alts do
+ var p0: nullable Production = null
+ for e in a.elems do
+ var p = p0
+ if e isa Production then p0 = e else p0 = null
+ if p == null then continue
+
+ var afs = p.afters
+
+ if e isa Token then
+ if try_add(afs, e) then changed = true
+ else if e isa Production then
+ for t in e.firsts do
+ if try_add(afs, t) then changed = true
+ end
+ if e.is_nullable then
+ for t in e.afters do
+ if try_add(afs, t) then changed = true
+ end
+ end
+ else
+ abort
+ end
+ end
+ if p0 != null then
+ var afs = p0.afters
+ for t in p1.afters do
+ if try_add(afs, t) then changed = true
+ end
+ end
+ end
+ end
+ if not changed then break
+ end
+ end
+
+ # used by `analyse`
+ private fun try_add(set: Set[Token], t: Token): Bool
+ do
+ var res = not set.has(t)
+ if res then set.add(t)
+ return res
+ end
+
+end
+
+# A production of a grammar
+class Production
+ super Element
+ # The alternative of the production
+ var alts = new Array[Alternative]
+
+ # Additionnal alternatives in the AST
+ var ast_alts = new Array[Alternative]
+
+ # Is self the accept production
+ var accept = false
+
+ # Is self transformed to a other production for the AST
+ # FIXME: cleaup AST
+ var spe: nullable Production writable = null
+
+ # Is self contains only a single alternative (then no need for a abstract production class in the AST)
+ # FIXME cleanup AST
+ var altone writable = false
+
+ # The cname of the class in the AST
+ # FIXME: cleanup AST
+ redef fun acname do
+ if spe != null then return spe.acname
+ return super
+ end
+
+ # Is the production nullable
+ var is_nullable = false
+ # The first tokens of the production
+ var firsts = new HashSet[Token]
+ # The tokens that may follows the production (as in SLR)
+ var afters = new HashSet[Token]
+
+
+ # Crate a new empty alternative
+ fun new_alt0(name: String): Alternative
+ do
+ var a = new Alternative(self, name, new Array[Element])
+ alts.add a
+ return a
+ end
+
+ # Crate a new alternative with some elements
+ fun new_alt(name: String, elems: Element...): Alternative
+ do
+ var a = new Alternative(self, name, elems)
+ alts.add a
+ return a
+ end
+
+ # Crate a new alternative with some elements
+ fun new_alt2(name: String, elems: Array[Element]): Alternative
+ do
+ var a = new Alternative(self, name, elems)
+ alts.add a
+ return a
+ end
+
+ # Return a set of items for the production
+ fun start_state: Array[Item]
+ do
+ var res = new Array[Item]
+ for a in alts do
+ res.add new Item(a, 0)
+ 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
+class Alternative
+ # The production
+ var prod: Production
+
+ # The name of the alternative
+ var name: String writable
+
+ # The elements of the alternative
+ var elems: Array[Element]
+
+ # The name of the elements
+ var elems_names = new Array[nullable String]
+
+ # Return a name for a given element (or a number if unamed)
+ fun elemname(i: Int): String
+ do
+ if i >= elems_names.length then return "{i}"
+ var n = elems_names[i]
+ if n == null then return "{i}"
+ return n
+ end
+
+ redef fun to_s do return "{prod.name}::{name}={elems.join(" ")}"
+
+ # A mangled name
+ fun cname: String do
+ return "N{name.to_cmangle}"
+ end
+
+ # The code for the reduction
+ var codes: nullable Array[Code] writable = null
+
+ # Is the alternative transformed (ie not in the AST)
+ var trans writable = false
+
+ # Imitialize codes with the elements
+ fun make_codes
+ do
+ if codes != null then return
+ var codes = new Array[Code]
+ self.codes = codes
+ for e in elems do
+ codes.add(new CodePop)
+ end
+ codes.add(new CodeNew(self))
+ end
+end
+
+# A step in the construction of the AST. used to modelize transformations
+interface Code
+end
+# Get a element from the stack
+class CodePop
+ super Code
+ redef fun to_s do return "pop"
+end
+# Allocate a new AST node for an alternative using the correct number of poped elements
+class CodeNew
+ super Code
+ var alt: Alternative
+ redef fun to_s do return "New {alt.name}/{alt.elems.length}"
+end
+# Get null
+class CodeNull
+ super Code
+ redef fun to_s do return "null"
+end
+
+# Elements of an alternative.
+# Either a `Production` or a `Token`
+abstract class Element
+ # The name of the element
+ var name: String
+ redef fun to_s do return name
+
+ private var acname_cache: nullable String = null
+
+ # The mangled name of the element
+ fun cname: String do return "N{name.to_cmangle}"
+
+ # the name of the class in the AST
+ fun acname: String do
+ var res = acname_cache
+ if res == null then
+ res = "N{to_s.to_cmangle}"
+ acname_cache = res
+ end
+ return res
+ end
+ fun acname=(s: String) do acname_cache = s
+end
+
+# A terminal element
+class Token
+ super Element
+ # States of the LR automatio that shift on self
+ var shifts = new ArraySet[LRState]
+ # States of the LR automatio 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]
+
+ # 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 "s{s.number} {s.name}\n"
+ res.add "\tCORE\n"
+ for i in s.core do
+ res.add "\t\t{i}\n"
+ 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"
+ end
+ 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 LALR\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.to_s
+ end
+
+ # Generate a graphviz file of the automaton
+ fun to_dot(path: String)
+ do
+ var f = new OFStream.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.number} {s.name.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)
+ do
+ var gen = new Generator
+ gen.gen_to_nit(self)
+ var f = new OFStream.open(filepath)
+ for s in gen.out do
+ f.write(s)
+ f.write("\n")
+ end
+ f.close
+ end
+end
+
+redef class String
+ # escape string used in labels for graphviz
+ fun escape_to_dot: String
+ do
+ return escape_more_to_c("|\{\}")
+ end
+end
+
+private class Generator
+ var out = new Array[String]
+ fun add(s: String) do out.add(s)
+ fun gen_to_nit(autom: LRAutomaton)
+ do
+ var states = autom.states
+ var gram = autom.grammar
+
+ add "# Parser generated by nitcc"
+ add "import nitcc_runtime"
+
+ add "class MyParser"
+ add "\tsuper Parser"
+ add "\tredef fun start_state do return state_{states.first.cname}"
+ add "end"
+
+ add "redef class Object"
+ for s in states do
+ add "\tprivate fun state_{s.cname}: LRState{s.cname} do return once new LRState{s.cname}"
+ end
+ for p in gram.prods do
+ add "\tprivate fun goto_{p.cname}: Goto_{p.cname} do return once new Goto_{p.cname}"
+ end
+ add "end"
+
+ add "redef class NToken"
+ for s in states do
+ if not s.need_guard then continue
+ add "\t# guarded action for state {s.name}"
+ add "\t# {s.shifts.length} shift(s) and {s.reduces.length} reduce(s)"
+ add "\tprivate fun action_s{s.cname}(parser: Parser) do"
+ if s.reduces.length != 1 then
+ add "\t\tparser.parse_error"
+ else
+ 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.cname}(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.cname}(parser) do"
+ 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.cname}(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.cname}(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.name}"
+ add "private class LRState{s.cname}"
+ add "\tsuper LRState"
+
+ add "\tredef fun to_s do return \"{s.name.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.cname}(parser)"
+ else if s.reduces.length == 1 then
+ 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.cname}(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.cname})"
+
+ end
+
+ fun gen_goto_to_nit(s: LRState, p: Production)
+ do
+ var dest = s.trans(p)
+ add "\t\tparser.push(state_{dest.cname})"
+ 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
+
+ if alt.name.has_suffix("+_more") then
+ add "\t\tvar prod = n0"
+ add "\t\tn0.items.add(n1)"
+ else if alt.name.has_suffix("+_one") then
+ add "\t\tvar prod = new {alt.prod.acname}"
+ add "\t\tprod.items.add(n0)"
+ else
+ 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}")
+ end
+ end
+ assert st.length == 1
+ add "\t\tvar prod = {st.first}"
+ end
+
+ 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
+ # name of the automaton (short part from the start)
+ var name: String
+
+ # malglen name
+ fun cname: String do return name.to_cmangle
+
+ # number
+ var number: Int = -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 items.join(" ; ")
+
+ # Add and item in the core
+ fun add(i: Item): Bool
+ do
+ #if items.has(i) then return
+
+ #print "add {i} in {inspect}={self}"
+ var found = false
+ for i2 in items do
+ if i.alt == i2.alt and i.pos == i2.pos then
+ if i2.future.has_all(i.future) then return false
+ i2.future.add_all(i.future)
+ found = true
+
+ break
+ end
+ end
+ if not found then
+ items.add(i)
+ if i.pos > 0 or i.alt.prod.accept then core.add(i)
+ end
+ return true
+ end
+
+ # Recusively 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
+ var la = i.lookahead
+ for i2 in e.start_state do
+ i2.future.add_all(la)
+ if add(i2) then extends(i2)
+ end
+ 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, Array[Item]]
+ # Shitfs guarded by tokens
+ var guarded_shift = new HashMap[Token, Array[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
+ for i in items.to_a do
+ extends(i)
+ end
+
+ for i in items do
+ var n = i.next
+ if n == null then
+ reduces.add(i.alt)
+ #for t in i.alt.prod.afters do
+ for t in i.future do
+ t.reduces.add(self)
+ if guarded_reduce.has_key(t) then
+ guarded_reduce[t].add(i)
+ else
+ guarded_reduce[t] = [i]
+ end
+ end
+ else if n isa Token then
+ shifts.add(n)
+ n.shifts.add(self)
+ if guarded_shift.has_key(n) then
+ guarded_shift[n].add(i)
+ else
+ guarded_shift[n] = [i]
+ end
+ else if n isa Production then
+ gotos.add(n)
+ n.gotos.add(self)
+ else
+ abort
+ end
+ end
+ for t, a in guarded_reduce do
+ if a.length > 1 then
+ print "REDUCE/REDUCE Conflict on state {self.number} {self.name} for token {t}:"
+ for i in a do print "\t{i}"
+ else if guarded_shift.has_key(t) then
+ print "SHIFT/REDUCE Conflict on state {self.number} {self.name} for token {t}:"
+ for i in a do print "\t{i}"
+ for i in guarded_shift[t] do print "\t{i}"
+ end
+ end
+ end
+end
+
+# A transition in a LR automaton
+class LRTransition
+ # The origin state
+ var from: LRState
+ # The testination state
+ var to: LRState
+ # The element labeling the transition
+ var elem: Element
+end
+
+# A alternative with a cursor (dot) and possibly a future
+class Item
+ # The alternative
+ var alt: Alternative
+ # The dot index (0 means before the first element)
+ var pos: Int
+ # The possible future
+ var future = new ArraySet[Token]
+
+ redef fun ==(o) do return o isa Item and alt == o.alt and pos == o.pos
+ redef fun hash do return alt.hash + pos
+
+ redef fun to_s
+ do
+ var b = new Buffer
+ b.append("{alt.prod.name}::{alt.name}=")
+ for i in [0..alt.elems.length[
+ do
+ 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 not future.is_empty then
+ b.append("/\{")
+ b.append(future.join(" "))
+ b.append("\}")
+ end
+ return b.to_s
+ end
+
+ # The element thatr follow the dot, null if the fdot is at the end
+ fun next: nullable Element
+ do
+ if pos >= alt.elems.length then return null
+ return alt.elems[pos]
+ end
+
+ # SLR loohahead
+ fun lookahead: Set[Token]
+ do
+ var res = new HashSet[Token]
+ var p = pos + 1
+ while p < alt.elems.length do
+ var e = alt.elems[p]
+ if e isa Token then
+ res.add(e)
+ break
+ else if e isa Production then
+ res.add_all(e.firsts)
+ if not e.is_nullable then break
+ end
+ p += 1
+ end
+ if p >= alt.elems.length then
+ res.add_all(future)
+ end
+ return res
+ end
+
+ # The item that advanced once
+ fun avance: Item
+ do
+ var res = new Item(alt, pos+1)
+ res.future.add_all(future)
+ return res
+ end
+end
--- /dev/null
+set -e
+set -x
+
+NITC=../../bin/nitg
+
+echo "**************************"
+
+$NITC nitcc_parser_gen.nit -v
+./nitcc_parser_gen
+
+echo "**************************"
+
+$NITC nitcc.nit -v
+cp nitcc nitcc0
+
+echo "**************************"
+
+./nitcc nitcc.sablecc
+$NITC nitcc.nit
+cp nitcc nitcc1
+
+echo "**************************"
+
+./nitcc nitcc.sablecc
+$NITC nitcc.nit
+
+echo "**************************"
+
+cd examples
+../nitcc calc.sablecc
+../$NITC calc.nit -v -I ..
+./calc -e "1+2*3-40/5+9------1"
+
--- /dev/null
+# 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.
+
+# nitcc, a parser and lexer generator for Nit
+module nitcc
+
+import nitcc_semantic
+
+# Load the grammar file
+
+if args.is_empty then
+ print "usage: nitcc <file> | -"
+ exit 1
+end
+var fi = args.first
+
+var text
+if fi != "-" then
+ var f = new IFStream.open(fi)
+ text = f.read_all
+ f.close
+else
+ text = stdin.read_all
+end
+
+# Parse the grammar file
+
+var l = new MyLexer(text)
+var ts = l.lex
+
+var p = new MyParser
+p.tokens.add_all ts
+
+var node = p.parse
+
+if not node isa NProd then
+ print node
+ exit 1
+ abort
+end
+
+var name = node.children.first.as(Ngrammar).children[1].as(Nid).text
+
+print "Grammar {name} (see {name}.gram.dot))"
+node.to_dot("{name}.gram.dot")
+
+# Semantic analysis
+
+var v2 = new CollectNameVisitor
+v2.start(node)
+var gram = v2.gram
+
+if gram.prods.is_empty then
+ print "Error: grammar with no production"
+ exit(1)
+end
+
+# Generate the LR automaton
+
+var lr = gram.lr0
+
+var conflitcs = new ArraySet[Production]
+for s in lr.states do for t, a in s.guarded_reduce do if a.length > 1 or s.guarded_shift.has_key(t) then
+ for i in a do conflitcs.add(i.alt.prod)
+end
+
+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
+for prod in gram.prods do nbalts += prod.alts.length
+print "Concrete grammar: {gram.prods.length} productions, {nbalts} alternatives (see {name}.concrete_grammar.txt)"
+
+var pretty = gram.pretty
+var f = new OFStream.open("{name}.concrete_grammar.txt")
+f.write "// Concrete grammar of {name}\n"
+f.write pretty
+f.close
+
+print "LR automaton: {lr.states.length} states (see {name}.lr.dot and {name}.lr.txt)"
+lr.to_dot("{name}.lr.dot")
+pretty = lr.pretty
+f = new OFStream.open("{name}.lr.txt")
+f.write "// LR automaton of {name}\n"
+f.write pretty
+f.close
+
+# NFA and DFA
+
+var nfa = v2.nfa
+print "NFA automaton: {nfa.states.length} states (see {name}.nfa.dot)"
+nfa.to_dot("{name}.nfa.dot")
+
+var dfa = nfa.to_dfa
+if dfa.tags.has_key(dfa.start) then
+ print "ERROR: Empty tokens {dfa.tags[dfa.start].join(" ")}"
+end
+dfa.solve_token_inclusion
+for s, tks in dfa.tags do
+ if tks.length <= 1 then continue
+ print "ERROR: Conflicting tokens: {tks.join(" ")}"
+end
+print "DFA automaton: {dfa.states.length} states (see {name}.dfa.dot)"
+dfa.to_dot("{name}.dfa.dot")
+
+# Generate Nit code
+
+print "Generate {name}_lexer.nit {name}_parser.nit {name}_test_parser.nit"
+dfa.gen_to_nit("{name}_lexer.nit", "{name}_parser")
+lr.gen_to_nit("{name}_parser.nit")
+
+f = new OFStream.open("{name}_test_parser.nit")
+f.write """# Generated by nitcc for the language {{{name}}}
+import nitcc_runtime
+import {{{name}}}_lexer
+import {{{name}}}_parser
+class MyTest
+ super TestParser
+ redef fun name do return \"{{{name}}}\"
+ redef fun new_lexer(text) do return new MyLexer(text)
+ redef fun new_parser do return new MyParser
+end
+var t = new MyTest
+t.main
+"""
+f.close
--- /dev/null
+// The grammar of nitcc (subset of sablecc4)
+Grammar nitcc;
+
+Lexer
+
+// Identifier, used for production, expressions, alternatives, etc.
+id = ('a'..'z')('a'..'z'|'0'..'9'|'_')*;
+
+// A printable character (inside strings)
+ch = ' ' .. '~';
+
+// Literal strings
+str = '\'' (ch-'\\'-'\''|'\\'ch)* '\'';
+
+// A char by decimal ascii
+ch_dec = '#' ('0'..'9')+ ;
+
+// A single-line comment
+comment = '//' ch* '\n'?;
+
+any = '\t'..'~';
+not_star = any - '*';
+not_star_not_slash = not_star - '/';
+
+ccomment = '/*' not_star* ('*' (not_star_not_slash not_star*)?)* '*/';
+
+unknown_keyword = 'A..Z'('A'..'Z'|'a'..'z'|'0'..'9'|'_')*;
+
+// Igndored stufs
+blank = '\n' | '\t' | ' ' | comment | ccomment;
+
+Parser
+
+Ignored blank;
+
+grammar = 'Grammar' id ';' lexer_part? parser_part? reject?;
+
+reject = 'XXX' unknown_keyword ';';
+
+lexer_part = 'Lexer' expr*;
+
+expr = id '=' re ';';
+
+// No priority (yet) so just decompose
+re =
+ {alter:} re '|' re1 |
+ re1 ;
+
+re1 {-> re} =
+ {minus:} re1 '-' re2 |
+ re2 ;
+
+re2 {-> re} =
+ {conc:} re2 re3 |
+ re3 ;
+
+re3 {-> re} =
+ {ques:} re3 '?' |
+ {star:} re3 '*' |
+ {plus:} re3 '+' |
+ {id:} id |
+ {str:} str |
+ {par:} '(' re ')' |
+ {class:} str '.' '.' str |
+ {any:} 'Any' |
+ {ch_dec:} ch_dec ;
+
+
+parser_part = 'Parser' ign? prod*;
+
+ign = 'Ignored' id ';' ;
+
+prod = id ptrans? '=' alts ';';
+
+ptrans = '{' '->' id '}';
+atrans = '{' '->' id '}';
+
+alts =
+ {more:} alts '|' alt |
+ {one:} alt ;
+
+alt = altid? nelem* atrans?;
+
+altid = '{' id ':' '}' | '{' id '}';
+
+nelem = elem | elemid elem;
+
+elemid = '[' id ':' ']' | '[' id ']' ':';
+
+elem =
+ {id:} id |
+ {str:} str |
+ {star:} elem '*' |
+ {ques:} elem '?' |
+ {plus:} elem '+' ;
--- /dev/null
+# 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.
+
+# Ad-hoc hand-writen lexer for nitcc
+# This avoid to commit (and relyon ) a generated lexer
+#
+module nitcc_lexer0
+
+# Required for the tokens definitions
+import nitcc_parser
+
+# Hand-writen lexer of nitcc
+# Used only for the boostrap of the tool.
+class MyLexer
+ var text: String
+
+ var iter: Iterator[Char] = "".iterator
+ var pos = 0
+
+ var tokens = new Array[NToken]
+
+ fun lex: Array[NToken]
+ do
+ iter = text.iterator
+ while iter.is_ok do
+ trim
+ if not iter.is_ok then break
+ var c = iter.item
+ iter.next
+ pos += 1
+ if c == '*' then
+ tokens.add new Nstar
+ else if c == '?' then
+ tokens.add new Nques
+ else if c == '+' then
+ tokens.add new Nplus
+ else if c == '-' then
+ if iter.item == '>' then
+ iter.next
+ tokens.add new Narrow
+ else
+ tokens.add new Nminus
+ end
+ else if c == '(' then
+ tokens.add new Nopar
+ else if c == ')' then
+ tokens.add new Ncpar
+ else if c == '{' then
+ tokens.add new Nocur
+ else if c == '}' then
+ tokens.add new Nccur
+ else if c == '|' then
+ tokens.add new Npipe
+ else if c == ':' then
+ tokens.add new Ncolo
+ else if c == ';' then
+ tokens.add new Nsemi
+ else if c == '.' then
+ tokens.add new Ndot
+ else if c == '=' then
+ tokens.add new Neq
+ else if c == '\'' then
+ str
+ else if c >= 'a' and c <= 'z' then
+ id(c)
+ else if c >= 'A' and c <= 'Z' then
+ kw(c)
+ else if c == '/' and iter.is_ok and iter.item == '/' then
+ while iter.is_ok and iter.item != '\n' do iter.next
+ else
+ error(c)
+ end
+ end
+ tokens.add new NEof
+ return tokens
+ end
+
+ fun error(c: Char)
+ do
+ print "pos {pos}: Lexer error on '{c}'."
+ abort
+ end
+
+ fun str
+ do
+ var b = new Buffer
+ b.add('\'')
+ while iter.is_ok do
+ var c = iter.item
+ iter.next
+ if c == '\\' then
+ if not iter.is_ok then
+ error(c)
+ end
+ b.add(c)
+ c = iter.item
+ iter.next
+ else if c == '\'' then
+ b.add(c)
+ var token = new Nstr
+ token.text = b.to_s
+ tokens.add token
+ return
+ end
+ b.add c
+ end
+ error('\n')
+ abort
+ end
+
+ fun id(c: Char)
+ do
+ var b = new Buffer
+ b.add c
+ while iter.is_ok do
+ c = iter.item
+ if c != '_' and (c<'a' or c >'z') and (c<'0' or c>'9') then
+ break
+ end
+ b.add c
+ iter.next
+ end
+ var token = new Nid
+ token.text = b.to_s
+ tokens.add token
+ end
+
+ fun kw(c: Char)
+ do
+ var b = new Buffer
+ b.add c
+ while iter.is_ok do
+ c = iter.item
+ if c != '_' and (c<'a' or c >'z') and (c<'0' or c>'9') then
+ break
+ end
+ b.add c
+ iter.next
+ end
+ var token = new Nkw
+ token.text = b.to_s
+ tokens.add token
+ end
+
+ fun trim
+ do
+ while iter.is_ok and iter.item <= ' ' do
+ iter.next
+ pos += 1
+ end
+ end
+end
--- /dev/null
+# 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.
+
+# Bootstraping the nitcc parser
+#
+# Instead of commiting a generated parser on each version,
+# this program just generate the nitcc_parser using the API of `grammar`
+#
+# Pros:
+#
+# - no generated file commited
+# - easier to modify and bootstrap
+#
+# Cons:
+#
+# - somewhat dublicate the ful grammar of nitcc
+# - need an ad-hoc lexer (nitcc_lexer0.nit)
+#
+module nitcc_parser_gen
+
+import grammar
+
+var g = new Gram
+var p_gr = new Production("grammar")
+var p_lex = new Production("lexer")
+var p_exprs = new Production("exprs")
+var p_expr = new Production("expression")
+var p_re = new Production("re")
+var p_re1 = new Production("re1")
+var p_re2 = new Production("re2")
+var p_re3 = new Production("re3")
+var p_par = new Production("parser")
+var p_ign = new Production("ignored")
+var p_prods = new Production("prods")
+var p_prod = new Production("production")
+var p_ptrans_o = new Production("ptrans_o")
+var p_alts = new Production("alts")
+var p_alt = new Production("alternative")
+var p_altid_o = new Production("altid_o")
+var p_altid = new Production("altident")
+var p_elems = new Production("elems")
+var p_elem = new Production("elem")
+g.prods.add_all([p_gr, p_re, p_re1, p_re2, p_re3, p_lex, p_exprs, p_expr, p_par, p_ign, p_prods, p_prod, p_ptrans_o, p_alts, p_alt, p_altid_o, p_altid, p_elems, p_elem])
+g.prods.add(new Production("atrans"))
+g.prods.add(new Production("elemid"))
+g.prods.add(new Production("nelem"))
+
+var t_opar = new Token("opar")
+var t_cpar = new Token("cpar")
+var t_ocur = new Token("ocur")
+var t_ccur = new Token("ccur")
+var t_pipe = new Token("pipe")
+var t_star = new Token("star")
+var t_ques = new Token("ques")
+var t_plus = new Token("plus")
+var t_minus = new Token("minus")
+var t_colo = new Token("colo")
+var t_semi = new Token("semi")
+var t_dot = new Token("dot")
+var t_eq = new Token("eq")
+var t_arrow = new Token("arrow")
+var t_str = new Token("str")
+var t_id = new Token("id")
+var t_kw = new Token("kw")
+var t_any = new Token("any")
+var t_ch_dec = new Token("ch_dec")
+g.tokens.add_all([t_opar, t_cpar, t_ocur, t_ccur, t_pipe, t_star, t_ques, t_plus, t_minus, t_colo, t_semi, t_dot, t_eq, t_arrow, t_str, t_id, t_kw, t_any, t_ch_dec])
+
+p_gr.new_alt("gr", t_kw, t_id, t_semi, p_lex, p_par)
+
+p_lex.new_alt("lex", t_kw, p_exprs)
+
+p_exprs.new_alt("exprs_many", p_exprs, p_expr)
+p_exprs.new_alt0("exprs_none")
+
+p_expr.new_alt("expr", t_id, t_eq, p_re, t_semi)
+
+p_re.new_alt("re_alter", p_re, t_pipe, p_re1)
+p_re.new_alt("re_re2", p_re1)
+
+p_re1.new_alt("re_minus", p_re1, t_minus, p_re2)
+p_re1.new_alt("re_re1", p_re2)
+
+p_re2.new_alt("re_conc", p_re2, p_re3)
+p_re2.new_alt("re_re3", p_re3)
+
+p_re3.new_alt("re_star", p_re3, t_star)
+p_re3.new_alt("re_ques", p_re3, t_ques)
+p_re3.new_alt("re_plus", p_re3, t_plus)
+p_re3.new_alt("re_par", t_opar, p_re, t_cpar)
+p_re3.new_alt("re_str", t_str)
+p_re3.new_alt("re_ch_dec", t_ch_dec)
+p_re3.new_alt("re_class", t_str, t_dot, t_dot, t_str)
+p_re3.new_alt("re_any", t_any)
+p_re3.new_alt("re_id", t_id)
+
+p_par.new_alt("par", t_kw, p_ign, p_prods)
+
+p_ign.new_alt0("ign_none")
+p_ign.new_alt("ign", t_kw, t_id, t_semi)
+
+p_prods.new_alt("prods_many", p_prods, p_prod)
+p_prods.new_alt0("prods_none")
+
+p_prod.new_alt("prod", t_id, p_ptrans_o, t_eq, p_alts, t_semi)
+
+p_ptrans_o.new_alt("ptrans", t_ocur, t_arrow, t_id, t_ccur)
+p_ptrans_o.new_alt0("ptrans_none")
+
+p_alts.new_alt("alts_many", p_alts, t_pipe, p_alt)
+p_alts.new_alt("alts_one", p_alt)
+
+p_alt.new_alt("alt", p_altid_o, p_elems)
+
+p_altid_o.new_alt0("altid_o_none")
+p_altid_o.new_alt("altid_o_one", p_altid)
+
+p_altid.new_alt("altid", t_ocur, t_id, t_colo, t_ccur)
+
+p_elems.new_alt("elems_many", p_elems, p_elem)
+p_elems.new_alt0("elems_none")
+
+p_elem.new_alt("elem_id", t_id)
+p_elem.new_alt("elem_str", t_str)
+p_elem.new_alt("elem_par", t_opar, p_alts, t_cpar)
+p_elem.new_alt("elem_star", p_elem, t_star)
+p_elem.new_alt("elem_ques", p_elem, t_ques)
+p_elem.new_alt("elem_plus", p_elem, t_plus)
+
+var a = g.lr0
+
+print "LR automaton: {a.states.length} states (see nitcc0.lr.dot)"
+a.to_dot("nitcc0.lr.dot")
+
+a.gen_to_nit("nitcc_parser.nit")
+
+var f = new OFStream.open("nitcc_lexer.nit")
+f.write("import nitcc_lexer0\n")
+f.close
--- /dev/null
+# 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.
+
+# Runtime library required by parsers and lexers generated by nitcc
+module nitcc_runtime
+
+# A abstract parser engine generated by nitcc
+abstract class Parser
+ # The list of tokens
+ # FIXME: provide something better, like a lexer?
+ var tokens = new List[NToken]
+
+ # Look at the next token
+ # Used by generated parsers
+ fun peek_token: NToken do return tokens.first
+
+ # Consume the next token
+ # Used by generated parsers
+ fun get_token: NToken do return tokens.shift
+
+ # Consume the next token and shift to the state `dest`.
+ # Used by generated parsers
+ fun shift(dest: LRState)
+ do
+ var t = get_token
+ #print "shift {t} -> {dest}"
+ node_stack.push t
+ state_stack.push state
+ state = dest
+ end
+
+ # After a reduction on `goto` go to the next state
+ # Used by generated parsers
+ fun goto(goto: LRGoto)
+ do
+ #print "reduce from {state} -> {prod}"
+ state.goto(self, goto)
+ end
+
+ # push a new state on the stack of states (
+ # Used by generated parsers
+ fun push(dest: LRState)
+ do
+ #print "push prod {prod} -> {dest}"
+ state_stack.push state
+ state = dest
+ end
+
+ # Pop and return the last node
+ # Also pop (and discard) the associated state
+ # Used by generated parsers
+ fun pop: Node
+ do
+ var res = node_stack.pop
+ state = state_stack.pop
+ return res
+ end
+
+ # Produce a parse error and stop the parsing
+ # Used by generated parsers
+ fun parse_error
+ do
+ var token = peek_token
+ print "* parse error in state {state} on token {token}"
+ print " expected: {state.error_msg}"
+ print " node_stack={node_stack.join(", ")}"
+ print " state_stack={state_stack.join(", ")}"
+ var error: Node
+ if token isa NLexerError then
+ error = token
+ token.error_tree.items.add_all(node_stack)
+ else
+ error = new NParserError
+ error.position = token.position
+ error.text = token.text
+ error.token = token
+ error.error_tree.items.add_all(node_stack)
+ end
+ node_stack.clear
+ node_stack.add error
+ stop = true
+ end
+
+ # The stating state for parsing
+ # Used by generated parsers
+ protected fun start_state: LRState is abstract
+
+ # The current state
+ # Used by generated parsers
+ var state: LRState
+
+ init
+ do
+ state = start_state
+ end
+
+ # The stack of nodes
+ # Used by generated parsers
+ var node_stack = new Array[Node]
+
+ # The stack of states
+ # Used by generated parsers
+ var state_stack = new Array[LRState]
+
+ # Should the parser stop
+ # Used by generated parsers
+ var stop writable = true
+
+ # Parse a full sequence of tokens and return a complete syntactic tree
+ fun parse: Node
+ do
+ state = start_state
+ state_stack.clear
+ node_stack.clear
+ stop = false
+ while not stop do
+ #print "* current state {state}"
+ #print " tokens={tokens.join(" ")}"
+ #print " node_stack={node_stack.join(" ")}"
+ #print " state_stack={state_stack.join(" ")}"
+ state.action(self)
+ end
+ #print "* over"
+ return node_stack.first
+ end
+end
+
+# A state in a parser LR automaton generated by nitcc
+# Used by generated parsers
+abstract class LRState
+ fun action(parser: Parser) is abstract
+ fun goto(parser: Parser, goto: LRGoto) is abstract
+ fun error_msg: String do return "FIXME"
+end
+
+# A concrete production in a parser LR automation generated by nitcc
+# Used by generated parsers
+abstract class LRGoto
+end
+
+###
+
+# A abstract lexer engine generated by nitcc
+abstract class Lexer
+ # The input stream of characters
+ var stream: String
+
+ # The stating state for lexing
+ # Used by generated parsers
+ protected fun start_state: DFAState is abstract
+
+ # Lexize a stream of characters and return a sequence of tokens
+ fun lex: List[NToken]
+ do
+ var res = new List[NToken]
+ var state = start_state
+ var pos = 0
+ var pos_start = 0
+ var pos_end = 0
+ var line = 1
+ var line_start = 1
+ var line_end = 0
+ var col = 1
+ var col_start = 1
+ var col_end = 0
+ var last_state: nullable DFAState = null
+ var text = stream
+ var length = text.length
+ loop
+ if state.is_accept then
+ pos_end = pos - 1
+ line_end = line
+ col_end = col
+ last_state = state
+ end
+ var c
+ if pos >= length then
+ c = '\0'
+ else
+ c = text[pos]
+ end
+ var next = state.trans(c)
+ if next == null then
+ if last_state == null then
+ var token = new NLexerError
+ var position = new Position(pos_start, pos, line_start, line, col_start, col)
+ token.position = position
+ token.text = text.substring(pos_start, pos-pos_start+1)
+ res.add token
+ break
+ end
+ var position = new Position(pos_start, pos_end, line_start, line_end, col_start, col_end)
+ var token = last_state.make_token(position, text.substring(pos_start, pos_end-pos_start+1))
+ if token != null then res.add(token)
+ if pos >= length then
+ token = new NEof
+ position = new Position(pos, pos, line, line, col, col)
+ token.position = position
+ token.text = ""
+ res.add token
+ break
+ end
+ state = start_state
+ pos_start = pos_end + 1
+ pos = pos_start
+ line_start = line_end
+ line = line_start
+ col_start = col_end
+ col = col_start
+
+ last_state = null
+ continue
+ end
+ state = next
+ pos += 1
+ col += 1
+ if c == '\n' then
+ line += 1
+ col = 1
+ end
+ end
+ return res
+ end
+end
+
+# A state in a lexer automaton generated by nitcc
+# Used by generated lexers
+interface DFAState
+ fun is_accept: Bool do return false
+ fun trans(c: Char): nullable DFAState do return null
+ fun make_token(position: Position, text: String): nullable NToken is abstract
+end
+
+###
+
+# A abstract visitor on syntactic trees generated by nitcc
+abstract class Visitor
+ # The main entry point to visit a node `n`
+ # Should not be redefined
+ fun enter_visit(n: Node)
+ do
+ visit(n)
+ end
+
+ # The specific implementation of a visit
+ #
+ # Should be redefined by concrete visitors
+ #
+ # Should not be called directly (use `enter_visit` instead)
+ #
+ # By default, the visitor just rescursively visit the children of `n`
+ protected fun visit(n: Node)
+ do
+ n.visit_children(self)
+ end
+end
+
+# Print a node (using to_s) on a line and recustively each children indented (with two spaces)
+class TreePrinterVisitor
+ super Visitor
+ var writer: OStream
+ private var indent = 0
+ init(writer: OStream) do self.writer = writer
+ redef fun visit(n)
+ do
+ for i in [0..indent[ do writer.write(" ")
+ writer.write(n.to_s)
+ writer.write("\n")
+ indent += 1
+ super
+ indent -= 1
+ end
+end
+
+# A position into a input stream
+# Used to give position to tokens
+class Position
+ var pos_start: Int
+ var pos_end: Int
+ var line_start: Int
+ var line_end: Int
+ var col_start: Int
+ var col_end: Int
+ redef fun to_s do return "{line_start}:{col_start}-{line_end}:{col_end}"
+end
+
+# A node of a syntactic tree
+abstract class Node
+ # The name of the node (as used in the grammar file)
+ fun node_name: String do return class_name
+
+ # A point of view on the direct childrens of the node
+ fun children: SequenceRead[nullable Node] is abstract
+
+ # Visit all the children of the node with the visitor `v`
+ protected fun visit_children(v: Visitor)
+ do
+ for c in children do if c != null then v.enter_visit(c)
+ end
+
+ # The position of the node in the input stream
+ var position: nullable Position writable = null
+
+ # Produce a graphiz file for the syntaxtic tree rooted at `self`.
+ fun to_dot(filepath: String)
+ do
+ var f = new OFStream.open(filepath)
+ f.write("digraph g \{\n")
+ f.write("rankdir=BT;\n")
+
+ var a = new Array[NToken]
+ to_dot_visitor(f, a)
+
+ f.write("\{ rank=same\n")
+ var first = true
+ for n in a do
+ if first then
+ first = false
+ else
+ f.write("->")
+ end
+ f.write("n{n.object_id}")
+ end
+ f.write("[style=invis];\n")
+ f.write("\}\n")
+
+ f.write("\}\n")
+ f.close
+ end
+
+ private fun to_dot_visitor(f: OStream, a: Array[NToken])
+ do
+ f.write("n{object_id} [label=\"{node_name}\"];\n")
+ for x in children do
+ if x == null then continue
+ f.write("n{x.object_id} -> n{object_id};\n")
+ x.to_dot_visitor(f,a )
+ end
+ end
+
+ redef fun to_s do
+ var pos = position
+ if pos == null then
+ return "{node_name}"
+ else
+ return "{node_name}@({pos})"
+ end
+ end
+end
+
+# A token produced by the lexer and used in a syntactic tree
+abstract class NToken
+ super Node
+
+ redef fun children do return once new Array[Node]
+
+ redef fun to_dot_visitor(f, a)
+ do
+ var labe = "{node_name}"
+ var pos = position
+ if pos != null then labe += "\\n{pos}"
+ var text = self.text
+ if node_name != "'{text}'" then
+ labe += "\\n'{text.escape_to_c}'"
+ end
+ f.write("n{object_id} [label=\"{labe}\",shape=box];\n")
+ a.add(self)
+ end
+
+ # The text associated with the token
+ var text: String writable = ""
+
+ redef fun to_s do
+ var res = super
+ var text = self.text
+ if node_name != "'{text}'" then
+ res += "='{text.escape_to_c}'"
+ end
+ return res
+ end
+end
+
+# The special token for the end of stream
+class NEof
+ super NToken
+end
+
+# A special token used to represent a parser or lexer error
+abstract class NError
+ super NToken
+
+ # All the partially built tree during parsing (aka the node_stack)
+ var error_tree = new Nodes[Node]
+end
+
+# A lexer error as a token for the unexpected characted
+class NLexerError
+ super NError
+end
+
+# A parser error linked to a unexpected token
+class NParserError
+ super NError
+ # The unexpected token
+ var token: nullable NToken
+end
+
+# A hogeneous sequence of node, used to represent unbounded lists (and + modifier)
+class Nodes[T: Node]
+ super Node
+ redef fun children do return items
+ var items = new Array[T]
+end
+
+# A production with a specific, named and statically typed childrens
+class NProd
+ super Node
+ redef var children: SequenceRead[nullable Node] = new NProdChildren(self)
+
+ # The exact number of direct children
+ # Used to implement `children` by generated parsers
+ fun number_of_children: Int is abstract
+
+ # The specific children got by its index
+ # Used to implement `children` by generated parsers
+ fun child(x: Int): nullable Node is abstract
+end
+
+
+private class NProdChildren
+ super SequenceRead[nullable Node]
+ var prod: NProd
+ redef fun iterator do return new NProdIterator(prod)
+ redef fun length do return prod.number_of_children
+ redef fun is_empty do return prod.number_of_children == 0
+ redef fun [](i) do return prod.child(i)
+end
+
+private class NProdIterator
+ super IndexedIterator[nullable Node]
+ var prod: NProd
+ redef var index = 0
+ redef fun is_ok do return index < prod.number_of_children
+ redef fun next do index += 1
+ redef fun item do return prod.child(index)
+end
+
+# All-in-one abstract class to test generated parsers on a given
+abstract class TestParser
+ # How to get a new lexer on a given stream of character
+ fun new_lexer(text: String): Lexer is abstract
+
+ # How to get a new parser
+ fun new_parser: Parser is abstract
+
+ # The name of the language (used for generated files)
+ fun name: String is abstract
+
+ # Use the class as the main enrty point of the program
+ # - parse arguments and options of the command
+ # - test the parser (see `work`)
+ fun main: Node
+ do
+ if args.is_empty then
+ print "usage {name}_test <filepath> | - | -e <text>"
+ exit 0
+ end
+
+ var filepath = args.shift
+ var text
+ if filepath == "-" then
+ text = stdin.read_all
+ else if filepath == "-e" then
+ if args.is_empty then
+ print "Error: -e need a text"
+ exit 1
+ end
+ text = args.shift
+ else
+ var f = new IFStream.open(filepath)
+ text = f.read_all
+ f.close
+ end
+
+ if not args.is_empty then
+ print "Error: superfluous arguments."
+ exit 1
+ end
+
+ return work(text)
+ end
+
+ # Produce a full syntactic tree for a given stream of character
+ # Produce also statistics and output files
+ fun work(text: String): Node
+ do
+ print "INPUT: {text.length} chars"
+ var l = new_lexer(text)
+ var tokens = l.lex
+
+ var tokout = "{name}.tokens.out"
+ print "TOKEN: {tokens.length} tokens (see {tokout})"
+
+ var f = new OFStream.open(tokout)
+ for t in tokens do
+ f.write "{t.to_s}\n"
+ end
+ f.close
+
+ var p = new_parser
+ p.tokens.add_all(tokens)
+
+ var n = p.parse
+
+ var astout = "{name}.ast.out"
+ f = new OFStream.open(astout)
+ var tpv = new TreePrinterVisitor(f)
+ var astdotout = "{name}.ast.dot"
+ if n isa NError then
+ print "ERROR: {n} (see {astout} and {astdotout})"
+ tpv.enter_visit(n)
+ n = n.error_tree
+ else
+ print "ROOT: {n} (see {astout} and {astdotout})"
+ end
+ tpv.enter_visit(n)
+ n.to_dot(astdotout)
+ f.close
+
+ return n
+ end
+end
--- /dev/null
+# 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.
+
+# Semantic analysis of a sablecc grammar file
+# User to validate the file and generate the grammar and the nfa
+#
+# TODO: split in submodules
+module nitcc_semantic
+
+import nitcc_parser
+import grammar
+import re2nfa
+
+# Main visitor for the semantic analysis
+#
+# TODO clean-up the visitors
+class CollectNameVisitor
+ super Visitor
+
+ var nprods = new Array[Nprod]
+
+ # Symbol table to associate things (prods and exprs) with their name
+ var names = new HashMap[String, Node]
+
+ # The associated grammar object
+ # Read the result of the visit here
+ var gram = new Gram
+
+ # The associated NFA object
+ # Read the result of the visit here
+ var nfa = new Automaton.empty
+
+ # Run the semantic analysis of the grammar
+ fun start(n: Node)
+ do
+ # First visit to collect names
+ enter_visit(n)
+
+ # Second visit to use collectec names and build rhings
+ var v2 = new CheckNameVisitor(self)
+ v2.enter_visit(n)
+
+ # Inline all the ?
+ gram.inline(v2.quesizes.values)
+ # Inlile all the prods sufixed by '_imline' #TODO use a real keyword
+ for p in gram.prods do
+ if not p.name.has_suffix("_inline") then continue
+ print "inline {p}"
+ gram.inline([p])
+ end
+
+ # Build the NFA automaton
+ for t in gram.tokens do
+ var nexpr = t.nexpr
+ var nfa2
+ if nexpr == null then
+ nfa2 = new Automaton.epsilon
+ for c in t.text.as(not null) do
+ nfa2.concat(new Automaton.atom(c.ascii))
+ end
+ else
+ nfa2 = nexpr.build_nfa
+ end
+ nfa.states.add_all nfa2.states
+ nfa.start.add_trans(nfa2.start, null)
+ for s in nfa2.accept do nfa.add_tag(s, t)
+ nfa.accept.add_all nfa2.accept
+ end
+ end
+
+ redef fun visit(n) do n.accept_collect_prod(self)
+end
+
+private class CheckNameVisitor
+ super Visitor
+
+ # The collected altname, for the alternative
+ var altname: nullable String = null
+
+ # The collected elements, for the alternative
+ var elems = new Array[Element]
+
+ # The collected element names, for the alternative
+ var elems_names = new Array[nullable String]
+
+ # The collected elementname, for the nelem
+ var elemname: nullable String = null
+
+ # Is the alternative transformed, for the alternative
+ var trans = false
+
+ # Pool of elements that are modified with + (reuse them!)
+ private var plusizes = new HashMap[Element, Production]
+
+ # Create a + version of an element
+ fun plusize(e: Element): Production
+ do
+ if plusizes.has_key(e) then return plusizes[e]
+ var name = "{e}+"
+ var prod = new Production(name)
+ prod.acname = "Nodes[{e.acname}]"
+ v1.gram.prods.add(prod)
+ prod.new_alt("{name}_one", e)
+ prod.new_alt("{name}_more", prod, e)
+ plusizes[e] = prod
+ return prod
+ end
+
+ # Pool of elements that are modified with ? (reuse them!)
+ private var quesizes = new HashMap[Element, Production]
+
+ # Create a ? version of an element
+ fun quesize(e: Element): Production
+ do
+ if quesizes.has_key(e) then return quesizes[e]
+ var name = "{e}?"
+ var prod = new Production(name)
+ prod.acname = "nullable {e.acname}"
+ v1.gram.prods.add(prod)
+ var a1 = prod.new_alt("{name}_one", e)
+ a1.codes = [new CodePop]
+ var a0 = prod.new_alt0("{name}_none")
+ a0.codes = [new CodeNull]
+ quesizes[e] = prod
+ return prod
+ end
+
+ # The current nexpr, used to track dependency on named expressions (see `Nexpr::precs`)
+ var nexpr: nullable Nexpr
+
+ # The current production, used to initialize alternatives
+ var prod: nullable Production
+
+ # The main visitor, used to access the grammar of the symbol table
+ var v1: CollectNameVisitor
+ init(v1: CollectNameVisitor) do self.v1 = v1
+
+ redef fun visit(n) do n.accept_check_name_visitor(self)
+end
+
+redef class Node
+ private fun accept_collect_prod(v: CollectNameVisitor) do visit_children(v)
+ private fun accept_check_name_visitor(v: CheckNameVisitor) do visit_children(v)
+end
+
+redef class Nexpr
+ # The associated token
+ var token: nullable Token
+
+ # The associated name
+ var name: nullable String
+
+ # The required expression (Nexpr that are used inside)
+ var precs = new ArraySet[Nexpr]
+
+ redef fun accept_collect_prod(v) do
+ var id = children.first.as(Nid)
+ var name = id.text
+ v.names[name] = self
+ self.name = name
+ end
+ redef fun accept_check_name_visitor(v) do
+ v.nexpr = self
+ super
+ v.nexpr = null
+ end
+
+ # Flag to track circular regular expression
+ var is_building = false
+
+ # The associated NFA (cached, see `build_nfa`)
+ private var nfa: nullable Automaton
+
+ # Build the NFA, possibily building the NFA of required expressions
+ # Print an error if there is a circular dependency
+ # The result is cached
+ fun build_nfa: Automaton do
+ var res = nfa
+ if res != null then return res
+ if is_building then
+ print "Error: circular regular expression {name.to_s}."
+ exit(1)
+ abort
+ end
+ is_building = true
+ for p in precs do
+ p.build_nfa
+ end
+ is_building = false
+ var nre = self.children[2]
+ res = nre.make_rfa
+ nfa = res
+ return res
+ end
+end
+
+redef class Nre_id
+ # The named expression
+ var nexpr: nullable Nexpr
+
+ redef fun accept_check_name_visitor(v) do
+ var id = children.first.as(Nid)
+ var name = id.text
+ if not v.v1.names.has_key(name) then
+ print "Error: unknown name {name}"
+ exit(1)
+ abort
+ end
+ var node = v.v1.names[name]
+ if node isa Nprod then
+ print "Error: cannot use production {name} in a regular expression"
+ exit(1)
+ abort
+ else if not node isa Nexpr then
+ abort
+ end
+ nexpr = node
+ v.nexpr.precs.add(node)
+ end
+
+ redef fun make_rfa
+ do
+ return nexpr.nfa.dup
+ end
+end
+
+redef class Nign
+ # The named element
+ var elem: nullable Element
+
+ redef fun accept_check_name_visitor(v) do
+ var id = children[1].as(Nid)
+ var name = id.text
+ if not v.v1.names.has_key(name) then
+ print "Error: unknown name {name}"
+ exit(1)
+ abort
+ end
+ var node = v.v1.names[name]
+ var elem: nullable Element
+ if node isa Nprod then
+ print "Error cannot ignore a production"
+ exit(1)
+ abort
+ else if node isa Nexpr then
+ elem = node.token
+ if elem == null then
+ elem = new Token("Ignored")
+ elem.nexpr = node
+ v.v1.gram.tokens.add(elem)
+ node.token = elem
+ end
+ else
+ abort
+ end
+ self.elem = elem
+ end
+end
+
+redef class Nprod
+ # The associated production
+ var prod: nullable Production
+
+ redef fun accept_collect_prod(v) do
+ var id = children.first.as(Nid)
+ var name = id.text
+ v.names[name] = self
+ v.nprods.add(self)
+ prod = new Production(name)
+ v.gram.prods.add(prod.as(not null))
+ end
+ redef fun accept_check_name_visitor(v) do
+ v.prod = prod
+ super
+ v.prod = null
+ end
+end
+
+redef class Nptrans
+ redef fun accept_check_name_visitor(v) do
+ var id = children[2].as(Nid)
+ var name = id.text
+
+ var node = v.v1.names[name]
+ if node isa Nprod then
+ v.prod.spe = node.prod.as(not null)
+ if v.prod.spe.spe != null then
+ print "Cannot transform into {name}, {name} is already transformed."
+ exit(1)
+ abort
+ end
+ else if node isa Nexpr then
+ print "Cannot transform into {name}, {name} is a token."
+ else
+ abort
+ end
+ end
+end
+
+redef class Natrans
+ redef fun accept_check_name_visitor(v) do
+ var id = children[2].as(Nid)
+ var name = id.text
+
+ v.trans = true
+ end
+end
+
+redef class Nalt
+ # The associated alternative
+ var alt: nullable Alternative
+
+ redef fun accept_check_name_visitor(v)
+ do
+ v.altname = null
+ v.trans = false
+ v.elems = new Array[Element]
+ v.elems_names = new Array[nullable String]
+ super
+ var prod = v.prod.as(not null)
+ var prodabs = prod.spe
+ if prodabs == null then prodabs = prod
+ var name = v.altname
+ if name == null then
+ if v.trans then
+ name = prod.name + "_" + prod.alts.length.to_s
+ else if prod.spe == null and prod.alts.is_empty then
+ name = prod.name
+ prod.altone = true
+ else
+ if prodabs.altone then
+ prodabs.altone = false
+ prodabs.alts.first.name = prodabs.name + "_0"
+ end
+ name = prod.name + "_" + prod.alts.length.to_s
+ end
+ else
+ if prodabs.altone then
+ prodabs.altone = false
+ prodabs.alts.first.name = prodabs.name + "_0"
+ end
+ name = prodabs.name + "_" + name
+ end
+ var alt = prod.new_alt2(name, v.elems)
+ alt.elems_names.add_all(v.elems_names)
+ self.alt = alt
+ if v.trans then
+ alt.trans = true
+ alt.codes = [new CodePop]
+ end
+ end
+end
+
+redef class Naltid
+ redef fun accept_check_name_visitor(v)
+ do
+ var id = children[1].as(Nid)
+ var name = id.text
+ v.altname = name
+ end
+end
+
+redef class Nelem
+ # The associated element
+ var elem: nullable Element
+end
+
+redef class Token
+ # The associated expression (if any, ie defined in the lexer part)
+ var nexpr: nullable Nexpr
+ # The associated text (if any, ie defined in the parser part)
+ var text: nullable String
+end
+
+redef class Nnelem
+ redef fun accept_check_name_visitor(v)
+ do
+ v.elemname = null
+ super
+ var elemname = v.elemname
+ if elemname != null and v.elems_names.has(elemname) then
+ var i = 2
+ while v.elems_names.has(elemname+i.to_s) do i += 1
+ elemname = elemname+i.to_s
+ end
+ v.elems_names.add elemname
+ end
+end
+
+redef class Nelemid
+ redef fun accept_check_name_visitor(v)
+ do
+ var id = children[1].as(Nid)
+ var name = id.text
+ v.elemname = name
+ end
+end
+
+redef class Nelem_id
+ redef fun accept_check_name_visitor(v) do
+ var id = children.first.as(Nid)
+ var name = id.text
+ if not v.v1.names.has_key(name) then
+ print "Error: unknown name {name}"
+ exit(1)
+ abort
+ end
+ var node = v.v1.names[name]
+ var elem: nullable Element
+ if node isa Nprod then
+ elem = node.prod
+ else if node isa Nexpr then
+ elem = node.token
+ if elem == null then
+ elem = new Token(name)
+ elem.nexpr = node
+ v.v1.gram.tokens.add(elem)
+ node.token = elem
+ end
+ else
+ abort
+ end
+ assert elem != null
+ self.elem = elem
+ v.elems.add(elem)
+ if v.elemname == null then v.elemname = name
+ end
+end
+
+redef class Nelem_str
+ redef fun accept_check_name_visitor(v) do
+ var str = children.first.as(Nstr)
+ var text = str.value
+ var name = str.text
+ var elem: nullable Element
+ if v.v1.names.has_key(name) then
+ elem = v.v1.names[name].as(Nelem_str).elem
+ assert elem != null
+ else
+ elem = new Token(name)
+ elem.text = text
+ v.v1.gram.tokens.add(elem)
+ v.v1.names[name] = self
+ end
+ self.elem = elem
+ v.elems.add(elem)
+ end
+end
+
+redef class Nelem_star
+ redef fun accept_check_name_visitor(v) do
+ super
+ var elem = v.elems.pop
+ elem = v.plusize(elem)
+ elem = v.quesize(elem)
+ v.elems.add elem
+ end
+end
+
+redef class Nelem_ques
+ redef fun accept_check_name_visitor(v) do
+ super
+ var elem = v.elems.pop
+ elem = v.quesize(elem)
+ v.elems.add elem
+ end
+end
+
+redef class Nelem_plus
+ redef fun accept_check_name_visitor(v) do
+ super
+ var elem = v.elems.pop
+ elem = v.plusize(elem)
+ v.elems.add elem
+ end
+end
--- /dev/null
+# 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.
+
+# Transformation of regular expression to NFA
+module re2nfa
+
+import nitcc_lexer
+import autom
+
+redef class Node
+ # Build the NFA of the regular expression
+ fun make_rfa: Automaton do
+ print inspect
+ abort
+ end
+end
+
+redef class Nstr
+ # The real value of the string
+ fun value: String do return text.substring(1, text.length-2).unescape_nit
+ redef fun make_rfa: Automaton
+ do
+ var a = new Automaton.epsilon
+ var val
+ for c in self.value do
+ var b = new Automaton.atom(c.ascii)
+ a.concat(b)
+ end
+ return a
+ end
+end
+
+redef class Nch_dec
+ # The real value of the char
+ fun value: String do return text.substring_from(1).to_i.ascii.to_s
+ redef fun make_rfa: Automaton
+ do
+ var a = new Automaton.atom(self.value.first.ascii)
+ return a
+ end
+end
+
+redef class NProd
+ redef fun make_rfa: Automaton
+ do
+ assert children.length == 1 else print "no make_rfa for {self}"
+ return children.first.make_rfa
+ end
+end
+
+redef class Nre_alter
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ var b = children[2].make_rfa
+ a.alternate(b)
+ return a
+ end
+end
+
+redef class Nre_minus
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ var b = children[2].make_rfa.to_dfa
+ for t in b.start.outs do
+ if not t.to.outs.is_empty then
+ print "Not Yet Implemented Error: '-' only works on single char"
+ exit(1)
+ end
+ a.minus_sym(t.symbol.as(not null))
+ end
+ return a
+ end
+end
+
+redef class Nre_conc
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ var b = children[1].make_rfa
+ a.concat(b)
+ return a
+ end
+end
+
+redef class Nre_star
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ a.close
+ return a
+ end
+end
+
+redef class Nre_ques
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ a.optionnal
+ return a
+ end
+end
+
+redef class Nre_plus
+ redef fun make_rfa
+ do
+ var a = children[0].make_rfa
+ a.plus
+ return a
+ end
+end
+
+redef class Nre_par
+ redef fun make_rfa
+ do
+ return children[1].make_rfa
+ end
+end
+
+redef class Nre_class
+ redef fun make_rfa: Automaton
+ do
+ var c1 = children[0].as(Nstr).value
+ var c2 = children[3].as(Nstr).value
+ if c1.length != 1 or c2.length != 1 then
+ print "Classes only works on single char"
+ exit(1)
+ abort
+ end
+ var a = new Automaton.cla(c1.first.ascii, c2.first.ascii)
+ return a
+ end
+end
+
+redef class Nre_any
+ redef fun make_rfa: Automaton
+ do
+ var a = new Automaton.cla(0, 127)
+ return a
+ end
+end
--- /dev/null
+#!/usr/bin/perl
+
+# Alterner.pl
+#
+# Copyright 2011 Jean Privat <jean@pryen.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.
+
+use strict;
+use File::Basename;
+
+# Default values for options
+my $directory = "alt"; # The directory where alternatives will be generated.
+my $start = "//"; # The marker at the begin of a directive (usually the start of a comment).
+my $end = ""; # The marker at the end of the line (usually the end of a comment)
+my $altsep = "."; # The separator used in generated file between the basename and the altmark.
+
+sub usage(;$) {
+ my $msg = shift;
+ my $usage = "Usage: alterner.pl [-d dir] [--start pat] [--end pat] file...";
+ if (defined $msg) {
+ print STDERR $msg . "\n" . $usage . "\n";
+ exit 1;
+ } else {
+ print $usage . "\n";
+ exit 0;
+ }
+}
+
+# Process arguments.
+my $stop = 0;
+while (@ARGV && !$stop) {
+ my $arg = $ARGV[0];
+ my $val = $ARGV[1];
+ if ($arg eq "-d") {
+ $directory = $val or usage "$arg requires a directory.";
+ shift @ARGV;
+ shift @ARGV;
+ } elsif ($arg eq "--start") {
+ $start = $val or usage "$arg requires a pattern.";
+ shift @ARGV;
+ shift @ARGV;
+ } elsif ($arg eq "--end") {
+ $end = $val or usage "$arg requires a pattern.";
+ shift @ARGV;
+ shift @ARGV;
+ } elsif ($arg eq "--altsep") {
+ $altsep = $val or usage "$arg requires a pattern.";
+ shift @ARGV;
+ shift @ARGV;
+ } elsif ($arg eq "-h" || $arg eq "--help") {
+ shift @ARGV;
+ usage
+ } elsif ($arg eq "--") {
+ shift @ARGV;
+ $stop = 1;
+ } elsif ($arg =~ /^-/) {
+ usage "Unknown argument $arg.";
+ } else {
+ $stop = 1;
+ }
+}
+
+if ($#ARGV == -1) {
+ usage("No input file.");
+}
+
+# Is $_[0] triggers the alternative directive $[1]?
+sub triggers_alt($$) {
+ my $number = shift;
+ my $directive = shift;
+ foreach my $a (split ",", $directive) {
+ if ($a =~ /^(\d+)-(\d+)$/) {
+ if ($1 <= $number && $number <= $2) {
+ return 1;
+ }
+ } else {
+ if ($number == $a) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+# Generate alternatives from the specified input-file using a specific altmark
+sub process_alts($$) {
+ my $file = shift;
+ my $altmark = shift;
+ # Read the file
+ open my $in, "<", $file or die "$file: $!";
+ my @lines = <$in>;
+ close($file);
+
+ my $prefix = $start . $altmark;
+
+ # Collect alternatives
+ my %alt;
+ foreach my $l (@lines) {
+ while ($l =~ /\Q$prefix\E([\d,-]+)(\Q$start\E|\b)/g) {
+ for my $a (split /[,-]/, $1) {
+ $alt{$a} = 1;
+ }
+ }
+ }
+ my @alt = sort(keys(%alt));
+
+ my @files;
+ # Process each alternatives
+ foreach my $alt (@alt) {
+ # Exctact the basename and the suffix
+ my ($name, $path, $suffix) = fileparse($file, qr/\.[^\.]*/);
+
+ # Compute filename of the alternative
+ my $outfile = $name . $altsep . $altmark . $alt . $suffix;
+ if (defined $directory && $directory ne ".") {
+ $outfile = $directory . "/" . $outfile;
+ if (! -d $directory) {
+ mkdir $directory or die "$directory: $!";
+ }
+ }
+ push @files, $outfile;
+
+ # Write the alternative
+ open my $out, ">", $outfile or die "$outfile: $!";
+ print "$outfile\n";
+ foreach my $l (@lines) {
+ my $l2 = $l;
+ my $selected;
+ while ($l =~ /(\Q$prefix\E([\d,-]+)(\Q$start\E|\b))/g) {
+ if (triggers_alt $alt, $2) {
+ $selected = $1;
+ }
+ }
+ if ($selected && $l =~ /^(\s*)(.*)(\s*)(\Q$selected\E)([ \t]*)(.*)([ \t]*\Q$end\E\s*)$/) {
+ $l2 = "$1$6$3$4$5$2$7";
+ }
+ print $out $l2 or die "$outfile: $!";
+ }
+ close $out;
+ }
+ return @files;
+}
+
+# Generate combination of alternatives from the specified input-file
+sub process_xalts($) {
+ my $file = shift;
+ # Read the file
+ open my $in, "<", $file or die "$file: $!";
+ my @lines = <$in>;
+ close($file);
+
+ # Collect combination of alternatives
+ my %alt;
+ foreach my $l (@lines) {
+ while ($l =~ /\Q$start\E(\d*alt)\d+(\Q$start\E|\b)/g) {
+ $alt{$1} = $1;
+ }
+ }
+ my @alt = sort(keys(%alt));
+
+ my @files = $file;
+ # Process each combination of alternatives
+ foreach my $alt (@alt) {
+ my @newfiles;
+ foreach my $f (@files) {
+ push @newfiles, process_alts($f, $alt);
+ }
+ push @files, @newfiles;
+ }
+}
+
+# Do the job
+foreach my $file (@ARGV) {
+ process_xalts($file);
+}
--- /dev/null
+a
\ No newline at end of file
--- /dev/null
+//alt1 /*
+Grammar x;
+//alt2 /*
+Lexer
+//alt3 /*
+a = 'a';
+//alt4 /*
+Parser
+//alt5 /*
+Ignored a;
+//alt6 /*
+p = a;
+
+/* //alt1 //alt2 //alt3 //alt4 //alt5 //alt6
+*/
--- /dev/null
+Grammar if;
+Lexer
+Parser
+
+p = 'e' e | 'f' f;
+
+e =
+ 'if' e 'then' e else? |
+ 'e' ;
+else = 'else' e;
+
+f =
+ 'if' f 'then' f |
+ 'if' f 'then' f 'else' f |
+ 'f' ;
--- /dev/null
+if iff iff i f
+if50i
+if 50 i
+50
+50.50
+50.
+.50
+50.f
+f.50
--- /dev/null
+Grammar automate;
+Lexer
+ letter = 'a'..'z';
+ digit = '0'..'9';
+ id = letter(letter|digit)*;
+ if = 'if';
+ int = (digit)+;
+ float = (digit)+ '.' (digit)+;
+ dot = '.';
+ blank = #10|#13|#32;
+Parser
+ Ignored blank;
+ p = t*;
+ t = id | if | int | float | dot;
--- /dev/null
+if iff iff i f
+if50i
+if,i
--- /dev/null
+Grammar demo;
+Lexer
+ letter = 'a'..'z';
+ digit = '0'..'9';
+ identifier = letter (letter | digit)*;
+ comma = ',';
+ blank = (' ' | #9 | #10 | #13)+;
+ if = 'if';
+ else = 'else';
+Parser
+ Ignored
+ blank;
+ p = t*;
+ t = identifier | comma | if | else;
--- /dev/null
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab
--- /dev/null
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
--- /dev/null
+Grammar pire_cout;
+Lexer
+ a = 'a';
+ b = 'a'* 'b';
+blank = ' ' | #9 | #10 | #13;
+Parser
+ Ignored blank;
+ p = t*;
+ t = a | b;
--- /dev/null
+i f
+if
+ify
--- /dev/null
+Grammar priorite_d_inclusion;
+Lexer
+ identifier = ('a'..'z')+;
+ if = 'if';
+ blank = ' ' | #9 | #10 | #13;
+Parser
+ Ignored blank;
+ p = t*;
+ t = identifier | if;
--- /dev/null
+z10
+00ff1
+fff
+00fg1
--- /dev/null
+Grammar priorite_declaree;
+Lexer
+ letter = 'a'..'z';
+ digit = '0'..'9';
+ identifier = letter(letter|digit)*;
+ hexinteger = (digit|'a'..'f')+ Except identifier;
+ blank = ' ' | #9 | #10 | #13;
+Parser
+ Ignored blank;
+ p = t*;
+ t = identifier | hexinteger;
--- /dev/null
+5 + 4 * 2
+5 - 4 - 2
+5 + 4 + 2
+(5 + 4) * 2
+5 + (4 * 2)
--- /dev/null
+Grammar arithmetique;
+Lexer
+int = ('0'..'9')+;
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+exps = exp*;
+
+exp = {add:} exp '+' exp |
+ {min:} exp '-' exp |
+ {mul:} exp '*' exp |
+ {int:} int |
+ {par:} '(' exp ')' ;
+//alt1 Precedence
+//alt1 Left mul;
+//alt1 Left add, min;
--- /dev/null
+5 + 4 * 2
+5 - 4 - 2
+5 + 4 + 2
+(5 + 4) * 2
+5 + (4 * 2)
--- /dev/null
+Grammar arithmetique;
+Lexer
+int = ('0'..'9')+;
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+exps = exp*;
+
+exp = {add:} exp '+' factor |
+ {min:} exp '-' factor |
+ {factor:} factor ;
+factor = {mul:} factor '*' term |
+ {term:} term ;
+term = {int:} int |
+ {par:} '(' exp ')' ;
--- /dev/null
+centre (5pt, 1cm) rayon 10 px
--- /dev/null
+Grammar formes;
+Lexer
+ nombre = ('0'..'9')+;
+ blank = ' ' | #9 | #10 | #13;
+
+Parser
+ Ignored blank;
+ forme = {cercle:} 'centre' point 'rayon' long |
+ {segment:} point '--' point ;
+ point = '(' long ',' long ')' ;
+ long = nombre unite ;
+ unite = 'cm' | 'mm' | 'pt' | 'px' ;
--- /dev/null
+(ida(idb idc)(idd ide(idf))())
--- /dev/null
+Grammar lisp;
+Lexer
+id = ('a'..'z')+;
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+item = {par:} '(' list ')' |
+ {nil:} '(' ')' |
+ {id:} id ;
+list = {many:} item list |
+ {one:} item ;
--- /dev/null
+ida idb idc
--- /dev/null
+Grammar listes;
+Lexer
+id = ('a'..'z')+;
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+list = {many:} id list |
+ {one:} id ;
--- /dev/null
+Grammar parentheses;
+Lexer
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+par = {item:} '(' par ')' |
+ {empty:} '(' ')' ;
--- /dev/null
+1+2*(3+4)+5
--- /dev/null
+Grammar calc;
+Lexer
+ int = ('0'..'9')+;
+ blank = ' ' | #9 | #10 | #13;
+
+Parser
+ Ignored blank;
+ exprs = expr*;
+ expr =
+ {add:} [left:]expr '+' [right:]expr |
+ {sub:} [left:]expr '-' [right:]expr |
+ {mul:} [left:]expr '*' [right:]expr |
+ {int:} int |
+ {par:} '(' expr ')' ;
+ Precedence Left mul; Left add, sub;
--- /dev/null
+prods = {many:} prods prod | {one:} prod ;
+prod = id '=' alts ';' ;
+alts = {many:} alts '|' alt | {one:} alt ;
+alt = altid atoms | atoms ;
+atoms = {many:} atoms atom | {none:} Empty;
+atom = {id:} id | {str:} str ;
--- /dev/null
+Grammar grammaire;
+Lexer
+id = ('a'..'z')+;
+altid = '{' id ':}';
+str = '\'' (Any* - '\'') '\'';
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+prods = {many:} prods prod | {one:} prod ;
+prod = id '=' alts ';' ;
+alts = {many:} alts '|' alt | {one:} alt ;
+alt = altid atoms | atoms ;
+atoms = {many:} atoms atom | {none:} ;
+atom = {id:} id | {str:} str ;
--- /dev/null
+prods = {many:} prods prod | {one:} prod ;
+prod = id '=' alts ';' ;
+alts = {many:} alts '|' alt | {one:} alt ;
+alt = altid atoms | atoms ;
+atoms = {many:} atoms atom | {none:} Empty;
+atom = {id:} id | {str:} str ;
--- /dev/null
+Grammar grammaire;
+Lexer
+id = ('a'..'z')+;
+altid = '{' id ':}';
+str = '\'' (Any* - '\'') '\'';
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+prods = prod+;
+prod = id '=' (alt Separator '|')+ ';' ;
+alt = altid? atom* ;
+atom = {id:} id | {str:} str ;
--- /dev/null
+x = y;
+while (z) { print(t); k=u; }
--- /dev/null
+Grammar instuctions;
+Lexer
+id = ('a'..'z')+;
+blank = ' ' | #9 | #10 | #13;
+
+Parser
+Ignored blank;
+
+prog = stmt* ;
+stmt =
+ {assign:} id '=' expr ';' |
+ {print:} 'print' '(' expr ')' ';' |
+ {while:} 'while' '(' expr ')' '{' stmt* '}'
+//alt1 | {until:} 'do' '{' stmt* '}' 'until' '(' expr ')' ';'
+//alt2 | {if:} 'if' '('expr')' '{'stmt*'}' elsex?
+//alt3 | {call:} id '(' args ')' ';'
+ ;
+
+//alt2 elsex = 'else' '{' stmt* '}' ;
+//alt3 args = (expr Separator ',')* ;
+
+expr = id;
--- /dev/null
+((0x,0y), (10, 0deg), (0x, 10y))
--- /dev/null
+Grammar polygones;
+Lexer
+ num = ('0'..'9')+;
+ blank = ' ' | #9 | #10 | #13;
+
+Parser
+ Ignored blank;
+ polygone = '(' (point Separator ',')* ')' ;
+ point =
+ {cart:} '(' num 'x' ',' num 'y' ')' |
+ {pol:} '(' num ',' num 'deg' ')' ;
--- /dev/null
+Grammar lalr;
+Parser
+p = q 'a' | r 'b' ;
+q = 'x' ;
+r = 'x';
--- /dev/null
+Grammar re;
+Lexer
+
+ notst = Any - '*';
+ notstsl = notst - '/';
+
+ com1 = '/*' (notst | '*'+ notstsl)* '*'+ '/';
+ com2 = '/*' notst* ('*' (notstsl notst*)?)* '*/'; //alt1 //alt2 //alt3 //alt4
+ //alt1 com2 = Shortest ('/*' Any* '*/');
+ //alt2 com2 = '/*' (Any* - '*/') '*/';
+ //alt3 com2 = '/*' (('' Lookahead Not '*/') Any)* '*/';
+ //alt4 com2 = '/*' (Any* Except (Any* '*/' Any*)) '*/';
+
+ dummy = notst | notstsl;
+
+Parser
+
+p = com1 | com2 | dummy;
+
--- /dev/null
+Grammar re;
+Lexer
+
+ //alt1 a = '';
+ //alt2 a = 'a' - 'a';
+ //alt3 a = a;
+ //alt4 a = b; b = a;
+ //alt5 a = fail;
+ //alt6 a = 'a'; i = 'i';
+ //alt7 a = '' Lookahead 'a';
+ //alt8 a = 'a';
+
+Parser
+//alt8 Ignored fail;
+t = a;
--- /dev/null
+Grammar re;
+Lexer
+a='a';
+anb='a' Lookahead Not 'b';
+ab='ab'; //alt1 ab='DUMMY';
+b='b';
+blank=#10|#13|#32;
+Parser
+Ignored blank;
+t = a | anb | ab | b;
--- /dev/null
+Grammar re;
+Lexer
+a = 'a' Lookahead 'b'; //alt1 a = 'a' Lookahead ('b'|'c');
+b = 'ab';
+blank = #10 | #13 | #32;
+Parser
+Ignored blank;
+t = a | b;
--- /dev/null
+Grammar re;
+Lexer
+a = 'a';
+b = 'a' 'b' 'a' 'c'; //alt1 //alt2
+//alt1 b = 'a' 'b' 'a'* 'c';
+//alt2 b = 'a'* 'b' 'a' 'c';
+blank = #10 | #13 | #32;
+Parser
+Ignored blank;
+t = a | b;
--- /dev/null
+Grammar re;
+Lexer
+ //alt1,2,3 a = 'a'; b = 'a' | 'b' ;
+ //alt4,5 a = 'a' | 'c' ; b = 'a' | 'b' ;
+ //alt6,7 a = 'a' ; b = 'b' ;
+ //alt8 a = ('a' Lookahead 'b') | 'c'; b = 'a';
+ //alt9,10 a = 'a'; b = 'b';
+
+xa = a; xb =b; //alt2,5,7,10 xa = a ; xb = b - a; //Precedence a > b; //alt3 xa = a - b ; xb = b; //Precedence b > a;
+blank = #10 | #13;
+
+Parser
+Ignored blank;
+t = xa | xb;
--- /dev/null
+Grammar re;
+Lexer
+ a = 'a';
+ ab = 'ab';
+ a_ab = a | ab;
+ alb_ab = 'a' Lookahead Not 'b' | 'ab';
+ s = Shortest(a_ab);
+ l = Longest(a_ab);
+ dummy = 'x' (a|ab|alb_ab|s|l);
+ blank = #10 | #13 | #32;
+
+Parser
+Ignored blank;
+ t =
+ ab | l | //alt1
+//alt1 a | s |
+//alt2 a |
+//alt3 alb_ab |
+ dummy;
+
--- /dev/null
+Grammar re;
+Lexer
+a = 'a' 'b'?;
+la = Longest(a);
+sa = Shortest(a);
+sla = Shortest(la);
+dummy = 'X' (a|la|sa|sla);
+blank = #10 | #13 | #32;
+Parser
+Ignored blank;
+t = dummy |
+a | la; //alt1 a | sa; //alt2 a | sla; //alt3 la | sa; //alt4 la | sla;
--- /dev/null
+Grammar lexer_conflict_token;
+
+Lexer
+
+a = 'a'|'x';
+b = 'b'|'x';
+
+Parser
+
+p = a | b;
--- /dev/null
+Grammar lexer_empty_token;
+
+Lexer
+
+a= 'a'*;
+b= '';
+
+Parser
+
+p = a | b;
--- /dev/null
+i if iff ifff
+70
+zero
+0xFFff // unseen
+192.168.0.1
--- /dev/null
+Grammar lr1;
+Parser
+p = q 'a' q 'b' | r 'a';
+q = 'x';
+r = 'x';
--- /dev/null
+aa
\ No newline at end of file
--- /dev/null
+abccbbcccdcba
\ No newline at end of file
--- /dev/null
+abba
\ No newline at end of file
--- /dev/null
+abcddcba
\ No newline at end of file
--- /dev/null
+Grammar modif;
+
+Parser
+
+p = 'a' q* 'a';
+q = 'b' r+ 'b';
+r = 'c' s? 'c';
+s = 'd';
--- /dev/null
+Grammar nit_expr;
+Parser
+e =
+ e '[' e ']' |
+ '[' e ']' |
+ '[' e '..' e '[' |
+ '[' e '..' e ']' |
+ 'id' e |
+ 'id';
+
+
--- /dev/null
+Grammar not_lalr;
+Parser
+p = 'a' x 'a' | 'a' y 'b' | 'b' y 'a' | 'b' x 'b' ;
+x = 'a';
+y = 'a';
--- /dev/null
+Grammar not_slr;
+
+Parser
+
+s = g '=' d | d ;
+g = '*' d | 'id' ;
+d = g ;
--- /dev/null
+Grammar pri;
+Parser
+a = 'p' p | 'q' q | 'r' r? ;
+p = p '+' p | 'p';
+q = q q | 'q' ;
+r = r2?;
+r2 = 'r';
--- /dev/null
+Grammar priority;
+Parser
+e =
+ e '+' e |
+ e '-' e |
+ e '*' e |
+ '0' ;
--- /dev/null
+Grammar qualified;
+
+Parser
+ e = r q id;
+ r = e '.' | ;
+ q = qe+ | ;
+ qe = id '::' ;
+ id = 'id';
--- /dev/null
+./t "$@" *.sablecc ../examples/*.sablecc
--- /dev/null
+* parse error in state Start on token Eof@(16:1-16:1)=''
+NParserError@(16:1-16:1)=''
--- /dev/null
+Error: grammar with no production
--- /dev/null
+1,1. Syntax error: Unexpected character 'a'.
--- /dev/null
+Error: grammar with no production
--- /dev/null
+Error: grammar with no production
--- /dev/null
+T_A@1,1:a
+TEnd@1,2:
--- /dev/null
+Error: grammar with no production
--- /dev/null
+T_A@1,1:a
+TEnd@1,2:
--- /dev/null
+Error: grammar with no production
--- /dev/null
+NParserError@(1:2-1:2)=''
+Nodes
--- /dev/null
+NLexerError@(1:1-1:1)=''
+Nodes
--- /dev/null
+Start
+ p
+ Nodes
+ t_1
+ if@(1:1-1:3)='if'
+ t_0
+ id@(1:4-1:7)='iff'
+ t_0
+ id@(1:8-1:11)='iff'
+ t_0
+ id@(1:12-1:13)='i'
+ t_0
+ id@(1:14-1:15)='f'
+ t_0
+ id@(2:1-2:6)='if50i'
+ t_1
+ if@(3:1-3:3)='if'
+ t_2
+ int@(3:4-3:6)='50'
+ t_0
+ id@(3:7-3:8)='i'
+ t_2
+ int@(4:1-4:3)='50'
+ t_3
+ float@(5:1-5:6)='50.50'
+ t_2
+ int@(6:1-6:3)='50'
+ t_4
+ dot@(6:3-6:4)='.'
+ t_4
+ dot@(7:1-7:2)='.'
+ t_2
+ int@(7:2-7:4)='50'
+ t_2
+ int@(8:1-8:3)='50'
+ t_4
+ dot@(8:3-8:4)='.'
+ t_0
+ id@(8:4-8:5)='f'
+ t_0
+ id@(9:1-9:2)='f'
+ t_4
+ dot@(9:2-9:3)='.'
+ t_2
+ int@(9:3-9:5)='50'
+ Eof@(10:1-10:1)=''
--- /dev/null
+Start
+ p
+ Nodes
+ t_2
+ if@(1:1-1:3)='if'
+ t_0
+ identifier@(1:4-1:7)='iff'
+ t_0
+ identifier@(1:8-1:11)='iff'
+ t_0
+ identifier@(1:12-1:13)='i'
+ t_0
+ identifier@(1:14-1:15)='f'
+ t_0
+ identifier@(2:1-2:6)='if50i'
+ t_2
+ if@(3:1-3:3)='if'
+ t_1
+ comma@(3:3-3:4)=','
+ t_0
+ identifier@(3:4-3:5)='i'
+ Eof@(4:1-4:1)=''
--- /dev/null
+NLexerError@(1:1-1:1)='5'
+Nodes
+ p
--- /dev/null
+Start
+ p
+ Nodes
+ t_1
+ b@(1:1-1:34)='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'
+ Eof@(2:1-2:1)=''
--- /dev/null
+Start
+ p
+ Nodes
+ t_0
+ a@(1:1-1:2)='a'
+ t_0
+ a@(1:2-1:3)='a'
+ t_0
+ a@(1:3-1:4)='a'
+ t_0
+ a@(1:4-1:5)='a'
+ t_0
+ a@(1:5-1:6)='a'
+ t_0
+ a@(1:6-1:7)='a'
+ t_0
+ a@(1:7-1:8)='a'
+ t_0
+ a@(1:8-1:9)='a'
+ t_0
+ a@(1:9-1:10)='a'
+ t_0
+ a@(1:10-1:11)='a'
+ t_0
+ a@(1:11-1:12)='a'
+ t_0
+ a@(1:12-1:13)='a'
+ t_0
+ a@(1:13-1:14)='a'
+ t_0
+ a@(1:14-1:15)='a'
+ t_0
+ a@(1:15-1:16)='a'
+ t_0
+ a@(1:16-1:17)='a'
+ t_0
+ a@(1:17-1:18)='a'
+ t_0
+ a@(1:18-1:19)='a'
+ t_0
+ a@(1:19-1:20)='a'
+ t_0
+ a@(1:20-1:21)='a'
+ t_0
+ a@(1:21-1:22)='a'
+ t_0
+ a@(1:22-1:23)='a'
+ t_0
+ a@(1:23-1:24)='a'
+ t_0
+ a@(1:24-1:25)='a'
+ t_0
+ a@(1:25-1:26)='a'
+ t_0
+ a@(1:26-1:27)='a'
+ t_0
+ a@(1:27-1:28)='a'
+ t_0
+ a@(1:28-1:29)='a'
+ t_0
+ a@(1:29-1:30)='a'
+ t_0
+ a@(1:30-1:31)='a'
+ t_0
+ a@(1:31-1:32)='a'
+ t_0
+ a@(1:32-1:33)='a'
+ Eof@(2:1-2:1)=''
--- /dev/null
+Start
+ p
+ Nodes
+ t_0
+ identifier@(1:1-1:2)='i'
+ t_0
+ identifier@(1:3-1:4)='f'
+ t_1
+ if@(2:1-2:3)='if'
+ t_0
+ identifier@(3:1-3:4)='ify'
+ Eof@(4:1-4:1)=''
--- /dev/null
+T_Identifier@1,1:z10
+T_Hexinteger@2,1:00ff1
+T_Identifier@3,1:fff
+T_Hexinteger@4,1:00f
+T_Identifier@4,4:g1
+TEnd@5,1:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(6:34-6:34)='E'
+NLexerError@(6:34-6:34)='E'
--- /dev/null
+* parse error in state grammar on token NLexerError@(16:1-16:2)='Pr'
+NLexerError@(16:1-16:2)='Pr'
--- /dev/null
+Start
+ exps
+ Nodes
+ exp_add
+ exp_int
+ int@(1:1-1:2)='5'
+ '+'@(1:3-1:4)
+ exp_mul
+ exp_int
+ int@(1:5-1:6)='4'
+ '*'@(1:7-1:8)
+ exp_int
+ int@(1:9-1:10)='2'
+ exp_min
+ exp_int
+ int@(2:1-2:2)='5'
+ '-'@(2:3-2:4)
+ exp_min
+ exp_int
+ int@(2:5-2:6)='4'
+ '-'@(2:7-2:8)
+ exp_int
+ int@(2:9-2:10)='2'
+ exp_add
+ exp_int
+ int@(3:1-3:2)='5'
+ '+'@(3:3-3:4)
+ exp_add
+ exp_int
+ int@(3:5-3:6)='4'
+ '+'@(3:7-3:8)
+ exp_int
+ int@(3:9-3:10)='2'
+ exp_mul
+ exp_par
+ '('@(4:1-4:2)
+ exp_add
+ exp_int
+ int@(4:2-4:3)='5'
+ '+'@(4:4-4:5)
+ exp_int
+ int@(4:6-4:7)='4'
+ ')'@(4:7-4:8)
+ '*'@(4:9-4:10)
+ exp_int
+ int@(4:11-4:12)='2'
+ exp_add
+ exp_int
+ int@(5:1-5:2)='5'
+ '+'@(5:3-5:4)
+ exp_par
+ '('@(5:5-5:6)
+ exp_mul
+ exp_int
+ int@(5:6-5:7)='4'
+ '*'@(5:8-5:9)
+ exp_int
+ int@(5:10-5:11)='2'
+ ')'@(5:11-5:12)
+ Eof@(6:1-6:1)=''
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Start
+ exps
+ Nodes
+ exp_add
+ exp_factor
+ factor_term
+ term_int
+ int@(1:1-1:2)='5'
+ '+'@(1:3-1:4)
+ factor_mul
+ factor_term
+ term_int
+ int@(1:5-1:6)='4'
+ '*'@(1:7-1:8)
+ term_int
+ int@(1:9-1:10)='2'
+ exp_min
+ exp_min
+ exp_factor
+ factor_term
+ term_int
+ int@(2:1-2:2)='5'
+ '-'@(2:3-2:4)
+ factor_term
+ term_int
+ int@(2:5-2:6)='4'
+ '-'@(2:7-2:8)
+ factor_term
+ term_int
+ int@(2:9-2:10)='2'
+ exp_add
+ exp_add
+ exp_factor
+ factor_term
+ term_int
+ int@(3:1-3:2)='5'
+ '+'@(3:3-3:4)
+ factor_term
+ term_int
+ int@(3:5-3:6)='4'
+ '+'@(3:7-3:8)
+ factor_term
+ term_int
+ int@(3:9-3:10)='2'
+ exp_factor
+ factor_mul
+ factor_term
+ term_par
+ '('@(4:1-4:2)
+ exp_add
+ exp_factor
+ factor_term
+ term_int
+ int@(4:2-4:3)='5'
+ '+'@(4:4-4:5)
+ factor_term
+ term_int
+ int@(4:6-4:7)='4'
+ ')'@(4:7-4:8)
+ '*'@(4:9-4:10)
+ term_int
+ int@(4:11-4:12)='2'
+ exp_add
+ exp_factor
+ factor_term
+ term_int
+ int@(5:1-5:2)='5'
+ '+'@(5:3-5:4)
+ factor_term
+ term_par
+ '('@(5:5-5:6)
+ exp_factor
+ factor_mul
+ factor_term
+ term_int
+ int@(5:6-5:7)='4'
+ '*'@(5:8-5:9)
+ term_int
+ int@(5:10-5:11)='2'
+ ')'@(5:11-5:12)
+ Eof@(6:1-6:1)=''
--- /dev/null
+Start
+ forme_cercle
+ 'centre'@(1:1-1:7)
+ point
+ '('@(1:8-1:9)
+ long
+ nombre@(1:9-1:10)='5'
+ unite_2
+ 'pt'@(1:10-1:12)
+ ','@(1:12-1:13)
+ long
+ nombre@(1:14-1:15)='1'
+ unite_0
+ 'cm'@(1:15-1:17)
+ ')'@(1:17-1:18)
+ 'rayon'@(1:19-1:24)
+ long
+ nombre@(1:25-1:27)='10'
+ unite_3
+ 'px'@(1:28-1:30)
+ Eof@(2:1-2:1)=''
--- /dev/null
+Start
+ item_par
+ '('@(1:1-1:2)
+ list_many
+ item_id
+ id@(1:2-1:5)='ida'
+ list_many
+ item_par
+ '('@(1:5-1:6)
+ list_many
+ item_id
+ id@(1:6-1:9)='idb'
+ list_one
+ item_id
+ id@(1:10-1:13)='idc'
+ ')'@(1:13-1:14)
+ list_many
+ item_par
+ '('@(1:14-1:15)
+ list_many
+ item_id
+ id@(1:15-1:18)='idd'
+ list_many
+ item_id
+ id@(1:19-1:22)='ide'
+ list_one
+ item_par
+ '('@(1:22-1:23)
+ list_one
+ item_id
+ id@(1:23-1:26)='idf'
+ ')'@(1:26-1:27)
+ ')'@(1:27-1:28)
+ list_one
+ item_nil
+ '('@(1:28-1:29)
+ ')'@(1:29-1:30)
+ ')'@(1:30-1:31)
+ Eof@(2:1-2:1)=''
--- /dev/null
+Start
+ list_many
+ id@(1:1-1:4)='ida'
+ list_many
+ id@(1:5-1:8)='idb'
+ list_one
+ id@(1:9-1:12)='idc'
+ Eof@(2:1-2:1)=''
--- /dev/null
+Start
+ par_item
+ '('@(1:1-1:2)
+ par_item
+ '('@(1:2-1:3)
+ par_empty
+ '('@(1:3-1:4)
+ ')'@(1:4-1:5)
+ ')'@(1:5-1:6)
+ ')'@(1:6-1:7)
+ Eof@(2:1-2:1)=''
--- /dev/null
+T_Int@1,1:1
+TAnonymous@1,2:+
+T_Int@1,3:2
+TAnonymous@1,4:*
+TAnonymous@1,5:(
+T_Int@1,6:3
+TAnonymous@1,7:+
+T_Int@1,8:4
+TAnonymous@1,9:)
+TAnonymous@1,10:+
+T_Int@1,11:5
+TEnd@2,1:
--- /dev/null
+* parse error in state grammar on token NLexerError@(15:7-15:8)='Pr'
+NLexerError@(15:7-15:8)='Pr'
--- /dev/null
+NLexerError@(5:38-5:38)='E'
+Nodes
+ prods_many
+ prods_many
+ prods_many
+ prods_one
+ prod
+ id@(1:1-1:6)='prods'
+ '='@(1:7-1:8)
+ alts_many
+ alts_one
+ alt_0
+ altid@(1:9-1:16)='{many:}'
+ atoms_many
+ atoms_many
+ atoms_none
+ atom_id
+ id@(1:17-1:22)='prods'
+ atom_id
+ id@(1:23-1:27)='prod'
+ '|'@(1:29-1:30)
+ alt_0
+ altid@(1:31-1:37)='{one:}'
+ atoms_many
+ atoms_none
+ atom_id
+ id@(1:38-1:42)='prod'
+ ';'@(1:43-1:44)
+ prod
+ id@(2:1-2:5)='prod'
+ '='@(2:6-2:7)
+ alts_one
+ alt_1
+ atoms_many
+ atoms_many
+ atoms_many
+ atoms_many
+ atoms_none
+ 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)
+ alts_many
+ alts_one
+ alt_0
+ altid@(3:8-3:15)='{many:}'
+ atoms_many
+ atoms_many
+ atoms_many
+ atoms_none
+ 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)
+ alt_0
+ altid@(3:31-3:37)='{one:}'
+ atoms_many
+ atoms_none
+ atom_id
+ id@(3:38-3:41)='alt'
+ ';'@(3:42-3:43)
+ prod
+ id@(4:1-4:4)='alt'
+ '='@(4:5-4:6)
+ alts_many
+ alts_one
+ alt_1
+ atoms_many
+ atoms_many
+ atoms_none
+ atom_id
+ id@(4:7-4:12)='altid'
+ atom_id
+ id@(4:13-4:18)='atoms'
+ '|'@(4:19-4:20)
+ alt_1
+ atoms_many
+ atoms_none
+ atom_id
+ id@(4:21-4:26)='atoms'
+ ';'@(4:27-4:28)
+ id@(5:1-5:6)='atoms'
+ '='@(5:7-5:8)
+ alts_many
+ alts_one
+ alt_0
+ altid@(5:9-5:16)='{many:}'
+ atoms_many
+ atoms_many
+ atoms_none
+ atom_id
+ id@(5:17-5:22)='atoms'
+ atom_id
+ id@(5:23-5:27)='atom'
+ '|'@(5:28-5:29)
+ alt_0
+ altid@(5:30-5:37)='{none:}'
+ atoms_none
--- /dev/null
+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'.
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Parser' id '=' alts on token '('@(11:15-11:16)
+NParserError@(11:15-11:16)='('
--- /dev/null
+Start
+ prog
+ Nodes
+ 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
+ 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)=''
--- /dev/null
+Start
+ prog
+ Nodes
+ 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
+ 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)=''
--- /dev/null
+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:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Parser' id '=' alts on token '('@(20:8-20:9)
+NParserError@(20:8-20:9)='('
--- /dev/null
+Start
+ prog
+ Nodes
+ 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
+ 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)=''
--- /dev/null
+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 ' '.
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Parser' id '=' alts on token '('@(8:18-8:19)
+NParserError@(8:18-8:19)='('
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(9:16-9:16)='S'
+NLexerError@(9:16-9:16)='S'
--- /dev/null
+Not Yet Implemented Error: '-' only works on single char
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' '(' re on token NLexerError@(11:26-11:27)='Lo'
+NLexerError@(11:26-11:27)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' '(' re on token NLexerError@(12:27-12:27)='E'
+NLexerError@(12:27-12:27)='E'
--- /dev/null
+ERROR: Conflicting tokens: com1 com2
--- /dev/null
+ERROR: Empty tokens a
--- /dev/null
+Error: circular regular expression a.
--- /dev/null
+Error: circular regular expression a.
--- /dev/null
+Error: unknown name fail
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(10:16-10:17)='Lo'
+NLexerError@(10:16-10:17)='Lo'
--- /dev/null
+Error: unknown name fail
--- /dev/null
+Error: unknown name a
--- /dev/null
+T_Anb@1,1:a
+T_A@1,2:a
+T_B@1,3:b
+T_B@1,4:b
+TEnd@2,1:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(4:9-4:10)='Lo'
+NLexerError@(4:9-4:10)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(4:9-4:10)='Lo'
+NLexerError@(4:9-4:10)='Lo'
--- /dev/null
+T_B@1,1:ab
+T_B@1,3:ab
+T_A@1,5:a
+1,6. Syntax error: Unexpected character 'c'.
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(3:10-3:11)='Lo'
+NLexerError@(3:10-3:11)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(3:9-3:10)='Lo'
+NLexerError@(3:9-3:10)='Lo'
--- /dev/null
+NParserError@(1:2-1:6)='abac'
+Nodes
+ t_0
+ a@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:6-1:7)='a'
+Nodes
+ t_1
+ b@(1:1-1:6)='aabac'
--- /dev/null
+NParserError@(1:2-1:6)='abac'
+Nodes
+ t_0
+ a@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_1
+ xb@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+ERROR: Conflicting tokens: xa xb
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' '(' re on token NLexerError@(6:18-6:19)='Lo'
+NLexerError@(6:18-6:19)='Lo'
--- /dev/null
+NParserError@(1:2-1:3)='a'
+Nodes
+ t_0
+ xa@(1:1-1:2)='a'
--- /dev/null
+Error: unknown name a
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(6:22-6:23)='Lo'
+NLexerError@(6:22-6:23)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(6:22-6:23)='Lo'
+NLexerError@(6:22-6:23)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(6:22-6:23)='Lo'
+NLexerError@(6:22-6:23)='Lo'
--- /dev/null
+T_Ab@1,1:ab
+T_L@1,3:a
+T_Ab@1,4:ab
+T_L@1,6:a
+TEnd@2,1:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' re on token NLexerError@(6:22-6:23)='Lo'
+NLexerError@(6:22-6:23)='Lo'
--- /dev/null
+T_Sa@1,1:a
+T_A@1,2:ab
+TEnd@2,1:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(4:6-4:7)='Lo'
+NLexerError@(4:6-4:7)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(4:6-4:7)='Lo'
+NLexerError@(4:6-4:7)='Lo'
--- /dev/null
+T_Sa@1,1:a
+T_La@1,2:ab
+TEnd@2,1:
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(4:6-4:7)='Lo'
+NLexerError@(4:6-4:7)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(4:6-4:7)='Lo'
+NLexerError@(4:6-4:7)='Lo'
--- /dev/null
+* parse error in state 'Grammar' id ';' 'Lexer' id '=' on token NLexerError@(4:6-4:7)='Lo'
+NLexerError@(4:6-4:7)='Lo'
--- /dev/null
+ERROR: Conflicting tokens: a b
--- /dev/null
+ERROR: Empty tokens b a
--- /dev/null
+T_Identifier@1,1:i
+TAnonymous@1,3:if
+T_Identifier@1,6:iff
+T_Identifier@1,10:ifff
+T_Decimal@2,1:70
+T_Zero@3,1:zero
+T_Hex@4,1:0xFFff
+T_IpAddress@5,1:192.168.0.1
+TEnd@6,1:
--- /dev/null
+T_Decimal@1,1:10
+1,3. Syntax error: Unexpected character '.'.
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Start
+ p
+ 'a'@(1:1-1:2)
+ 'a'@(1:2-1:3)
+ Eof@(1:3-1:3)=''
--- /dev/null
+Start
+ p
+ 'a'@(1:1-1:2)
+ Nodes
+ q
+ 'b'@(1:2-1:3)
+ Nodes
+ r
+ 'c'@(1:3-1:4)
+ 'c'@(1:4-1:5)
+ 'b'@(1:5-1:6)
+ q
+ 'b'@(1:6-1:7)
+ Nodes
+ r
+ 'c'@(1:7-1:8)
+ 'c'@(1:8-1:9)
+ r
+ 'c'@(1:9-1:10)
+ s
+ 'd'@(1:10-1:11)
+ 'c'@(1:11-1:12)
+ 'b'@(1:12-1:13)
+ 'a'@(1:13-1:14)
+ Eof@(1:14-1:14)=''
--- /dev/null
+NParserError@(1:3-1:4)='b'
+Nodes
+ 'a'@(1:1-1:2)
+ 'b'@(1:2-1:3)
--- /dev/null
+NParserError@(1:5-1:6)='d'
+Nodes
+ 'a'@(1:1-1:2)
+ 'b'@(1:2-1:3)
+ 'c'@(1:3-1:4)
+ s
+ 'd'@(1:4-1:5)
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Error: there is conflicts
--- /dev/null
+Error: there is conflicts
--- /dev/null
+'x'@(1:1-1:2)
+'x'@(1:2-1:3)
+'y'@(1:3-1:4)
+'y'@(1:4-1:5)
+'x'@(1:5-1:6)
+'y'@(1:6-1:7)
+NLexerError@(1:7-1:7)='\n'
--- /dev/null
+'a'@(1:1-1:2)
+'b'@(1:2-1:3)
+'x'@(1:3-1:4)
+'y'@(1:4-1:5)
+'c'@(1:5-1:6)
+'d'@(1:6-1:7)
+NLexerError@(1:7-1:7)='\n'
--- /dev/null
+#!/bin/bash
+
+# This file is part of Nit ( http://nitlanguage.org ).
+#
+# See the NOTICE file distributed with this work for copyright information.
+#
+# 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.
+
+# This program is used to perform regression tests of nitcc.
+
+NITCC=../nitcc
+NITC=../../../bin/nitc
+
+mkdir alt out 2>/dev/null
+
+if test "x$#" = "x0"
+then
+ echo usage: `basename $0` [-v] grammar-files*
+ exit
+fi
+
+
+verbose=false
+stop=false
+while [ $stop = false ]; do
+ case $1 in
+ -v) verbose=true; shift;;
+ *) stop=true
+ esac
+done
+
+# empty tap output
+tap="tap.output"
+>"$tap"
+tapcount=0
+
+differ() {
+ tapcount=$((tapcount + 1))
+ r="$1"
+ if test \! -f "sav/$r"
+ then
+ if test -s "out/$r"
+ then
+ echo "[***no sav***] cp 'out/$r' sav/"
+ test $verbose = true && { cat "out/$r" ; echo ; }
+ echo >>"$tap" "not ok $tapcount - $name # TODO no sav"
+ else
+ echo "[0K]"
+ echo >>"$tap" "ok $tapcount - $name"
+ fi
+ elif diff "sav/$r" "out/$r" >/dev/null
+ then
+ echo "[OK]"
+ echo >>"$tap" "ok $tapcount - $name"
+ else
+ echo "[******failed******] diff -u {sav,out}/$r"
+ test $verbose = true && { diff -u "sav/$r" "out/$r" ; echo ; }
+ echo >>"$tap" "not ok $tapcount - $name"
+ fi
+}
+
+for f in "$@"
+do
+ for a in "$f" `./alterner.pl $f`
+ do
+ cla=""
+ langname=""
+ echo -n "* $a: "
+ bn=`basename "$a" .sablecc`
+ res="$bn".res
+ $NITCC "$a" >"out/$bn.nitcc.log" 2>&1
+ if test $? != 0
+ then
+ echo -n "!nitcc "
+ else
+ t=`grep -o '[^ ]*_test_parser' < "out/$bn.nitcc.log"`
+ if test -f "${t}.nit"
+ then
+ echo -n ". "
+ $NITC -I .. "${t}.nit" >/dev/null 2>&1
+ if test $? != 0
+ then
+ echo -n "!nitc "
+ else
+ echo -n ". "
+ cla="${t}"
+ langname="${t/_test_parser}"
+ fi
+ else
+ echo -n "!res "
+ echo
+ tapcount=$((tapcount + 1))
+ echo >>"$tap" "not ok $tapcount - $a # TODO no res"
+ continue
+ fi
+ fi
+
+ grep -i "error" "out/$bn.nitcc.log" > "out/$res"
+ name="$a"
+ differ $res
+
+ if test \! -z $cla
+ then
+ bf=`basename "$f" .sablecc`
+ for i in "$bf".input*
+ do
+ test -f $i || { echo " - no input: $i" ; continue ; }
+ ni=${i#$bf}
+ resi="$bn$ni".res
+ rm "$langname.ast.out" 2>/dev/null
+ echo -n " - $i: "
+ ./"$cla" "$i" > "out/$bn.$ni.log"
+ if test $? != 0
+ then
+ echo -n "!run "
+ else
+ echo -n ". "
+ fi
+
+ cp "$langname.ast.out" "out/$resi"
+ name="$a $i"
+ differ "$resi"
+ done
+ fi
+ done
+done
+
+echo >>"$tap" "1..$tapcount"
+#prove --formatter=TAP::Formatter::JUnit /bin/cat :: tap.output > tap.xml
--- /dev/null
+Grammar trans;
+
+Parser
+
+a = a a2 | a2;
+a2 {-> a} = 'x' | a3 {-> a3};
+a3 {-> a} = 'y';
--- /dev/null
+Grammar trans_inline;
+
+Parser
+
+p = 'a' 'b' q_inline 'c' 'd' ;
+q_inline = 'x' 'y' ;