1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # nitcc, a parser and lexer generator for Nit
20 # Load the grammar file
23 print
"usage: nitcc <file> | -"
30 var f
= new IFStream.open
(fi
)
37 # Parse the grammar file
39 var l
= new Lexer_nitcc(text
)
42 var p
= new Parser_nitcc
47 if not node
isa NProd then
48 assert node
isa NError
49 print
"{node.position.to_s} Syntax Error: {node.message}"
54 var name
= node
.children
.first
.as(Ngrammar).children
[1].as(Nid).text
56 print
"Grammar {name} (see {name}.gram.dot))"
57 node
.to_dot
("{name}.gram.dot")
61 var v2
= new CollectNameVisitor
65 if gram
.prods
.is_empty
then
66 print
"Error: grammar with no production"
70 # Generate the LR automaton
74 var conflitcs
= new ArraySet[Production]
75 for s
in lr
.states
do for t
, a
in s
.guarded_reduce
do if a
.length
> 1 or s
.guarded_shift
.has_key
(t
) then
76 for i
in a
do conflitcs
.add
(i
.alt
.prod
)
79 if not conflitcs
.is_empty
then
80 print
"Error: there is conflicts"
84 if conflitcs
.is_empty
then break
85 print
"Inline {conflitcs.join(" ")}"
86 gram
.inline
(conflitcs
)
90 # Output concrete grammar and LR automaton
93 for prod
in gram
.prods
do nbalts
+= prod
.alts
.length
94 print
"Concrete grammar: {gram.prods.length} productions, {nbalts} alternatives (see {name}.concrete_grammar.txt)"
96 var pretty
= gram
.pretty
97 var f
= new OFStream.open
("{name}.concrete_grammar.txt")
98 f
.write
"// Concrete grammar of {name}\n"
102 print
"LR automaton: {lr.states.length} states (see {name}.lr.dot and {name}.lr.txt)"
103 lr
.to_dot
("{name}.lr.dot")
105 f
= new OFStream.open
("{name}.lr.txt")
106 f
.write
"// LR automaton of {name}\n"
113 print
"NFA automaton: {nfa.states.length} states (see {name}.nfa.dot)"
114 nfa
.to_dot
("{name}.nfa.dot")
116 var dfa
= nfa
.to_dfa
.to_minimal_dfa
118 dfa
.solve_token_inclusion
120 print
"DFA automaton: {dfa.states.length} states (see {name}.dfa.dot)"
121 dfa
.to_dot
("{name}.dfa.dot")
123 if dfa
.tags
.has_key
(dfa
.start
) then
124 print
"Error: Empty tokens {dfa.tags[dfa.start].join(" ")}"
127 for s
, tks
in dfa
.tags
do
128 if tks
.length
<= 1 then continue
129 print
"Error: Conflicting tokens: {tks.join(" ")}"
132 for t
in gram
.tokens
do
133 if t
.name
== "Eof" then continue
134 if dfa
.retrotags
.has_key
(t
) and not dfa
.retrotags
[t
].is_empty
then continue
135 print
"Error: Token {t} matches nothing"
141 print
"Generate {name}_lexer.nit {name}_parser.nit {name}_test_parser.nit"
142 dfa
.gen_to_nit
("{name}_lexer.nit", name
, "{name}_parser")
143 lr
.gen_to_nit
("{name}_parser.nit", name
)
145 f
= new OFStream.open
("{name}_test_parser.nit")
146 f
.write
"""# Generated by nitcc for the language {{{name}}}
148 import {{{name}}}_lexer
149 import {{{name}}}_parser
150 class TestParser_{{{name}}}
152 redef fun name do return \"{{{name}}}\"
153 redef fun new_lexer(text) do return new Lexer_{{{name}}}(text)
154 redef fun new_parser do return new Parser_{{{name}}}
156 var t = new TestParser_{{{name}}}