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 FileReader.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]
76 for i
in s
.conflicting_items
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.out)"
96 var pretty
= gram
.pretty
97 var f
= new FileWriter.open
("{name}.concrete_grammar.out")
98 f
.write
"// Concrete grammar of {name}\n"
102 print
"LR automaton: {lr.states.length} states (see {name}.lr.dot and {name}.lr.out)"
103 lr
.to_dot
("{name}.lr.dot")
105 f
= new FileWriter.open
("{name}.lr.out")
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 FileWriter.open
("{name}_test_parser.nit")
146 f
.write
"""# Generated by nitcc for the language {{{name}}}
148 # Standalone parser tester for the language {{{name}}}
149 module {{{name}}}_test_parser
151 import {{{name}}}_lexer
152 import {{{name}}}_parser
154 # Class to test the parser for the language {{{name}}}
155 class TestParser_{{{name}}}
157 redef fun name do return \"{{{name}}}\"
158 redef fun new_lexer(text) do return new Lexer_{{{name}}}(text)
159 redef fun new_parser do return new Parser_{{{name}}}
161 var t = new TestParser_{{{name}}}