parser: remove spaces errors in generated files
[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 var _state: Int
25
26         # The node stored with the state in the stack
27         readable writable var _nodes: nullable Object
28
29         init(state: Int, nodes: nullable Object)
30         do
31                 _state = state
32                 _nodes = nodes
33         end
34 end
35
36 class Parser
37 special ParserTable
38         # Associated lexer
39         var _lexer: Lexer
40
41         # Stack of pushed states and productions
42         var _stack: Array[State]
43
44         # Position in the stack
45         var _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 fun 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 fun push(numstate: Int, list_node: nullable 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 fun state: Int
98         do
99                 return _stack[_stack_pos].state
100         end
101
102         # Pop something from the stack state
103         private fun pop: nullable 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         fun parse: Start
112         do
113                 push(0, null)
114
115                 var lexer = _lexer
116                 while true do
117                         var token = lexer.peek
118                         var last_pos = token.location.column_start
119                         var last_line = token.location.line_start
120
121                         if token isa PError then
122                                 return new Start(null, token)
123                         end
124
125                         var index = token.parser_index
126                         var table = _action_table[state]
127                         var action_type = table[1]
128                         var action_value = table[2]
129
130                         var low = 1
131                         var high = table.length/3 - 1
132
133                         while low <= high do
134                                 var middle = (low + high) / 2
135                                 var subindex = middle * 3
136
137                                 if index < table[subindex] then
138                                         high = middle - 1
139                                 else if index > table[subindex] then
140                                         low = middle + 1
141                                 else
142                                         action_type = table[subindex + 1]
143                                         action_value = table[subindex + 2]
144                                         high = low -1 # break
145                                 end
146                         end
147
148                         if action_type == 0 then # SHIFT
149                                 push(action_value, lexer.next)
150                         else if action_type == 1 then # REDUCE
151                                 _reduce_table[action_value].action(self)
152                         else if action_type == 2 then # ACCEPT
153                                 var node2 = lexer.next
154                                 assert node2 isa EOF
155                                 var node1 = pop
156                                 assert node1 isa ${/parser/prods/prod/@ename}
157                                 var node = new Start(node1, node2)
158                                 (new SearchTokensVisitor).enter_visit(node)
159                                 return node
160                         else if action_type == 3 then # ERROR
161                                 var location = new Location(lexer.filename, last_line, last_line, last_pos, last_pos)
162                                 var node2 = new PError.init_error(error_messages[errors[action_value]],location)
163                                 var node = new Start(null, node2)
164                                 return node
165                         end
166                 end
167                 abort
168         end
169
170         var _reduce_table: Array[ReduceAction]
171         private fun build_reduce_table
172         do
173                 _reduce_table = new Array[ReduceAction].with_items(
174 $ foreach {rules/rule}
175                         new ReduceAction@index[-sep ','-]
176 $ end foreach
177                 )
178         end
179 end
180
181 # Find first and last tokens of production nodes
182 private class SearchTokensVisitor
183 special Visitor
184         var _untokenned_nodes: Array[Prod]
185         var _last_token: nullable Token = null
186         redef fun visit(n: nullable PNode)
187         do
188                 if n == null then
189                         return
190                 else if n isa Token then
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
202                         if n.first_token != null then
203                                 var start_location = n.first_token.location
204                                 var end_location = _last_token.location
205
206                                 if start_location != null and end_location != null then
207                                         var file = end_location.file
208                                         var line_start = start_location.line_start
209                                         var line_end = end_location.line_end
210                                         var column_start = start_location.column_start
211                                         var column_end = end_location.column_end
212                                         n.location = new Location(file, line_start, line_end, column_start, column_end)
213                                 end
214                         end
215                 end
216         end
217         init
218         do
219                 _untokenned_nodes = new Array[Prod]
220         end
221 end
222
223 # Each reduca action has its own class, this one is the root of the hierarchy.
224 private abstract class ReduceAction
225         fun action(p: Parser) is abstract
226 end
227
228 $ foreach {rules/rule}
229 private class ReduceAction@index
230 special ReduceAction
231         redef fun action(p: Parser)
232         do
233                                         var node_list: nullable Object = null
234 $   foreach {action}
235 $   choose
236 $     when {@cmd='POP'}
237                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = p.pop
238 $     end
239 $     when {@cmd='FETCHLIST'}
240                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
241                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa Array[Object]
242 $     end
243 $     when {@cmd='FETCHNODE'}
244                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
245                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa nullable @etype
246 $     end
247 $     when {@cmd='ADDNODE'}
248                                         if ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
249                                                 ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.add(${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
250                                         end
251 $     end
252 $     when {@cmd='ADDLIST'}
253 #                                       if ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
254                                                 if ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.is_empty then
255                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
256                                                 else
257                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.append(${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
258                                                 end
259 #                                       end
260 $     end
261 $     when {@cmd='MAKELIST'}
262                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = new Array[Object]
263 $     end
264 $     when {@cmd='MAKENODE'}
265                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}: nullable @etype = new @etype.init_${translate(@etype,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}(
266 $       foreach {arg}
267 $           if @null
268                                                 null[-sep ','-]
269 $           else
270                                                 ${translate(.,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}[-sep ','-]
271 $           end
272 $       end foreach
273                                         )
274 $     end
275 $     when {@cmd='RETURNNODE'}
276 $       if @null
277                                         node_list = null
278 $       else
279                                         node_list = ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
280 $       end
281 $     end
282 $     when {@cmd='RETURNLIST'}
283                                         node_list = ${translate(@list,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
284 $     end
285 $   end choose
286 $   end foreach
287                                         p.push(p.go_to(@leftside), node_list)
288         end
289 init do end
290 end
291 $ end foreach
292 $ end template
293
294 $ template make_parser_tables()
295 # Parser that build a full AST
296 abstract class ParserTable
297         var _action_table: Array[Array[Int]]
298         private fun build_action_table
299         do
300                 _action_table = once [
301 $ foreach {parser_data/action_table/row}
302                         action_table_row${position()}[-sep ','-]
303 $ end foreach
304                 ]
305         end
306
307 $ foreach {parser_data/action_table/row}
308         private fun action_table_row${position()}: Array[Int]
309         do
310                 return [
311 $   foreach {action}
312                                 @from, @action, @to[-sep ','-]
313 $   end foreach
314                         ]
315         end
316 $ end foreach
317
318         var _goto_table: Array[Array[Int]]
319         private fun build_goto_table
320         do
321                 _goto_table = once [
322 $ foreach {parser_data/goto_table/row}
323                         [
324 $   foreach {goto}
325                                 @from, @to[-sep ','-]
326 $   end foreach
327                         ][-sep ','-]
328 $ end foreach
329                 ]
330         end
331
332         private fun error_messages: Array[String]
333         do
334                 return once [
335 $ foreach {parser_data/error_messages/msg}
336                         "${sablecc:string2escaped_unicode(.)}"[-sep ','-]
337 $ end
338                 ]
339         end
340
341         private fun errors: Array[Int]
342         do
343                 return once [
344                         [-foreach {parser_data/errors/i}-]${.}[-sep ','-][-end-]
345                 ]
346         end
347
348         init do end
349 end
350 $ end template