Prepare default constructors
[nit.git] / src / parser / xss / parser.xss
1 /* This file is part of NIT ( http://www.nitlanguage.org ).
2  *
3  * Copyright 2008 Jean Privat <jean@pryen.org>
4  * Based on algorithms developped for ( http://www.sablecc.org/ ).
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 $ template make_parser()
20
21 # State of the parser automata as stored in the parser stack.
22 private class State
23         # The internal state number
24         readable writable attr _state: Int
25
26         # The node stored with the state in the stack
27         readable writable attr _nodes: Object 
28
29         init(state: Int, nodes: Object)
30         do
31                 _state = state
32                 _nodes = nodes
33         end
34 end
35
36 class Parser
37 special ParserTable
38         # Associated lexer
39         attr _lexer: Lexer
40
41         # Stack of pushed states and productions
42         attr _stack: Array[State]
43
44         # Position in the stack
45         attr _stack_pos: Int
46
47         # Create a new parser based on a given lexer
48         init(lexer: Lexer)
49         do
50                 _lexer = lexer
51                 _stack = new Array[State]
52                 _stack_pos = -1
53                 build_goto_table
54                 build_action_table
55                 build_reduce_table
56         end
57
58         # Do a transition in the automata
59         private meth go_to(index: Int): Int
60         do
61                 var state = state
62                 var table = _goto_table[index]
63                 var low = 1
64                 var high = table.length/2 - 1
65
66                 while low <= high do
67                         var middle = (low + high) / 2
68                         var subindex = middle * 2
69
70                         if state < table[subindex] then
71                                 high = middle - 1
72                         else if state > table[subindex] then
73                                 low = middle + 1
74                         else
75                                 return table[subindex + 1]
76                         end
77                 end
78
79                 return table[1] # Default value
80         end
81
82         # Push someting in the state stack
83         private meth push(numstate: Int, list_node: Object)
84         do
85                 var pos = _stack_pos + 1
86                 _stack_pos = pos
87                 if pos < _stack.length then
88                         var state = _stack[pos]
89                         state.state = numstate
90                         state.nodes = list_node
91                 else
92                         _stack.push(new State(numstate, list_node))
93                 end
94         end
95
96         # The current state
97         private meth state: Int
98         do
99                 return _stack[_stack_pos].state
100         end
101
102         # Pop something from the stack state
103         private meth pop: Object
104         do
105                 var res = _stack[_stack_pos].nodes
106                 _stack_pos = _stack_pos -1
107                 return res
108         end
109
110         # Build and return a full AST.
111         meth parse: Start
112         do
113                 push(0, null)
114
115                 var ign: List[Token] = null
116                 var lexer = _lexer
117                 while true do
118                         var token = lexer.peek
119                         var last_pos = token.pos
120                         var last_line = token.line
121
122                         if token isa PError then
123                                 assert token isa PError
124                                 return new Start(null, token)
125                         end
126
127                         var index = token.parser_index
128                         var table = _action_table[state]
129                         var action_type = table[1]
130                         var action_value = table[2]
131
132                         var low = 1
133                         var high = table.length/3 - 1
134
135                         while low <= high do
136                                 var middle = (low + high) / 2
137                                 var subindex = middle * 3
138
139                                 if index < table[subindex] then
140                                         high = middle - 1
141                                 else if index > table[subindex] then
142                                         low = middle + 1
143                                 else
144                                         action_type = table[subindex + 1]
145                                         action_value = table[subindex + 2]
146                                         high = low -1 # break
147                                 end
148                         end
149
150                         if action_type == 0 then # SHIFT
151                                 push(action_value, lexer.next)
152                         else if action_type == 1 then # REDUCE
153                                 _reduce_table[action_value].action(self)
154                         else if action_type == 2 then # ACCEPT
155                                 var node2 = lexer.next
156                                 assert node2 isa EOF
157                                 var node1 = pop
158                                 assert node1 isa ${/parser/prods/prod/@ename}
159                                 var node = new Start(node1, node2)
160                                 (new SearchTokensVisitor).visit(node)
161                                 return node
162                         else if action_type == 3 then # ERROR
163                                 var node2 = new PError.init_error(lexer.filename, last_line, last_pos, error_messages[errors[action_value]])
164                                 var node = new Start(null, node2)
165                                 return node
166                         end
167                 end
168                 return null
169         end
170
171         attr _reduce_table: Array[ReduceAction]
172         private meth build_reduce_table
173         do
174                 _reduce_table = new Array[ReduceAction].with(
175 $ foreach {rules/rule}
176                         new ReduceAction@index[-sep ','-]
177 $ end foreach
178                 )
179         end
180 end
181
182 # Find first and last tokens of production nodes
183 private class SearchTokensVisitor
184 special Visitor
185         attr _untokenned_nodes: Array[Prod]
186         attr _last_token: Token
187         redef meth visit(n: PNode)
188         do
189                 if n isa Token then
190                         assert n isa Token
191                         _last_token = n
192                         for no in _untokenned_nodes do
193                                 no.first_token = n
194                         end
195                         _untokenned_nodes.clear
196                 else
197                         assert n isa Prod
198                         _untokenned_nodes.add(n)
199                         n.visit_all(self)
200                         n.last_token = _last_token
201                 end
202         end
203         init
204         do
205                 _untokenned_nodes = new Array[Prod]
206         end
207 end
208
209 # Each reduca action has its own class, this one is the root of the hierarchy.
210 private abstract class ReduceAction
211         meth action(p: Parser) is abstract
212 end
213
214 $ foreach {rules/rule}
215 private class ReduceAction@index
216 special ReduceAction
217         redef meth action(p: Parser)
218         do
219                                         var node_list: Object = null
220 $   foreach {action}
221 $   choose
222 $     when {@cmd='POP'}
223                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = p.pop
224 $     end
225 $     when {@cmd='FETCHLIST'}
226                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} 
227                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa Array[Object]
228 $     end
229 $     when {@cmd='FETCHNODE'}
230                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
231                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa @etype
232 $     end
233 $     when {@cmd='ADDNODE'}
234                                         if ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
235                                                 ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.add(${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
236                                         end
237 $     end
238 $     when {@cmd='ADDLIST'}
239                                         if ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
240                                                 if ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.is_empty then
241                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
242                                                 else
243                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.append(${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
244                                                 end
245                                         end
246 $     end
247 $     when {@cmd='MAKELIST'}
248                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = new Array[Object]
249 $     end
250 $     when {@cmd='MAKENODE'}
251                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = new @etype.init_${translate(@etype,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}(
252 $       foreach {arg}
253 $           if @null
254                                                 null[-sep ','-]
255 $           else
256                                                 ${translate(.,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}[-sep ','-]
257 $           end
258 $       end foreach
259                                         )
260 $     end
261 $     when {@cmd='RETURNNODE'}
262 $       if @null
263                                         node_list = null
264 $       else
265                                         node_list = ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
266 $       end
267 $     end
268 $     when {@cmd='RETURNLIST'}
269                                         node_list = ${translate(@list,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
270 $     end
271 $   end choose
272 $   end foreach
273                                         p.push(p.go_to(@leftside), node_list)
274         end
275 init do end
276 end
277 $ end foreach
278 $ end template
279
280 $ template make_parser_tables()
281 # Parser that build a full AST
282 abstract class ParserTable
283         attr _action_table: Array[Array[Int]] = null
284         private meth build_action_table
285         do
286                 _action_table = once [ 
287 $ foreach {parser_data/action_table/row}
288                         action_table_row${position()}[-sep ','-]
289 $ end foreach
290                 ]
291         end
292
293 $ foreach {parser_data/action_table/row}
294         private meth action_table_row${position()}: Array[Int]
295         do
296                 return [
297 $   foreach {action}
298                                 @from, @action, @to [-sep ','-]
299 $   end foreach
300                         ]
301         end
302 $ end foreach
303
304         attr _goto_table: Array[Array[Int]] = null
305         private meth build_goto_table
306         do
307                 _goto_table = once [ 
308 $ foreach {parser_data/goto_table/row}
309                         [
310 $   foreach {goto}
311                                 @from, @to [-sep ','-]
312 $   end foreach
313                         ] [-sep ','-]
314 $ end foreach
315                 ]
316         end
317
318         private meth error_messages: Array[String]
319         do
320                 return once [
321 $ foreach {parser_data/error_messages/msg}
322                         "${sablecc:string2escaped_unicode(.)}" [-sep ','-]
323 $ end
324                 ]
325         end
326
327         private meth errors: Array[Int]
328         do
329                 return once [
330                         [-foreach {parser_data/errors/i}-]${.} [-sep ','-] [-end-]
331                 ]
332         end
333 end
334 $ end template