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 # Utils and tools related to parsers and AST
21 redef class ToolContext
22 # Parse a full module given as a string
23 # Fatal error if the `string` is not a syntactically correct module
24 fun parse_module
(string
: String): AModule
26 var source
= new SourceFile.from_string
("", string
)
27 var lexer
= new Lexer(source
)
28 var parser
= new Parser(lexer
)
29 var tree
= parser
.parse
32 if eof
isa AError then
33 self.fatal_error
(null, "Fatal Error: {eof.message}")
36 return tree
.n_base
.as(not null)
39 # Parse a full classdef given as a string
40 # Fatal error if the `string` is not a syntactically correct class definition
41 fun parse_classdef
(string
: String): AClassdef
43 var nmodule
= parse_module
(string
)
44 var nclassdefs
= nmodule
.n_classdefs
45 if nclassdefs
.length
!= 1 then
46 self.fatal_error
(null, "Fatal Error: not a classdef")
49 return nclassdefs
.first
52 # Parse a full propdef given as a string
53 # Fatal error if the `string` is not a syntactically correct property definition
54 fun parse_propdef
(string
: String): APropdef
56 var mod_string
= "class Dummy\n{string}\nend"
57 var nclassdef
= parse_classdef
(mod_string
)
58 var npropdefs
= nclassdef
.n_propdefs
59 if npropdefs
.length
!= 1 then
60 self.fatal_error
(null, "Fatal Error: not a propdef")
63 return npropdefs
.first
66 # Parse a full statement block given as a string
67 # Fatal error if the `string` is not a syntactically correct statement block
68 fun parse_stmts
(string
: String): AExpr
70 var mod_string
= "do\n{string}\nend"
71 var nmodule
= parse_module
(mod_string
)
72 var nblock
= nmodule
.n_classdefs
.first
.n_propdefs
.first
.as(AMethPropdef).n_block
.as(ABlockExpr).n_expr
.first
.as(ADoExpr).n_block
.as(not null)
76 # Parse a full expression given as a string
77 # Fatal error if the `string` is not a syntactically correct expression
78 fun parse_expr
(string
: String): AExpr
80 var mod_string
= "var dummy = \n{string}"
81 var nmodule
= parse_module
(mod_string
)
82 var nexpr
= nmodule
.n_classdefs
.first
.n_propdefs
.first
.as(AMethPropdef).n_block
.as(ABlockExpr).n_expr
.first
.as(AVardeclExpr).n_expr
.as(not null)
86 # Parse a super class declaration
87 # Fatal error if the `string` is not a syntactically correct super class declaration
88 fun parse_superclass
(string
: String): ASuperclass
90 var mod_string
= "class Dummy\nsuper {string}\nend"
91 var nclassdef
= parse_classdef
(mod_string
).as(AStdClassdef)
92 var nsuperclasses
= nclassdef
.n_superclasses
93 if nsuperclasses
.length
!= 1 then
94 self.fatal_error
(null, "Fatal Error: not a super class declaration")
97 return nsuperclasses
.first
100 # Try to parse the `string` as something
102 # Returns the first possible syntacticaly correct type among:
106 # - an expression `AExpr`
107 # - a block of statements `ABlockExpr`
108 # - a full module `AModule`
109 # - a `AError` if nothing else matches
111 # var tc = new ToolContext
112 # assert tc.parse_something("foo") isa TId
113 # assert tc.parse_something("foo[bar]") isa AExpr
114 # assert tc.parse_something("Foo[Bar]") isa AType
115 # assert tc.parse_something("foo\nbar") isa ABlockExpr
116 # assert tc.parse_something("fun foo do bar\nfoo") isa AModule
117 # assert tc.parse_something("fun fun") isa AParserError
118 # assert tc.parse_something("?%^&") isa ALexerError
119 fun parse_something
(string
: String): ANode
121 var source
= new SourceFile.from_string
("", string
)
127 lexer
= new InjectedLexer(source
)
128 lexer
.injected_before
.add
new TKwvar
129 lexer
.injected_before
.add
new TId
130 lexer
.injected_before
.add
new TColumn
131 lexer
.injected_before
.add
new TClassid
132 lexer
.injected_before
.add
new TObra
133 lexer
.injected_after
.add
new TCbra
134 tree
= (new Parser(lexer
)).parse
136 if not eof
isa AError then
137 var ntype
= tree
.n_base
.n_classdefs
.first
.n_propdefs
.first
.as(AMethPropdef).n_block
.as(ABlockExpr).n_expr
.first
.as(AVardeclExpr).n_type
.n_types
.first
143 lexer
= new Lexer(source
)
144 var first
= lexer
.next
145 if first
isa EOF then return first
146 var second
= lexer
.next
147 if second
isa EOF and not second
isa AError then
152 lexer
= new InjectedLexer(source
)
153 lexer
.injected_before
.add
new TKwvar
154 lexer
.injected_before
.add
new TId
155 lexer
.injected_before
.add
new TAssign
156 lexer
.injected_before
.add
new TOpar
157 lexer
.injected_after
.add
new TCpar
158 tree
= (new Parser(lexer
)).parse
160 if not eof
isa AError then
161 var nexpr
= tree
.n_base
.n_classdefs
.first
.n_propdefs
.first
.as(AMethPropdef).n_block
.as(ABlockExpr).n_expr
.first
.as(AVardeclExpr).n_expr
.as(AParExpr).n_expr
165 if eof
.location
> error
.location
then error
= eof
167 lexer
= new InjectedLexer(source
)
168 lexer
.injected_before
.add
new TKwdo
169 lexer
.injected_before
.add
new TEol
170 lexer
.injected_after
.add
new TEol
171 lexer
.injected_after
.add
new TKwend
172 tree
= (new Parser(lexer
)).parse
174 if not eof
isa AError then
175 var nblock
= tree
.n_base
.n_classdefs
.first
.n_propdefs
.first
.as(AMethPropdef).n_block
.as(ABlockExpr).n_expr
.first
.as(ADoExpr).n_block
.as(ABlockExpr)
176 nblock
.n_kwend
= null # drop injected token
180 if eof
.location
> error
.location
then error
= eof
182 lexer
= new Lexer(source
)
183 tree
= (new Parser(lexer
)).parse
185 if not eof
isa AError then
186 return tree
.n_base
.as(not null)
188 if eof
.location
> error
.location
then error
= eof
193 # Parse the input of the user as something
194 fun interactive_parse
(prompt
: String): ANode
201 var s
= sys
.stdin
.read_line
202 if s
== "" then continue
203 if s
.chars
.first
== ':' then
204 var res
= new TString
209 var text
= oldtext
+ s
+ "\n"
211 var n
= parse_something
(text
)
213 if n
isa AParserError and n
.token
isa EOF then
214 # Unexpected end of file, thus continuing
215 if oldtext
== "" then prompt
= "." * prompt
.length
228 var injected_before
= new List[Token]
229 var injected_after
= new List[Token]
230 private var is_finished
= false
234 if not injected_before
.is_empty
then
235 var tok
= injected_before
.shift
238 if not is_finished
then
240 if not next
isa EOF then return next
241 injected_after
.push
(next
)
245 var tok
= injected_after
.shift
251 # Do a deep search and return an array of tokens that match a given text
252 fun collect_tokens_by_text
(text
: String): Array[Token]
254 var v
= new CollectTokensByTextVisitor(text
)
259 # Do a deep search and return an array of node that are annotated
260 # The attached node can be retrieved by two invocation of parent
261 fun collect_annotations_by_name
(name
: String): Array[AAnnotation]
263 var v
= new CollectAnnotationsByNameVisitor(name
)
269 private class CollectTokensByTextVisitor
272 init(text
: String) do self.text
= text
273 var result
= new Array[Token]
274 redef fun visit
(node
)
277 if node
isa Token and node
.text
== text
then result
.add
(node
)
281 private class CollectAnnotationsByNameVisitor
284 init(name
: String) do self.name
= name
285 var result
= new Array[AAnnotation]
286 redef fun visit
(node
)
289 if node
isa AAnnotation and node
.n_atid
.n_id
.text
== name
then result
.add
(node
)