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 an ASCII byte value ?
133 fun is_ascii
: Bool do return prefix
== "b"
135 # Is the expression returning a Code Point ?
136 fun is_code_point
: Bool do return prefix
== "u"
138 redef fun text
do return n_char
.text
140 redef fun is_valid_augmentation
do
141 if suffix
!= "" then return false
142 if is_ascii
then return true
143 if is_code_point
then return true
144 if prefix
!= "" then return false
148 redef fun accept_literal
(v
)
150 if not is_valid_augmentation
then
151 v
.toolcontext
.error
(hot_location
, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
154 var txt
= content
.unescape_nit
155 if txt
.length
!= 3 then
156 v
.toolcontext
.error
(self.hot_location
, "Syntax Error: invalid character literal `{txt}`.")
159 self.value
= txt
.chars
[1]
160 if is_ascii
and txt
.chars
[1].code_point
> 127 then v
.toolcontext
.error
(self.hot_location
, "Syntax Error: usage of byte prefix on multibyte character.")
164 # Any kind of string form with augmentations from prefixes or suffixes
165 class AugmentedStringFormExpr
166 super AAugmentedLiteral
168 redef var delimiter_start
= '"'
169 redef var delimiter_end
= '"'
171 # Is `self` a regular String object ?
172 fun is_string
: Bool do return prefix
== "" or prefix
== "raw"
174 # Is `self` a Regular Expression ?
175 fun is_re
: Bool do return prefix
== "re"
177 # Is `self` a Byte String ?
178 fun is_bytestring
: Bool do return prefix
== "b"
180 redef fun is_valid_augmentation
do
181 if is_string
and suffix
== "" then return true
182 if is_bytestring
and suffix
== "" then return true
185 for i
in suf
.chars
do
186 if i
== 'i' then continue
187 if i
== 'm' then continue
188 if i
== 'b' then continue
193 if prefix
!= "" or suffix
!= "" then return false
198 redef class AStringFormExpr
199 super AugmentedStringFormExpr
201 # The value of the literal string once computed.
202 var value
: String is noinit
204 # The underlying bytes of the String, non-cleaned for UTF-8
205 var bytes
: Bytes is noinit
207 redef fun text
do return n_string
.text
209 # Returns the raw text read by the lexer
210 var raw_text
: String is lazy
do
214 if txt
.chars
[0] == txt
.chars
[1] and txt
.length
>= 6 then
217 if txt
.chars
[0] == delimiter_start
and txt
.chars
[3] == '\n' then behead
= 4 # ignore first \n in """
219 return txt
.substring
(behead
, txt
.length
- behead
- betail
)
222 redef fun accept_literal
(v
) do
224 bytes
= raw_text
.to_bytes
228 redef class AEndStringExpr
229 redef var delimiter_end
is lazy
do return '"'
230 redef fun prefix
do return ""
233 redef class AStartStringExpr
234 redef var delimiter_start
is lazy
do
235 var str
= n_string
.text
236 for i
in [0 .. str
.length
[ do
238 if c
== '"' or c
== '\'' then
242 # Cannot happen, unless the parser is bugged
246 redef fun suffix do return ""
249 redef class AMidStringExpr
250 redef fun prefix do return ""
251 redef fun suffix do return ""
254 redef class AStringExpr
255 redef var delimiter_start is lazy do
257 for i in [0 .. str.length[ do
259 if c == '"' or c == '\'' then
264 # Cannot happen, unless the parser is bugged
268 redef var delimiter_end is lazy do return delimiter_start
270 redef fun accept_literal(v)
273 if not is_valid_augmentation then
274 v.toolcontext.error(hot_location, "Error: invalid prefix
/suffix combination
{prefix}/{suffix}")
277 if prefix != "raw
" then
278 bytes = raw_text.unescape_to_bytes
284 redef class ASuperstringExpr
285 super AugmentedStringFormExpr
287 redef var prefix is lazy do
288 var fst = n_exprs.first
289 if fst isa AugmentedStringFormExpr then
291 delimiter_start = fst.delimiter_start
292 delimiter_end = delimiter_start
298 redef var suffix is lazy do
299 var lst = n_exprs.last
300 # Forces the system to update the delimiter's value
302 if lst isa AugmentedStringFormExpr then
303 lst.delimiter_end = delimiter_start
309 redef fun accept_literal(v)
311 if is_bytestring then
312 v.toolcontext.error(hot_location, "Error: cannot produce a
ByteString on a
Superstring")
315 if not is_valid_augmentation then
316 v.toolcontext.error(hot_location, "Error: invalid prefix
/suffix combination
{prefix}/{suffix}")
321 redef fun visit_all(v) do
323 if prefix != "raw
" then
325 if not i isa AStringFormExpr then continue
326 i.bytes = i.raw_text.unescape_to_bytes
327 i.value = i.bytes.to_s