1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Parsing of literal values in the abstract syntax tree.
22 redef class ToolContext
23 # Parses literal values in the whole AST and produces errors if needed
24 var literal_phase
: Phase = new LiteralPhase(self, null)
27 private class LiteralPhase
30 redef fun process_nmodule
(nmodule
) do nmodule
.do_literal
(toolcontext
)
34 # Visit the module to compute the real value of the literal-related node of the AST.
35 # Warnings and errors are displayed on the toolcontext.
36 fun do_literal
(toolcontext
: ToolContext)
38 var v
= new LiteralVisitor(toolcontext
)
43 private class LiteralVisitor
46 var toolcontext
: ToolContext
50 n
.accept_literal
(self)
56 private fun accept_literal
(v
: LiteralVisitor) do end
60 # Get `self` as a `String`.
61 # Return null if not a string.
62 fun as_string
: nullable String
64 if not self isa AStringFormExpr then return null
68 # Get `self` as an `Int`.
69 # Return null if not an integer.
70 fun as_int
: nullable Int
72 if not self isa AIntegerExpr then return null
73 return self.value
.as(not null).to_i
77 redef class AIntegerExpr
78 # The value of the literal int once computed.
79 var value
: nullable Numeric
81 redef fun accept_literal
(v
) do
82 value
= n_integer
.text
.to_num
84 v
.toolcontext
.error
(hot_location
, "Error: invalid literal `{n_integer.text}`")
89 redef class AFloatExpr
90 # The value of the literal float once computed.
91 var value
: nullable Float
92 redef fun accept_literal
(v
)
94 self.value
= self.n_float
.text
.to_f
98 # Any kind of literal which supports a prefix or a suffix
99 class AAugmentedLiteral
100 # Returns the text of the token
101 private fun text
: String is abstract
103 # Is the combination of prefixes and suffixes in `self` valid ?
104 fun is_valid_augmentation
: Bool is abstract
106 private fun delimiter_start
: Char is abstract
108 private fun delimiter_end
: Char is abstract
110 # Prefix for the entity, "" if no prefix is found
111 protected var prefix
: String is lazy
do return text
.substring
(0, text
.index_of
(delimiter_start
))
113 # Suffix for the entity, "" if no prefix is found
114 protected var suffix
: String is lazy
do return text
.substring_from
(text
.last_index_of
(delimiter_end
) + 1)
116 # Content of the entity, without prefix nor suffix
117 protected var content
: String is lazy
do
118 var npr
= text
.substring_from
(prefix
.length
)
119 return npr
.substring
(0, npr
.length
- suffix
.length
)
123 redef class ACharExpr
124 super AAugmentedLiteral
125 # The value of the literal char once computed.
126 var value
: nullable Char = null
128 redef fun delimiter_start
do return '\''
130 redef fun delimiter_end do return '\
''
132 # Is the expression returning a Code Point ?
133 fun is_code_point
: Bool do return prefix
== "u"
135 redef fun text
do return n_char
.text
137 redef fun is_valid_augmentation
do
138 if suffix
!= "" then return false
139 if is_code_point
then return true
140 if prefix
!= "" then return false
144 redef fun accept_literal
(v
)
146 if not is_valid_augmentation
then
147 v
.toolcontext
.error
(hot_location
, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
150 var txt
= content
.unescape_nit
151 if txt
.length
!= 3 then
152 v
.toolcontext
.error
(self.hot_location
, "Syntax Error: invalid character literal `{txt}`.")
155 self.value
= txt
.chars
[1]
159 # Any kind of string form with augmentations from prefixes or suffixes
160 class AugmentedStringFormExpr
161 super AAugmentedLiteral
163 redef var delimiter_start
= '"'
164 redef var delimiter_end
= '"'
166 # Is `self` a regular String object ?
167 fun is_string
: Bool do return prefix
== "" or prefix
== "raw"
169 # Is `self` a Regular Expression ?
170 fun is_re
: Bool do return prefix
== "re"
172 # Is `self` a Byte String ?
173 fun is_bytestring
: Bool do return prefix
== "b"
175 redef fun is_valid_augmentation
do
176 if is_string
and suffix
== "" then return true
177 if is_bytestring
and suffix
== "" then return true
180 for i
in suf
.chars
do
181 if i
== 'i' then continue
182 if i
== 'm' then continue
183 if i
== 'b' then continue
188 if prefix
!= "" or suffix
!= "" then return false
193 redef class AStringFormExpr
194 super AugmentedStringFormExpr
196 # The value of the literal string once computed.
197 var value
: String is noinit
199 # The underlying bytes of the String, non-cleaned for UTF-8
200 var bytes
: Bytes is noinit
202 redef fun text
do return n_string
.text
204 # Returns the raw text read by the lexer
205 var raw_text
: String is lazy
do
209 if txt
.chars
[0] == txt
.chars
[1] and txt
.length
>= 6 then
212 if txt
.chars
[0] == delimiter_start
and txt
.chars
[3] == '\n' then behead
= 4 # ignore first \n in """
214 return txt
.substring
(behead
, txt
.length
- behead
- betail
)
217 redef fun accept_literal
(v
) do
219 bytes
= raw_text
.to_bytes
223 redef class AEndStringExpr
224 redef var delimiter_end
is lazy
do return '"'
225 redef fun prefix
do return ""
228 redef class AStartStringExpr
229 redef var delimiter_start
is lazy
do
230 var str
= n_string
.text
231 for i
in [0 .. str
.length
[ do
233 if c
== '"' or c
== '\'' then
237 # Cannot happen, unless the parser is bugged
241 redef fun suffix do return ""
244 redef class AMidStringExpr
245 redef fun prefix do return ""
246 redef fun suffix do return ""
249 redef class AStringExpr
250 redef var delimiter_start is lazy do
252 for i in [0 .. str.length[ do
254 if c == '"' or c == '\'' then
259 # Cannot happen, unless the parser is bugged
263 redef var delimiter_end is lazy do return delimiter_start
265 redef fun accept_literal(v)
268 if not is_valid_augmentation then
269 v.toolcontext.error(hot_location, "Error: invalid prefix
/suffix combination
{prefix}/{suffix}")
272 if prefix != "raw
" then
273 bytes = raw_text.unescape_to_bytes
279 redef class ASuperstringExpr
280 super AugmentedStringFormExpr
282 redef var prefix is lazy do
283 var fst = n_exprs.first
284 if fst isa AugmentedStringFormExpr then
286 delimiter_start = fst.delimiter_start
287 delimiter_end = delimiter_start
293 redef var suffix is lazy do
294 var lst = n_exprs.last
295 # Forces the system to update the delimiter's value
297 if lst isa AugmentedStringFormExpr then
298 lst.delimiter_end = delimiter_start
304 redef fun accept_literal(v)
306 if is_bytestring then
307 v.toolcontext.error(hot_location, "Error: cannot produce a
ByteString on a
Superstring")
310 if not is_valid_augmentation then
311 v.toolcontext.error(hot_location, "Error: invalid prefix
/suffix combination
{prefix}/{suffix}")
316 redef fun visit_all(v) do
318 if prefix != "raw
" then
320 if not i isa AStringFormExpr then continue
321 i.bytes = i.raw_text.unescape_to_bytes
322 i.value = i.bytes.to_s