nitcc: introduce nitcc
[nit.git] / contrib / nitcc / grammar.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # A gramar describing a language
16 class Gram
17 # The productions (non-terminal) of the conctete grammar
18 var prods = new Array[Production]
19
20 # The additionnal abstract productions of the grammar
21 # TODO clean AST
22 var ast_prods = new Array[Production]
23
24 # The tokens (terminals) of the grammar
25 var tokens = new Array[Token]
26
27 # Dump of the concrete grammar and the transformations
28 fun pretty: String
29 do
30 var res = new Buffer
31 for p in prods do
32 if p.spe != null then
33 res.append("{p.name} \{-> {p.spe.name}\}=\n")
34 else
35 res.append("{p.name} =\n")
36 end
37 var last = p.alts.last
38 for a in p.alts do
39 res.append("\t\{{a.name}:\} {a.elems.join(" ")}")
40 if a.codes == null then a.make_codes
41 res.append("\n\t\t\{-> {a.codes.join(", ")}\}")
42 if a == last then res.append(" ;\n") else res.append(" |\n")
43 end
44 if p.is_nullable then res.append "\t// is nullable\n"
45 if not p.firsts.is_empty then res.append "\t// firsts: {p.firsts.join(" ")}\n"
46 if not p.afters.is_empty then res.append "\t// afters: {p.afters.join(" ")}\n"
47 end
48 return res.to_s
49 end
50
51 # Inline (ie. remove from the conctete grammar) some production
52 # REQUIRE: no circular production in `prods`
53 fun inline(prods: Collection[Production])
54 do
55 for p in self.prods do
56 for a in p.alts.to_a do
57 var to_inline = false
58 for e in a.elems do
59 if e isa Production and prods.has(e) then to_inline = true
60 end
61 if not to_inline then continue
62
63 if a.codes == null then a.make_codes
64
65 var a0 = new Alternative(p, a.name, new Array[Element])
66 a0.trans = true
67 a0.codes = new Array[Code]
68 var pool = [a0]
69 var pool2 = new Array[Alternative]
70 for e in a.elems do
71 if not e isa Production or not prods.has(e) then
72 for x in pool do
73 x.elems.add(e)
74 x.codes.add(new CodePop)
75 end
76 continue
77 end
78 if p == e then
79 print "Circular inlining on {p} :: {a}"
80 abort
81 end
82 pool2.clear
83 for a2 in e.alts do
84 if a2.codes == null then a2.make_codes
85 for x in pool do
86 var name = a.name + "_" + pool2.length.to_s
87 var na = new Alternative(p, name, new Array[Element])
88 na.trans = true
89 pool2.add(na)
90 na.elems.add_all(x.elems)
91 na.elems.add_all(a2.elems)
92 na.codes = new Array[Code]
93 na.codes.add_all(x.codes.as(not null))
94 na.codes.add_all(a2.codes.as(not null))
95 end
96 end
97 var tmp = pool
98 pool = pool2
99 pool2 = tmp
100 end
101 for x in pool do
102 x.codes.add(a.codes.last)
103 end
104 p.ast_alts.add(a)
105 p.alts.remove(a)
106 p.alts.add_all(pool)
107 end
108 end
109 for p in prods do
110 self.prods.remove(p)
111 self.ast_prods.add(p)
112 end
113 end
114
115 # Compute a LR automaton
116 fun lr0: LRAutomaton
117 do
118 analyse
119
120 var start = new Production("_start")
121 start.accept = true
122 var eof = new Token("Eof")
123 tokens.add(eof)
124 start.new_alt("Start", self.prods.first, eof)
125 prods.add(start)
126 var first = new LRState("Start")
127 first.number = 0
128 for i in start.start_state do first.add(i)
129
130 var automaton = new LRAutomaton(self)
131
132 var todo = new List[LRState]
133 todo.add first
134 var seen = new HashSet[LRState]
135 seen.add first
136
137 while not todo.is_empty do
138 var state = todo.shift
139
140 #print state
141 automaton.states.add(state)
142 state.analysis
143
144 var nexts = new HashMap[Element, LRState]
145 for i in state.items do
146 var e = i.next
147 if e == null then continue
148 if nexts.has_key(e) then
149 nexts[e].add(i.avance)
150 else
151 var name
152 if state == automaton.states.first then
153 name = e.to_s
154 else
155 name = "{state.name} {e}"
156 end
157 var next = new LRState(name)
158 nexts[e] = next
159 next.add(i.avance)
160 end
161 end
162
163 for e, next in nexts do
164
165 #print "#states: {seen.length}"
166
167 var new_state = true
168 for n in seen do
169 if n == next then
170 for i in next.items do
171 if n.add(i) then n.extends(i)
172 end
173 next = n
174 new_state = false
175 break
176 end
177 end
178
179 if new_state then
180 next.number = seen.length
181 assert not seen.has(next)
182 seen.add(next)
183 todo.add(next)
184 end
185
186 var t = new LRTransition(state, next, e)
187 state.outs.add t
188 next.ins.add t
189 end
190 end
191 return automaton
192 end
193
194 # Compute `nullables`, `firsts` and `afters` of productions
195 fun analyse
196 do
197 loop
198 var changed = false
199 for p in prods do
200 if p.is_nullable then continue
201 for a in p.alts do
202 var nullabl = true
203 for e in a.elems do
204 if e isa Token then
205 nullabl = false
206 break
207 else if e isa Production then
208 if not e.is_nullable then nullabl = false
209 break
210 else
211 abort
212 end
213 end
214 if nullabl then
215 p.is_nullable = true
216 changed = true
217 end
218 end
219 end
220 if not changed then break
221 end
222
223 loop
224 var changed = false
225 for p in prods do
226 var fs = p.firsts
227 for a in p.alts do
228 for e in a.elems do
229 if e isa Token then
230 if try_add(fs, e) then changed = true
231 break
232 else if e isa Production then
233 for t in e.firsts do
234 if try_add(fs, t) then changed = true
235 end
236 if not e.is_nullable then break
237 else
238 abort
239 end
240 end
241 end
242 end
243 if not changed then break
244 end
245
246 loop
247 var changed = false
248 for p1 in prods do
249 for a in p1.alts do
250 var p0: nullable Production = null
251 for e in a.elems do
252 var p = p0
253 if e isa Production then p0 = e else p0 = null
254 if p == null then continue
255
256 var afs = p.afters
257
258 if e isa Token then
259 if try_add(afs, e) then changed = true
260 else if e isa Production then
261 for t in e.firsts do
262 if try_add(afs, t) then changed = true
263 end
264 if e.is_nullable then
265 for t in e.afters do
266 if try_add(afs, t) then changed = true
267 end
268 end
269 else
270 abort
271 end
272 end
273 if p0 != null then
274 var afs = p0.afters
275 for t in p1.afters do
276 if try_add(afs, t) then changed = true
277 end
278 end
279 end
280 end
281 if not changed then break
282 end
283 end
284
285 # used by `analyse`
286 private fun try_add(set: Set[Token], t: Token): Bool
287 do
288 var res = not set.has(t)
289 if res then set.add(t)
290 return res
291 end
292
293 end
294
295 # A production of a grammar
296 class Production
297 super Element
298 # The alternative of the production
299 var alts = new Array[Alternative]
300
301 # Additionnal alternatives in the AST
302 var ast_alts = new Array[Alternative]
303
304 # Is self the accept production
305 var accept = false
306
307 # Is self transformed to a other production for the AST
308 # FIXME: cleaup AST
309 var spe: nullable Production writable = null
310
311 # Is self contains only a single alternative (then no need for a abstract production class in the AST)
312 # FIXME cleanup AST
313 var altone writable = false
314
315 # The cname of the class in the AST
316 # FIXME: cleanup AST
317 redef fun acname do
318 if spe != null then return spe.acname
319 return super
320 end
321
322 # Is the production nullable
323 var is_nullable = false
324 # The first tokens of the production
325 var firsts = new HashSet[Token]
326 # The tokens that may follows the production (as in SLR)
327 var afters = new HashSet[Token]
328
329
330 # Crate a new empty alternative
331 fun new_alt0(name: String): Alternative
332 do
333 var a = new Alternative(self, name, new Array[Element])
334 alts.add a
335 return a
336 end
337
338 # Crate a new alternative with some elements
339 fun new_alt(name: String, elems: Element...): Alternative
340 do
341 var a = new Alternative(self, name, elems)
342 alts.add a
343 return a
344 end
345
346 # Crate a new alternative with some elements
347 fun new_alt2(name: String, elems: Array[Element]): Alternative
348 do
349 var a = new Alternative(self, name, elems)
350 alts.add a
351 return a
352 end
353
354 # Return a set of items for the production
355 fun start_state: Array[Item]
356 do
357 var res = new Array[Item]
358 for a in alts do
359 res.add new Item(a, 0)
360 end
361 return res
362 end
363
364 # States in the LR automaton that has a outgoing transition on self
365 var gotos = new ArraySet[LRState]
366 end
367
368 # An alternative of a production
369 class Alternative
370 # The production
371 var prod: Production
372
373 # The name of the alternative
374 var name: String writable
375
376 # The elements of the alternative
377 var elems: Array[Element]
378
379 # The name of the elements
380 var elems_names = new Array[nullable String]
381
382 # Return a name for a given element (or a number if unamed)
383 fun elemname(i: Int): String
384 do
385 if i >= elems_names.length then return "{i}"
386 var n = elems_names[i]
387 if n == null then return "{i}"
388 return n
389 end
390
391 redef fun to_s do return "{prod.name}::{name}={elems.join(" ")}"
392
393 # A mangled name
394 fun cname: String do
395 return "N{name.to_cmangle}"
396 end
397
398 # The code for the reduction
399 var codes: nullable Array[Code] writable = null
400
401 # Is the alternative transformed (ie not in the AST)
402 var trans writable = false
403
404 # Imitialize codes with the elements
405 fun make_codes
406 do
407 if codes != null then return
408 var codes = new Array[Code]
409 self.codes = codes
410 for e in elems do
411 codes.add(new CodePop)
412 end
413 codes.add(new CodeNew(self))
414 end
415 end
416
417 # A step in the construction of the AST. used to modelize transformations
418 interface Code
419 end
420 # Get a element from the stack
421 class CodePop
422 super Code
423 redef fun to_s do return "pop"
424 end
425 # Allocate a new AST node for an alternative using the correct number of poped elements
426 class CodeNew
427 super Code
428 var alt: Alternative
429 redef fun to_s do return "New {alt.name}/{alt.elems.length}"
430 end
431 # Get null
432 class CodeNull
433 super Code
434 redef fun to_s do return "null"
435 end
436
437 # Elements of an alternative.
438 # Either a `Production` or a `Token`
439 abstract class Element
440 # The name of the element
441 var name: String
442 redef fun to_s do return name
443
444 private var acname_cache: nullable String = null
445
446 # The mangled name of the element
447 fun cname: String do return "N{name.to_cmangle}"
448
449 # the name of the class in the AST
450 fun acname: String do
451 var res = acname_cache
452 if res == null then
453 res = "N{to_s.to_cmangle}"
454 acname_cache = res
455 end
456 return res
457 end
458 fun acname=(s: String) do acname_cache = s
459 end
460
461 # A terminal element
462 class Token
463 super Element
464 # States of the LR automatio that shift on self
465 var shifts = new ArraySet[LRState]
466 # States of the LR automatio that reduce on self in the lookahead(1)
467 var reduces = new ArraySet[LRState]
468 end
469
470 #
471
472 # A LR automaton
473 class LRAutomaton
474 # The grammar of the automaton
475 var grammar: Gram
476
477 # The set of states
478 var states = new Array[LRState]
479
480 # Dump of the automaton
481 fun pretty: String
482 do
483 var res = new Array[String]
484 res.add "* LRAutomaton: {states.length} states\n"
485 for s in states do
486 res.add "s{s.number} {s.name}\n"
487 res.add "\tCORE\n"
488 for i in s.core do
489 res.add "\t\t{i}\n"
490 end
491 res.add "\tOTHER ITEMS\n"
492 for i in s.items do
493 if s.core.has(i) then continue
494 res.add "\t\t{i}\n"
495 end
496 res.add "\tTRANSITIONS {s.outs.length}\n"
497 for t in s.outs do
498 res.add "\t\t{t.elem} |-> s{t.to.number}\n"
499 end
500 res.add "\tACTIONS\n"
501 if s.is_lr0 then
502 res.add "\t\tSTATE LR0\n"
503 else
504 res.add "\t\tSTATE LALR\n"
505 for t, a in s.guarded_reduce do
506 if a.length > 1 then
507 res.add "\t\t/!\\ REDUCE/REDUCE CONFLICT\n"
508 break
509 else if s.shifts.has(t) then
510 res.add "\t\t/!\\ SHIFT/REDUCE CONFLICT\n"
511 break
512 end
513 end
514 end
515 if not s.shifts.is_empty then
516 res.add "\t\tSHIFT {s.shifts.join(" ")}\n"
517 end
518 for r in s.reduces do
519 res.add "\t\tREDUCE {r}\n"
520 end
521 end
522 return res.to_s
523 end
524
525 # Generate a graphviz file of the automaton
526 fun to_dot(path: String)
527 do
528 var f = new OFStream.open(path)
529 f.write("digraph g \{\n")
530 f.write("rankdir=LR;\n")
531 f.write("node[shape=Mrecord,height=0];\n")
532
533 for s in states do
534 f.write "s{s.number} [label=\"{s.number} {s.name.escape_to_dot}|"
535 for i in s.core do
536 f.write "{i.to_s.escape_to_dot}\\l"
537 end
538 f.write("|")
539 for i in s.items do
540 if s.core.has(i) then continue
541 f.write "{i.to_s.escape_to_dot}\\l"
542 end
543 f.write "\""
544 if not s.is_lr0 then
545 var conflict = false
546 for t, a in s.guarded_reduce do
547 if a.length > 1 then
548 f.write ",color=red"
549 conflict = true
550 break
551 else if s.shifts.has(t) then
552 f.write ",color=orange"
553 conflict = true
554 break
555 end
556 end
557 if not conflict then
558 f.write ",color=blue"
559 end
560 end
561 f.write "];\n"
562 for t in s.outs do
563 f.write "s{s.number} -> s{t.to.number} [label=\"{t.elem.to_s.escape_to_dot}\"];\n"
564 end
565 end
566 f.write("\}\n")
567 f.close
568 end
569
570 # Generate the parser of the automaton
571 fun gen_to_nit(filepath: String)
572 do
573 var gen = new Generator
574 gen.gen_to_nit(self)
575 var f = new OFStream.open(filepath)
576 for s in gen.out do
577 f.write(s)
578 f.write("\n")
579 end
580 f.close
581 end
582 end
583
584 redef class String
585 # escape string used in labels for graphviz
586 fun escape_to_dot: String
587 do
588 return escape_more_to_c("|\{\}")
589 end
590 end
591
592 private class Generator
593 var out = new Array[String]
594 fun add(s: String) do out.add(s)
595 fun gen_to_nit(autom: LRAutomaton)
596 do
597 var states = autom.states
598 var gram = autom.grammar
599
600 add "# Parser generated by nitcc"
601 add "import nitcc_runtime"
602
603 add "class MyParser"
604 add "\tsuper Parser"
605 add "\tredef fun start_state do return state_{states.first.cname}"
606 add "end"
607
608 add "redef class Object"
609 for s in states do
610 add "\tprivate fun state_{s.cname}: LRState{s.cname} do return once new LRState{s.cname}"
611 end
612 for p in gram.prods do
613 add "\tprivate fun goto_{p.cname}: Goto_{p.cname} do return once new Goto_{p.cname}"
614 end
615 add "end"
616
617 add "redef class NToken"
618 for s in states do
619 if not s.need_guard then continue
620 add "\t# guarded action for state {s.name}"
621 add "\t# {s.shifts.length} shift(s) and {s.reduces.length} reduce(s)"
622 add "\tprivate fun action_s{s.cname}(parser: Parser) do"
623 if s.reduces.length != 1 then
624 add "\t\tparser.parse_error"
625 else
626 gen_reduce_to_nit(s.reduces.first)
627 end
628 add "\tend"
629 end
630 add "end"
631
632 for t in gram.tokens do
633 if t.name == "Eof" then
634 add "redef class {t.cname}"
635 else
636 add "class {t.cname}"
637 end
638 add "\tsuper NToken"
639 for s in t.shifts do
640 if not s.need_guard then continue
641 add "\tredef fun action_s{s.cname}(parser) do"
642 gen_shift_to_nit(s, t)
643 add "\tend"
644 end
645 for s in t.reduces do
646 if not s.need_guard then continue
647 if s.reduces.length <= 1 then continue
648 add "\tredef fun action_s{s.cname}(parser) do"
649 gen_reduce_to_nit(s.guarded_reduce[t].first.alt)
650 add "\tend"
651 end
652 add "\tredef fun node_name do return \"{t.name.escape_to_nit}\""
653 add "end"
654 end
655
656 add "redef class LRGoto"
657 for s in states do
658 if s.gotos.length <= 1 then continue
659 add "\tprivate fun goto_s{s.cname}(parser: Parser) do abort"
660 end
661 add "end"
662
663 for p in gram.prods do
664 add "class Goto_{p.cname}"
665 add "\tsuper LRGoto"
666 for s in p.gotos do
667 if s.gotos.length <= 1 then continue
668 add "\tredef fun goto_s{s.cname}(parser) do"
669 gen_goto_to_nit(s, p)
670 add "\tend"
671 end
672 add "end"
673 end
674
675 var ps = gram.prods.to_a
676 ps.add_all(gram.ast_prods)
677 for p in ps do
678 if p.spe == null and not p.altone then
679 if p.name.has_suffix("?") or p.name.has_suffix("+") then continue
680 add "class {p.acname}"
681 add "\tsuper NProd"
682 add "\tredef fun node_name do return \"{p.name.escape_to_nit}\""
683 add "end"
684 end
685
686 var als = p.alts.to_a
687 als.add_all(p.ast_alts)
688 for a in als do
689 if a.trans then continue
690 add "class {a.cname}"
691 if p.altone then
692 add "\tsuper NProd"
693 else
694 add "\tsuper {p.acname}"
695 end
696 add "\tredef fun node_name do return \"{a.name.escape_to_nit}\""
697 var initarg = new Array[String]
698 for i in [0..a.elems.length[ do
699 add "\tvar n_{a.elemname(i)}: {a.elems[i].acname}"
700 initarg.add("n_{a.elemname(i)}: {a.elems[i].acname}")
701 end
702 if initarg.is_empty then
703 add "\tinit do end"
704 else
705 add "\tinit({initarg.join(", ")}) do"
706 for i in [0..a.elems.length[ do
707 add "\t\tself.n_{a.elemname(i)} = n_{a.elemname(i)}"
708 end
709 add "\tend"
710 end
711 add "\tredef fun number_of_children do return {a.elems.length}"
712 add "\tredef fun child(i) do"
713 for i in [0..a.elems.length[ do
714 add "\t\tif i == {i} then return n_{a.elemname(i)}"
715 end
716 add "\t\tabort"
717 add "\tend"
718 add "end"
719 end
720 end
721
722 for s in states do
723 add "# State {s.name}"
724 add "private class LRState{s.cname}"
725 add "\tsuper LRState"
726
727 add "\tredef fun to_s do return \"{s.name.escape_to_nit}\""
728
729 var err = new Array[String]
730 for t in s.outs do
731 var e = t.elem
732 if e isa Production then err.add e.name
733 end
734 if err.is_empty then for t in s.outs do
735 var e = t.elem
736 if e isa Token then err.add e.name
737 end
738
739 add "\tredef fun error_msg do return \"{err.join(", ").escape_to_nit}\""
740
741 add "\tredef fun action(parser) do"
742 if s.need_guard then
743 add "\t\tparser.peek_token.action_s{s.cname}(parser)"
744 else if s.reduces.length == 1 then
745 gen_reduce_to_nit(s.reduces.first)
746 else
747 abort
748 end
749 add "\tend"
750
751 if not s.gotos.is_empty then
752 add "\tredef fun goto(parser, goto) do"
753 if s.gotos.length > 1 then
754 add "\t\tgoto.goto_s{s.cname}(parser)"
755 else
756 gen_goto_to_nit(s, s.gotos.first)
757 end
758 add "\tend"
759 end
760
761 add "end"
762 end
763
764
765 end
766
767 fun gen_shift_to_nit(s: LRState, t: Token)
768 do
769 var dest = s.trans(t)
770 add "\t\tparser.shift(state_{dest.cname})"
771
772 end
773
774 fun gen_goto_to_nit(s: LRState, p: Production)
775 do
776 var dest = s.trans(p)
777 add "\t\tparser.push(state_{dest.cname})"
778 end
779
780 fun gen_reduce_to_nit(alt: Alternative)
781 do
782 add "\t\t# REDUCE {alt}"
783 var i = alt.elems.length - 1
784 for e in alt.elems.to_a.reversed do
785 add "\t\tvar n{i} = parser.pop.as({e.acname})"
786 i -= 1
787 end
788
789 if alt.name.has_suffix("+_more") then
790 add "\t\tvar prod = n0"
791 add "\t\tn0.items.add(n1)"
792 else if alt.name.has_suffix("+_one") then
793 add "\t\tvar prod = new {alt.prod.acname}"
794 add "\t\tprod.items.add(n0)"
795 else
796 alt.make_codes
797 var cpt = 0
798 i = 0
799 var st = new Array[String]
800 for c in alt.codes.as(not null) do
801 if c isa CodePop then
802 st.add "n{i}"
803 i += 1
804 else if c isa CodeNull then
805 st.add "null"
806 else if c isa CodeNew then
807 var calt = c.alt
808 cpt += 1
809 var from = st.length - calt.elems.length
810 var args = new List[String]
811 for j in [from..st.length[ do
812 args.unshift(st.pop)
813 end
814
815 if args.is_empty then
816 add "\t\tvar p{cpt} = new {calt.cname}"
817 else
818 add "\t\tvar p{cpt} = new {calt.cname}({args.join(", ")})"
819 end
820 #var x = 0
821 #for j in [from..st.length[ do
822 #if st[j] == "null" then continue
823 #add "\t\tp{cpt}.n_{calt.elemname(x)} = {st[j]}"
824 #x += 1
825 #end
826 st.add("p{cpt}")
827 end
828 end
829 assert st.length == 1
830 add "\t\tvar prod = {st.first}"
831 end
832
833 add "\t\tparser.node_stack.push prod"
834 if alt.prod.accept then
835 add "\t\tparser.stop = true"
836 else
837 add "\t\tparser.goto(goto_{alt.prod.cname})"
838 end
839 end
840 end
841
842 # A state in a LR automaton
843 class LRState
844 # name of the automaton (short part from the start)
845 var name: String
846
847 # malglen name
848 fun cname: String do return name.to_cmangle
849
850 # number
851 var number: Int = -1
852
853 # Set of all items
854 var items = new HashSet[Item]
855
856 # Set of items only in the core
857 var core = new HashSet[Item]
858
859 # Outgoing transitions
860 var ins = new Array[LRTransition]
861
862 # Ingoing transitions
863 var outs = new Array[LRTransition]
864
865 # trans function
866 fun trans(e: Element): nullable LRState
867 do
868 for t in outs do if t.elem == e then return t.to
869 return null
870 end
871
872 redef fun ==(o) do return o isa LRState and core == o.core
873 redef fun hash do return items.length
874
875 redef fun to_s do return items.join(" ; ")
876
877 # Add and item in the core
878 fun add(i: Item): Bool
879 do
880 #if items.has(i) then return
881
882 #print "add {i} in {inspect}={self}"
883 var found = false
884 for i2 in items do
885 if i.alt == i2.alt and i.pos == i2.pos then
886 if i2.future.has_all(i.future) then return false
887 i2.future.add_all(i.future)
888 found = true
889
890 break
891 end
892 end
893 if not found then
894 items.add(i)
895 if i.pos > 0 or i.alt.prod.accept then core.add(i)
896 end
897 return true
898 end
899
900 # Recusively extends item outside the core
901 fun extends(i: Item)
902 do
903 var e = i.next
904 if e == null then return
905 if not e isa Production then return
906 var la = i.lookahead
907 for i2 in e.start_state do
908 i2.future.add_all(la)
909 if add(i2) then extends(i2)
910 end
911 end
912
913 # Set of all reductions
914 var reduces = new ArraySet[Alternative]
915 # Set of all shifts
916 var shifts = new ArraySet[Token]
917 # Set of all goto
918 var gotos = new ArraySet[Production]
919 # Reduction guarded by tokens
920 var guarded_reduce = new HashMap[Token, Array[Item]]
921 # Shitfs guarded by tokens
922 var guarded_shift = new HashMap[Token, Array[Item]]
923
924 # Does the state need a guard to perform an action?
925 fun need_guard: Bool do return not shifts.is_empty or reduces.length > 1
926
927 # Is the state LR0
928 fun is_lr0: Bool do return reduces.length <= 1 and shifts.is_empty or reduces.is_empty
929
930 # compute guards and conflicts
931 fun analysis
932 do
933 for i in items.to_a do
934 extends(i)
935 end
936
937 for i in items do
938 var n = i.next
939 if n == null then
940 reduces.add(i.alt)
941 #for t in i.alt.prod.afters do
942 for t in i.future do
943 t.reduces.add(self)
944 if guarded_reduce.has_key(t) then
945 guarded_reduce[t].add(i)
946 else
947 guarded_reduce[t] = [i]
948 end
949 end
950 else if n isa Token then
951 shifts.add(n)
952 n.shifts.add(self)
953 if guarded_shift.has_key(n) then
954 guarded_shift[n].add(i)
955 else
956 guarded_shift[n] = [i]
957 end
958 else if n isa Production then
959 gotos.add(n)
960 n.gotos.add(self)
961 else
962 abort
963 end
964 end
965 for t, a in guarded_reduce do
966 if a.length > 1 then
967 print "REDUCE/REDUCE Conflict on state {self.number} {self.name} for token {t}:"
968 for i in a do print "\t{i}"
969 else if guarded_shift.has_key(t) then
970 print "SHIFT/REDUCE Conflict on state {self.number} {self.name} for token {t}:"
971 for i in a do print "\t{i}"
972 for i in guarded_shift[t] do print "\t{i}"
973 end
974 end
975 end
976 end
977
978 # A transition in a LR automaton
979 class LRTransition
980 # The origin state
981 var from: LRState
982 # The testination state
983 var to: LRState
984 # The element labeling the transition
985 var elem: Element
986 end
987
988 # A alternative with a cursor (dot) and possibly a future
989 class Item
990 # The alternative
991 var alt: Alternative
992 # The dot index (0 means before the first element)
993 var pos: Int
994 # The possible future
995 var future = new ArraySet[Token]
996
997 redef fun ==(o) do return o isa Item and alt == o.alt and pos == o.pos
998 redef fun hash do return alt.hash + pos
999
1000 redef fun to_s
1001 do
1002 var b = new Buffer
1003 b.append("{alt.prod.name}::{alt.name}=")
1004 for i in [0..alt.elems.length[
1005 do
1006 if pos == i then b.append(".") else b.append(" ")
1007 b.append(alt.elems[i].to_s)
1008 end
1009 if pos == alt.elems.length then b.append(".")
1010 if not future.is_empty then
1011 b.append("/\{")
1012 b.append(future.join(" "))
1013 b.append("\}")
1014 end
1015 return b.to_s
1016 end
1017
1018 # The element thatr follow the dot, null if the fdot is at the end
1019 fun next: nullable Element
1020 do
1021 if pos >= alt.elems.length then return null
1022 return alt.elems[pos]
1023 end
1024
1025 # SLR loohahead
1026 fun lookahead: Set[Token]
1027 do
1028 var res = new HashSet[Token]
1029 var p = pos + 1
1030 while p < alt.elems.length do
1031 var e = alt.elems[p]
1032 if e isa Token then
1033 res.add(e)
1034 break
1035 else if e isa Production then
1036 res.add_all(e.firsts)
1037 if not e.is_nullable then break
1038 end
1039 p += 1
1040 end
1041 if p >= alt.elems.length then
1042 res.add_all(future)
1043 end
1044 return res
1045 end
1046
1047 # The item that advanced once
1048 fun avance: Item
1049 do
1050 var res = new Item(alt, pos+1)
1051 res.future.add_all(future)
1052 return res
1053 end
1054 end