end
class Parser
-special ParserTable
+ super TablesCapable
# Associated lexer
var _lexer: Lexer
_lexer = lexer
_stack = new Array[State]
_stack_pos = -1
- build_goto_table
- build_action_table
build_reduce_table
end
private fun go_to(index: Int): Int
do
var state = state
- var table = _goto_table[index]
var low = 1
- var high = table.length/2 - 1
+ var high = parser_goto(index, 0) - 1
while low <= high do
var middle = (low + high) / 2
- var subindex = middle * 2
+ var subindex = middle * 2 + 1 # +1 because parser_goto(index, 0) is the length
- if state < table[subindex] then
+ var goal = parser_goto(index, subindex)
+ if state < goal then
high = middle - 1
- else if state > table[subindex] then
+ else if state > goal then
low = middle + 1
else
- return table[subindex + 1]
+ return parser_goto(index, subindex+1)
end
end
- return table[1] # Default value
+ return parser_goto(index, 2) # Default value
end
# Push someting in the state stack
return new Start(null, token)
end
+ var state = self.state
var index = token.parser_index
- var table = _action_table[state]
- var action_type = table[1]
- var action_value = table[2]
+ var action_type = parser_action(state, 2)
+ var action_value = parser_action(state, 3)
var low = 1
- var high = table.length/3 - 1
+ var high = parser_action(state, 0) - 1
while low <= high do
var middle = (low + high) / 2
- var subindex = middle * 3
+ var subindex = middle * 3 + 1 # +1 because parser_action(state, 0) is the length
- if index < table[subindex] then
+ var goal = parser_action(state, subindex)
+ if index < goal then
high = middle - 1
- else if index > table[subindex] then
+ else if index > goal then
low = middle + 1
else
- action_type = table[subindex + 1]
- action_value = table[subindex + 2]
- high = low -1 # break
+ action_type = parser_action(state, subindex+1)
+ action_value = parser_action(state, subindex+2)
+ break
end
end
(new ComputeProdLocationVisitor).enter_visit(node)
return node
else if action_type == 3 then # ERROR
- var node2 = new PError.init_error("Syntax error: unexpected token.", token.location)
+ var node2 = new PParserError.init_parser_error("Syntax error: unexpected {token}.", token.location, token)
var node = new Start(null, node2)
return node
end
- if false then break # FIXME remove once unreach loop exits are in c_src
end
- abort # FIXME remove once unreach loop exits are in c_src
end
var _reduce_table: Array[ReduceAction]
do
_reduce_table = new Array[ReduceAction].with_items(
$ foreach {rules/rule}
- new ReduceAction@index[-sep ','-]
+ new ReduceAction@index(@leftside)[-sep ','-]
$ end foreach
)
end
# Find location of production nodes
# Uses existing token locations to infer location of productions.
private class ComputeProdLocationVisitor
-special Visitor
+ super Visitor
# Currenlty visited productions that need a first token
var _need_first_prods: Array[Prod] = new Array[Prod]
# Location of the last visited token in the current production
var _last_location: nullable Location = null
- redef fun visit(n: nullable PNode)
+ redef fun visit(n: PNode)
do
- if n == null then
- return
- else if n isa Token then
+ if n isa Token then
var loc = n.location
_last_location = loc
# Add a first token to productions that need one
- for no in _need_first_prods do
- no._first_location = loc
+ if not _need_first_prods.is_empty then
+ for no in _need_first_prods do
+ no._first_location = loc
+ end
+ _need_first_prods.clear
end
- _need_first_prods.clear
# Find location for already visited epsilon production that need one
- for no in _need_after_epsilons do
- # Epsilon production that is in the middle of a non-epsilon production
- # The epsilon production has both a token before and after it
- var endl = loc
- var startl = no._last_location
- no.location = new Location(endl.file, startl.line_end, endl.line_start, startl.column_end, endl.column_start)
+ if not _need_after_epsilons.is_empty then
+ for no in _need_after_epsilons do
+ # Epsilon production that is in the middle of a non-epsilon production
+ # The epsilon production has both a token before and after it
+ var endl = loc
+ var startl = no._last_location
+ no.location = new Location(endl.file, startl.line_end, endl.line_start, startl.column_end, endl.column_start)
+ end
+ _need_after_epsilons.clear
end
- _need_after_epsilons.clear
else
assert n isa Prod
_need_first_prods.add(n)
n.location = new Location(startl.file, startl.line_start, endl.line_end, startl.column_start, endl.column_end)
- for no in _need_before_epsilons do
- # Epsilon production that starts the current non-epsilon production
- #var startl = n.location
- no.location = new Location(startl.file, startl.line_start, startl.line_start, startl.column_start, startl.column_start)
+ if not _need_before_epsilons.is_empty then
+ var loc = new Location(startl.file, startl.line_start, startl.line_start, startl.column_start, startl.column_start)
+ for no in _need_before_epsilons do
+ # Epsilon production that starts the current non-epsilon production
+ no.location = loc
+ end
+ _need_before_epsilons.clear
end
- _need_before_epsilons.clear
- for no in _need_after_epsilons do
- # Epsilon production that finishes the current non-epsilon production
- #var endl = n.location
- no.location = new Location(endl.file, endl.line_end, endl.line_end, endl.column_end, endl.column_end)
+ if not _need_after_epsilons.is_empty then
+ var loc = new Location(endl.file, endl.line_end, endl.line_end, endl.column_end, endl.column_end)
+ for no in _need_after_epsilons do
+ # Epsilon production that finishes the current non-epsilon production
+ no.location = loc
+ end
+ _need_after_epsilons.clear
end
- _need_after_epsilons.clear
else
# No first token means epsilon production (or "throw all my tokens" production)
# So, it must be located it later
l1.append(l2)
return l1
end
+ var _goto: Int
+ init(g: Int) do _goto = g
end
$ foreach {rules/rule}
private class ReduceAction@index
-special ReduceAction
+ super ReduceAction
redef fun action(p: Parser)
do
var node_list: nullable Object = null
var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = new Array[Object]
$ end
$ when {@cmd='MAKENODE'}
+$ if {count(arg)!=0}
var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}: nullable @etype = new @etype.init_${translate(@etype,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}(
$ foreach {arg}
$ if @null
$ end
$ end foreach
)
+$ else
+ var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}: nullable @etype = new @etype.init_${translate(@etype,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
+$ end
$ end
$ when {@cmd='RETURNNODE'}
$ if @null
$ end
$ end choose
$ end foreach
- p.push(p.go_to(@leftside), node_list)
+ p.push(p.go_to(_goto), node_list)
end
-init do end
end
$ end foreach
$ end template
-$ template make_parser_tables()
-# Parser that build a full AST
-abstract class ParserTable
- var _action_table: Array[Array[Int]]
- private fun build_action_table
- do
- _action_table = once [
+$ template make_parser_table()
$ foreach {parser_data/action_table/row}
- action_table_row${position()}[-sep ','-]
+static int parser_action_row${position()}[] = {
+ ${count(action)},
+$ foreach {action}
+ @from, @action, @to[-sep ','-]
+$ end foreach
+};
$ end foreach
- ]
- end
+const int* const parser_action_table[] = {
$ foreach {parser_data/action_table/row}
- private fun action_table_row${position()}: Array[Int]
- do
- return [
-$ foreach {action}
- @from, @action, @to[-sep ','-]
-$ end foreach
- ]
- end
+ parser_action_row${position()}[-sep ','-]
$ end foreach
+};
- var _goto_table: Array[Array[Int]]
- private fun build_goto_table
- do
- _goto_table = once [
$ foreach {parser_data/goto_table/row}
- [
+static int parser_goto_row${position()}[] = {
+ ${count(goto)},
$ foreach {goto}
- @from, @to[-sep ','-]
+ @from, @to[-sep ','-]
$ end foreach
- ][-sep ','-]
+};
$ end foreach
- ]
- end
- init do end
-end
+const int* const parser_goto_table[] = {
+$ foreach {parser_data/goto_table/row}
+ parser_goto_row${position()}[-sep ','-]
+$ end foreach
+};
$ end template