First NIT release and new clean mercurial repository
[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 redef class Parser
37         # Associated lexer
38         attr _lexer: Lexer
39
40         # Stack of pushed states and productions
41         attr _stack: Array[State]
42
43         # Position in the stack
44         attr _stack_pos: Int
45
46         # Create a new parser based on a given lexer
47         init(lexer: Lexer)
48         do
49                 _lexer = lexer
50                 _stack = new Array[State]
51                 _stack_pos = -1
52                 build_goto_table
53                 build_action_table
54                 build_reduce_table
55         end
56
57         # Do a transition in the automata
58         private meth go_to(index: Int): Int
59         do
60                 var state = state
61                 var table = _goto_table[index]
62                 var low = 1
63                 var high = table.length/2 - 1
64
65                 while low <= high do
66                         var middle = (low + high) / 2
67                         var subindex = middle * 2
68
69                         if state < table[subindex] then
70                                 high = middle - 1
71                         else if state > table[subindex] then
72                                 low = middle + 1
73                         else
74                                 return table[subindex + 1]
75                         end
76                 end
77
78                 return table[1] # Default value
79         end
80
81         # Push someting in the state stack
82         private meth push(numstate: Int, list_node: Object)
83         do
84                 var pos = _stack_pos + 1
85                 _stack_pos = pos
86                 if pos < _stack.length then
87                         var state = _stack[pos]
88                         state.state = numstate
89                         state.nodes = list_node
90                 else
91                         _stack.push(new State(numstate, list_node))
92                 end
93         end
94
95         # The current state
96         private meth state: Int
97         do
98                 return _stack[_stack_pos].state
99         end
100
101         # Pop something from the stack state
102         private meth pop: Object
103         do
104                 var res = _stack[_stack_pos].nodes
105                 _stack_pos = _stack_pos -1
106                 return res
107         end
108
109         # Build and return a full AST.
110         meth parse: Start
111         do
112                 push(0, null)
113
114                 var ign: List[Token] = null
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                                 assert token isa PError
123                                 return new Start(null, token)
124                         end
125
126                         var index = token.parser_index
127                         var table = _action_table[state]
128                         var action_type = table[1]
129                         var action_value = table[2]
130
131                         var low = 1
132                         var high = table.length/3 - 1
133
134                         while low <= high do
135                                 var middle = (low + high) / 2
136                                 var subindex = middle * 3
137
138                                 if index < table[subindex] then
139                                         high = middle - 1
140                                 else if index > table[subindex] then
141                                         low = middle + 1
142                                 else
143                                         action_type = table[subindex + 1]
144                                         action_value = table[subindex + 2]
145                                         high = low -1 # break
146                                 end
147                         end
148
149                         if action_type == 0 then # SHIFT
150                                 push(action_value, lexer.next)
151                         else if action_type == 1 then # REDUCE
152                                 _reduce_table[action_value].action(self)
153                         else if action_type == 2 then # ACCEPT
154                                 var node2 = lexer.next
155                                 assert node2 isa EOF
156                                 var node1 = pop
157                                 assert node1 isa ${/parser/prods/prod/@ename}
158                                 var node = new Start(node1, node2)
159                                 (new SearchTokensVisitor).visit(node)
160                                 return node
161                         else if action_type == 3 then # ERROR
162                                 var node2 = new PError.init_error(lexer.filename, last_line, last_pos, error_messages[errors[action_value]])
163                                 var node = new Start(null, node2)
164                                 return node
165                         end
166                 end
167                 return null
168         end
169
170         attr _reduce_table: Array[ReduceAction]
171         private meth build_reduce_table
172         do
173                 _reduce_table = new Array[ReduceAction].with(
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         attr _untokenned_nodes: Array[Prod]
185         attr _last_token: Token
186         redef meth visit(n: PNode)
187         do
188                 if n isa Token then
189                         assert n isa Token
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         meth action(p: Parser) is abstract
211 end
212
213 $ foreach {rules/rule}
214 private class ReduceAction@index
215 special ReduceAction
216         redef meth action(p: Parser)
217         do
218                                         var node_list: 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 @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")} = 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 class Parser
282         attr _action_table: Array[Array[Int]]
283         private meth 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 meth 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         attr _goto_table: Array[Array[Int]]
304         private meth 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 meth 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 meth errors: Array[Int]
327         do
328                 return once [
329                         [-foreach {parser_data/errors/i}-]${.} [-sep ','-] [-end-]
330                 ]
331         end
332 end
333 $ end template