-
Notifications
You must be signed in to change notification settings - Fork 3
/
pegasus_crystal_template.ecr
130 lines (105 loc) · 3.2 KB
/
pegasus_crystal_template.ecr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module Pegasus::Generated
MAX_TERMINAL = <%= @max_terminal %>
LEX_FINAL_TABLE = [ <% @lex_final_table.each do |final| %> <%= final %>_i64, <% end %> ]
LEX_STATE_TABLE = [ <% @lex_state_table.each do |state| %>
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %>
]
PARSE_ACTION_TABLE = [ <% @parse_action_table.each do |state| %>
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %>
]
PARSE_STATE_TABLE = [ <% @parse_state_table.each do |state| %>
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %>
]
ITEMS = <% if @items.size == 0 %> [] of Tuple(Int64, Int64) <% else %> [ <% @items.each do |item| %>
{ <%= item.head.id %>_i64, <%= item.body.size %>_i64 },<%- end %>
] <%- end %>
extend self
abstract class Tree
abstract def table_index
end
class NonterminalTree < Tree
getter nonterminal_id : Int64
getter children : Array(Tree)
def initialize(@nonterminal_id, @children = [] of Tree)
end
def table_index
nonterminal_id + 1 + MAX_TERMINAL + 1
end
def name
case nonterminal_id<% @nonterminals.each do |nt| %>
when <%= nt[1].id %>_i64
<%= nt[0].dump -%>
<%- end %>
else
"???"
end
end
end
class TerminalTree < Tree
getter terminal_id : Int64
getter string : String
def initialize(@terminal_id, @string)
end
def table_index
terminal_id + 1
end
end
class Token
getter terminal_id : Int64
getter string : String
def initialize(@terminal_id, @string)
end
end
def lex(string)
index = 0
tokens = [] of Token
bytes = string.bytes
while index < bytes.size
start_index = index
last_match_index = -1
last_pattern = -1_i64
state = 1
while index < bytes.size
state = LEX_STATE_TABLE[state][bytes[index]]
id = LEX_FINAL_TABLE[state]
break if state == 0
next if id == 0
last_match_index = index
last_pattern = id
index += 1
end
raise "Invalid character at position #{index}" if last_match_index == -1
tokens << Token.new(last_pattern - 1, string[start_index..last_match_index])
end
return tokens
end
def parse(tokens)
tree_stack = [ ] of Tree
state_stack = [ 1_i64 ]
index = 0
while true
break if tree_stack.last?.try(&.as?(NonterminalTree)).try(&.nonterminal_id) == 0
token = tokens[index]?
action = PARSE_ACTION_TABLE[state_stack.last][token.try(&.terminal_id.+(1)) || 0_i64]
raise "Invalid token #{token.try &.string}" if action == -1
if action == 0
raise "Unexpected end of file" unless token
tree_stack << TerminalTree.new token.terminal_id, token.string
index += 1
else
item = ITEMS[action - 1]
tree = NonterminalTree.new item[0]
item[1].times do
tree.children.insert 0, tree_stack.pop
state_stack.pop
end
tree_stack << tree
end
state_stack << PARSE_STATE_TABLE[state_stack.last][tree_stack.last.table_index]
end
return tree_stack.last
end
def process(string)
parse(lex(string))
end
end