PageRenderTime 31ms CodeModel.GetById 20ms app.highlight 5ms RepoModel.GetById 2ms app.codeStats 0ms

/src/lib/parse/parse_nt_node.e

http://github.com/tybor/Liberty
Specman e | 468 lines | 141 code | 8 blank | 319 comment | 14 complexity | 545f744bc651b8e0add00191661604fa MD5 | raw file
  1-- This file is part of a Liberty Eiffel library.
  2-- See the full copyright at the end.
  3--
  4class PARSE_NT_NODE
  5   --
  6   -- An internal class used by PARSE_NON_TERMINAL that implements the LL(n) parsing algorithm.
  7   --
  8
  9insert
 10   TRISTATE_VALUES
 11      redefine
 12         copy, is_equal, out_in_tagged_out_memory
 13      end
 14   LOGGING
 15      redefine
 16         copy, is_equal, out_in_tagged_out_memory
 17      end
 18
 19creation {PARSE_NON_TERMINAL}
 20   root
 21
 22creation {PARSE_NT_NODE}
 23   make
 24
 25feature {ANY}
 26   out_in_tagged_out_memory is
 27      local
 28         i: INTEGER
 29      do
 30         if prefix_name /= Void then
 31            tagged_out_memory.extend('"')
 32            tagged_out_memory.append(prefix_name)
 33            tagged_out_memory.extend('"')
 34         end
 35         if suffices /= Void then
 36            tagged_out_memory.extend('(')
 37            from
 38               i := suffices.lower
 39            until
 40               i > suffices.upper
 41            loop
 42               if i > suffices.lower then
 43                  tagged_out_memory.extend('|')
 44               end
 45               suffices.item(i).out_in_tagged_out_memory
 46               i := i + 1
 47            end
 48            tagged_out_memory.extend(')')
 49         end
 50         if end_of_rule then
 51            tagged_out_memory.extend('$')
 52         end
 53      end
 54
 55feature {PARSE_NON_TERMINAL}
 56   add (rule: TRAVERSABLE[FIXED_STRING]; a_action: PROCEDURE[TUPLE[FIXED_STRING, TRAVERSABLE[FIXED_STRING]]]) is
 57      local
 58         node: PARSE_NT_NODE; name: FIXED_STRING
 59      do
 60         check
 61            is_root: prefix_name = Void
 62         end
 63         if rule.is_empty then
 64            -- a non-terminal with Epsilon
 65            if a_action /= Void then
 66               action := agent call_non_terminal_builder(a_action, rule)
 67            end
 68            end_of_rule := True
 69         else
 70            name := rule.first.intern
 71            node := suffices.fast_reference_at(name)
 72            if node = Void then
 73               create node.make(name, nt)
 74               suffices.add(node, name)
 75            end
 76            node.do_add(a_action, rule, rule.lower)
 77         end
 78      end
 79
 80   parse (buffer: MINI_PARSER_BUFFER; actions: COLLECTION[PARSE_ACTION]): TRISTATE is
 81      local
 82         parse_action: PARSE_ACTION
 83      do
 84         check
 85            is_root: prefix_name = Void
 86         end
 87         Result := parse_suffices(buffer, actions)
 88         if Result /= yes and then end_of_rule then
 89            -- Epsilon
 90            Result := yes
 91            create parse_action.make(action)
 92            debug ("parse")
 93               parse_action.set_name(once "Reduce %"" + nt.name + once "%"")
 94            end
 95            actions.add_last(parse_action)
 96         end
 97      ensure
 98         actions.count >= old actions.count
 99         ;(Result /= yes) implies buffer.current_index = old buffer.current_index and then actions.count = old actions.count
