syntax: 'meth' -> 'fun', 'attr' -> 'var'
[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.pos
119                         var last_line = token.line
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).visit(node)
159                                 return node
160                         else if action_type == 3 then # ERROR
161                                 var node2 = new PError.init_error(lexer.filename, last_line, last_pos, error_messages[errors[action_value]])
162                                 var node = new Start(null, node2)
163                                 return node
164                         end
165                 end
166                 abort
167         end
168
169         var _reduce_table: Array[ReduceAction]
170         private fun build_reduce_table
171         do
172                 _reduce_table = new Array[ReduceAction].with_items(
173 $ foreach {rules/rule}
174                         new ReduceAction@index[-sep ','-]
175 $ end foreach
176                 )
177         end
178 end
179
180 # Find first and last tokens of production nodes
181 private class SearchTokensVisitor
182 special Visitor
183         var _untokenned_nodes: Array[Prod]
184         var _last_token: nullable Token = null
185         redef fun visit(n: nullable PNode)
186         do
187                 if n == null then
188                         return
189                 else if n isa Token then
190                         _last_token = n
191                         for no in _untokenned_nodes do
192                                 no.first_token = n
193                         end
194                         _untokenned_nodes.clear
195                 else
196                         assert n isa Prod
197                         _untokenned_nodes.add(n)
198                         n.visit_all(self)
199                         n.last_token = _last_token
200                 end
201         end
202         init
203         do
204                 _untokenned_nodes = new Array[Prod]
205         end
206 end
207
208 # Each reduca action has its own class, this one is the root of the hierarchy.
209 private abstract class ReduceAction
210         fun action(p: Parser) is abstract
211 end
212
213 $ foreach {rules/rule}
214 private class ReduceAction@index
215 special ReduceAction
216         redef fun action(p: Parser)
217         do
218                                         var node_list: nullable Object = null
219 $   foreach {action}
220 $   choose
221 $     when {@cmd='POP'}
222                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = p.pop
223 $     end
224 $     when {@cmd='FETCHLIST'}
225                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
226                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa Array[Object]
227 $     end
228 $     when {@cmd='FETCHNODE'}
229                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@from,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
230                                         assert ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} isa nullable @etype
231 $     end
232 $     when {@cmd='ADDNODE'}
233                                         if ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
234                                                 ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.add(${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
235                                         end
236 $     end
237 $     when {@cmd='ADDLIST'}
238 #                                       if ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} != null then
239                                                 if ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.is_empty then
240                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = ${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
241                                                 else
242                                                         ${translate(@tolist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}.append(${translate(@fromlist,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")})
243                                                 end
244 #                                       end
245 $     end
246 $     when {@cmd='MAKELIST'}
247                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")} = new Array[Object]
248 $     end
249 $     when {@cmd='MAKENODE'}
250                                         var ${translate(@result,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}: nullable @etype = new @etype.init_${translate(@etype,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}(
251 $       foreach {arg}
252 $           if @null
253                                                 null[-sep ','-]
254 $           else
255                                                 ${translate(.,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}[-sep ','-]
256 $           end
257 $       end foreach
258                                         )
259 $     end
260 $     when {@cmd='RETURNNODE'}
261 $       if @null
262                                         node_list = null
263 $       else
264                                         node_list = ${translate(@node,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
265 $       end
266 $     end
267 $     when {@cmd='RETURNLIST'}
268                                         node_list = ${translate(@list,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
269 $     end
270 $   end choose
271 $   end foreach
272                                         p.push(p.go_to(@leftside), node_list)
273         end
274 init do end
275 end
276 $ end foreach
277 $ end template
278
279 $ template make_parser_tables()
280 # Parser that build a full AST
281 abstract class ParserTable
282         var _action_table: Array[Array[Int]]
283         private fun build_action_table
284         do
285                 _action_table = once [ 
286 $ foreach {parser_data/action_table/row}
287                         action_table_row${position()}[-sep ','-]
288 $ end foreach
289                 ]
290         end
291
292 $ foreach {parser_data/action_table/row}
293         private fun action_table_row${position()}: Array[Int]
294         do
295                 return [
296 $   foreach {action}
297                                 @from, @action, @to [-sep ','-]
298 $   end foreach
299                         ]
300         end
301 $ end foreach
302
303         var _goto_table: Array[Array[Int]]
304         private fun build_goto_table
305         do
306                 _goto_table = once [ 
307 $ foreach {parser_data/goto_table/row}
308                         [
309 $   foreach {goto}
310                                 @from, @to [-sep ','-]
311 $   end foreach
312                         ] [-sep ','-]
313 $ end foreach
314                 ]
315         end
316
317         private fun error_messages: Array[String]
318         do
319                 return once [
320 $ foreach {parser_data/error_messages/msg}
321                         "${sablecc:string2escaped_unicode(.)}" [-sep ','-]
322 $ end
323                 ]
324         end
325
326         private fun errors: Array[Int]
327         do
328                 return once [
329                         [-foreach {parser_data/errors/i}-]${.} [-sep ','-] [-end-]
330                 ]
331         end
332
333         init do end
334 end
335 $ end template