100      end
101
102feature {PARSE_NON_TERMINAL, PARSE_NT_NODE}
103   is_coherent: BOOLEAN is
104      local
105         i: INTEGER
106      do
107         if prefix_name = Void then
108            Result := True
109         else
110            Result := nt.table.has(prefix_name)
111         end
112         if suffices /= Void then
113            from
114               i := suffices.lower
115            until
116               not Result or else i > suffices.upper
117            loop
118               Result := suffices.item(i).is_coherent
119               i := i + 1
120            end
121         end
122      ensure
123         must_be_coherent: Result
124      end
125
126   set_default_tree_builder (non_terminal_builder: PROCEDURE[TUPLE[FIXED_STRING, TRAVERSABLE[FIXED_STRING]]]; path: COLLECTION[FIXED_STRING]) is
127      require
128         non_terminal_builder /= Void
129      local
130         i: INTEGER
131      do
132         if end_of_rule and then action = Void then
133            debug ("parse")
134               log.trace.put_string(once "Setting default non-terminal tree builder for %"")
135               log.trace.put_string(nt.name)
136               log.trace.put_string(once "%": ")
137               if prefix_name = Void then
138                  -- root
139                  check
140                     path.is_empty
141                  end
142                  log.trace.put_line(once "Epsilon")
143               else
144                  from
145                     i := path.lower
146                  until
147                     i > path.upper
148                  loop
149                     log.trace.put_character('"')
150                     log.trace.put_string(path.item(i))
151                     log.trace.put_character('"')
152                     log.trace.put_character(' ')
153                     i := i + 1
154                  end
155                  log.trace.put_character('"')
156                  log.trace.put_string(prefix_name)
157                  log.trace.put_character('"')
158                  log.trace.put_character('>')
159                  log.trace.put_new_line
160               end
161            end
162            if prefix_name /= Void then
163               path.add_last(prefix_name)
164               action := agent call_non_terminal_builder(non_terminal_builder, path.twin)
165               path.remove_last
166            else
167               -- Epsilon rule
168               check
169                  path.is_empty
170               end
171               action := agent call_non_terminal_builder(non_terminal_builder, path.twin)
172            end
173         end
174         if suffices /= Void then
175            if prefix_name = Void then
176               -- root
177               check
178                  path.is_empty
179               end
180               from
181                  i := suffices.lower
182               until
183                  i > suffices.upper
184               loop
185                  suffices.item(i).set_default_tree_builder(non_terminal_builder, path)
186                  i := i + 1
187               end
188            else
189               path.add_last(Void)
190               from
191                  i := suffices.lower
192               until
193                  i > suffices.upper
194               loop
195                  path.put(prefix_name, path.upper)
196                  suffices.item(i).set_default_tree_builder(non_terminal_builder, path)
197                  i := i + 1
198               end
199               path.remove_last
200            end
201         end
202      end
203
204   set_non_terminal (a_non_terminal: like nt) is
205      local
206         i: INTEGER
207      do
208         nt := a_non_terminal
209         if suffices /= Void then
210            from
211               i := suffices.lower
212            until
213               i > suffices.upper
214            loop
215               suffices.item(i).set_non_terminal(a_non_terminal)
216               i := i + 1
217            end
218         end
219      ensure
220         nt = a_non_terminal
221      end
222
223feature {PARSE_NT_NODE}
224   do_add (a_action: PROCEDURE[TUPLE[FIXED_STRING, TRAVERSABLE[FIXED_STRING]]]; rule: TRAVERSABLE[FIXED_STRING]; i: INTEGER) is
225      require
226         rule.valid_index(i)
227         rule.item(i) = prefix_name
228      local
229         name: FIXED_STRING; node: PARSE_NT_NODE
230      do
231         if i < rule.upper then
232            name := rule.item(i + 1).intern
233            if suffices = Void then
234               create suffices.make
235            end
236            node := suffices.fast_reference_at(name)
237            if node = Void then
238               create node.make(name, nt)
239               suffices.add(node, name)
240            end
241            node.do_add(a_action, rule, i + 1)
242         else
243            check
244               i = rule.upper
245            end
246            if a_action /= Void then
247               action := agent call_non_terminal_builder(a_action, rule)
248            end
249            end_of_rule := True
250         end
251      end
252
253   do_parse (buffer: MINI_PARSER_BUFFER; actions: COLLECTION[PARSE_ACTION]): TRISTATE is
254      require
255         not_root: prefix_name /= Void
256      local
257         memo, old_count: INTEGER; atom: PARSE_ATOM
258         parse_action: PARSE_ACTION
259      do
260         memo := buffer.memo
261         old_count := actions.count
262         atom := nt.table.item(prefix_name)
263         check
264            atom /= Void
265         end
266         Result := atom.parse(buffer, actions)
267         check
268            suffices = Void implies end_of_rule
269         end
270         if Result = yes then
271            if suffices = Void then
272               create parse_action.make(action)
273               debug ("parse")
274                  parse_action.set_name(once "Reduce %"" + nt.name + once "%"")
275               end
276               actions.add_last(parse_action)
277            else
278               Result := parse_suffices(buffer, actions)
279               if Result /= yes and then end_of_rule then
280                  -- that's fine: we can end here
281                  Result := yes
282                  create parse_action.make(action)
283                  debug ("parse")
284                     parse_action.set_name(once "Reduce %"" + nt.name + once "%"")
285                  end
286                  actions.add_last(parse_action)
287               end
288            end
289         end
290         if Result /= yes then
291            buffer.restore(memo)
292            if actions.count > old_count then
293               actions.remove_tail(actions.count - old_count)
294            end
295         end
296      ensure
297         (Result /= yes) implies buffer.current_index = old buffer.current_index and then actions.count = old actions.count
298      end
299
300feature {}
301   parse_suffices (buffer: MINI_PARSER_BUFFER; actions: COLLECTION[PARSE_ACTION]): TRISTATE is
302      require
303         suffices /= Void
304      local
305         memo, old_count, i: INTEGER; node: PARSE_NT_NODE; parsenode: TRISTATE; perhaps: BOOLEAN
306      do
307         debug ("parse")
308            log.trace.put_string(once "Scanning non-terminal %"")
309            log.trace.put_string(nt.name)
310            log.trace.put_character('"')
311            if prefix_name /= Void then
312               log.trace.put_string(once " for a suffix of %"")
313               log.trace.put_string(prefix_name)
314               log.trace.put_character('"')
315            end
316            log.trace.put_new_line
317         end
318         memo := buffer.memo
319         old_count := actions.count
320         from
321            i := suffices.lower
322            Result := no
323         until
324            Result = yes or else i > suffices.upper
325         loop
326            node := suffices.item(i)
327            parsenode := node.do_parse(buffer, actions)
328            if parsenode = yes then
329               Result := yes
330            else
331               if parsenode = maybe then
332                  perhaps := True
333               end
334               buffer.restore(memo)
335               if actions.count > old_count then
336                  actions.remove_tail(actions.count - old_count)
337               end
338               debug ("parse")
339                  log.trace.put_string(once "Still scanning non-terminal %"")
340                  log.trace.put_string(nt.name)
341                  log.trace.put_character('"')
342                  if prefix_name /= Void then
343                     log.trace.put_string(once " for a suffix of %"")
344                     log.trace.put_string(prefix_name)
345                     log.trace.put_character('"')
346                  end
347                  log.trace.put_string(once " (%"")
348                  log.trace.put_string(suffices.key(i))
349                  log.trace.put_line(once "%" did not match)")
350               end
351               i := i + 1
352            end
353         end
354         if Result = no and then perhaps then
355            Result := maybe
356         end
357      ensure
358         (Result /= yes) implies buffer.current_index = old buffer.current_index and then actions.count = old actions.count
359      end
360
361feature {}
362   call_non_terminal_builder (non_terminal_builder: PROCEDURE[TUPLE[FIXED_STRING, TRAVERSABLE[FIXED_STRING]]]; path: TRAVERSABLE[FIXED_STRING]) is
363      do
364         non_terminal_builder.call([nt.name, path])
365      end
366
367feature {}
368   make (a_prefix_name: like prefix_name; a_nt: like nt) is
369      require
370         a_prefix_name /= Void
371         a_nt /= Void
372      do
373         prefix_name := a_prefix_name
374         nt := a_nt
375      ensure
376         prefix_name = a_prefix_name
377         nt = a_nt
378      end
379
380   root (a_nt: like nt) is
381      require
382         a_nt /= Void
383      do
384         nt := a_nt
385         create suffices.make
386      ensure
387         nt = a_nt
388         suffices /= Void
389         is_root: prefix_name = Void
390      end
391
392feature {ANY}
393   copy (other: like Current) is
394      local
395         i: INTEGER
396      do
397         nt := other.nt
398         prefix_name := other.prefix_name
399         action := other.action
400         end_of_rule := other.end_of_rule
401         if other.suffices /= Void then
402            create suffices.with_capacity(other.suffices.capacity)
403            from
404               i := other.suffices.lower
405            until
406               i > other.suffices.upper
407            loop
408               suffices.add(other.suffices.item(i).twin, other.suffices.key(i))
409               i := i + 1
410            end
411         end
412      end
413
414   is_equal (other: like Current): BOOLEAN is
415      local
416         i: INTEGER
417      do
418         Result := prefix_name = other.prefix_name
419            and then action = other.action
420            and then end_of_rule = other.end_of_rule
421         if Result and then other.suffices /= Void then
422            Result := suffices /= Void and then suffices.count = other.suffices.count
423            from
424               i := suffices.lower
425            until
426               not Result or else i > suffices.upper
427            loop
428               Result := suffices.fast_reference_at(other.suffices.key(i)).is_equal(other.suffices.item(i))
429               i := i + 1
430            end
431         end
432      end
433
434feature {PARSE_NT_NODE}
435   prefix_name: FIXED_STRING
436
437   suffices: HASHED_DICTIONARY[PARSE_NT_NODE, FIXED_STRING]
438
439   nt: PARSE_NON_TERMINAL
440
441   action: PROCEDURE[TUPLE]
442
443   end_of_rule: BOOLEAN
444
445invariant
446   backlinked: nt /= Void
447
448end -- class PARSE_NT_NODE
449--
450-- Copyright (c) 2009 by all the people cited in the AUTHORS file.
451--
452-- Permission is hereby granted, free of charge, to any person obtaining a copy
453-- of this software and associated documentation files (the "Software"), to deal
454-- in the Software without restriction, including without limitation the rights
455-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
456-- copies of the Software, and to permit persons to whom the Software is
457-- furnished to do so, subject to the following conditions:
458--
459-- The above copyright notice and this permission notice shall be included in
460-- all copies or substantial portions of the Software.
461--
462-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
463-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
464-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
465-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
466-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
467-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
468-- THE SOFTWARE.