tests: add some runtime error in nitin.input
[nit.git] / src / literal.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Parsing of literal values in the abstract syntax tree.
18 module literal
19
20 import phase
21
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)
25 end
26
27 private class LiteralPhase
28 super Phase
29
30 redef fun process_nmodule(nmodule) do nmodule.do_literal(toolcontext)
31 end
32
33 redef class AModule
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)
37 do
38 var v = new LiteralVisitor(toolcontext)
39 v.enter_visit(self)
40 end
41 end
42
43 private class LiteralVisitor
44 super Visitor
45
46 var toolcontext: ToolContext
47
48 redef fun visit(n)
49 do
50 n.accept_literal(self)
51 n.visit_all(self)
52 end
53 end
54
55 redef class ANode
56 private fun accept_literal(v: LiteralVisitor) do end
57 end
58
59 redef class AExpr
60 # Get `self` as a `String`.
61 # Return null if not a string.
62 fun as_string: nullable String
63 do
64 if not self isa AStringFormExpr then return null
65 return self.value
66 end
67
68 # Get `self` as an `Int`.
69 # Return null if not an integer.
70 fun as_int: nullable Int
71 do
72 if not self isa AIntegerExpr then return null
73 return self.value.as(not null).to_i
74 end
75 end
76
77 redef class AIntegerExpr
78 # The value of the literal int once computed.
79 var value: nullable Numeric
80
81 redef fun accept_literal(v) do
82 value = n_integer.text.to_num
83 if value == null then
84 v.toolcontext.error(hot_location, "Error: invalid literal `{n_integer.text}`")
85 end
86 end
87 end
88
89 redef class AFloatExpr
90 # The value of the literal float once computed.
91 var value: nullable Float
92 redef fun accept_literal(v)
93 do
94 self.value = self.n_float.text.to_f
95 end
96 end
97
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
102
103 # Is the combination of prefixes and suffixes in `self` valid ?
104 fun is_valid_augmentation: Bool is abstract
105
106 private fun delimiter_start: Char is abstract
107
108 private fun delimiter_end: Char is abstract
109
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))
112
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)
115
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)
120 end
121 end
122
123 redef class ACharExpr
124 super AAugmentedLiteral
125 # The value of the literal char once computed.
126 var value: nullable Char = null
127
128 redef fun delimiter_start do return '\''
129
130 redef fun delimiter_end do return '\''
131
132 # Is the expression returning an ASCII byte value ?
133 fun is_ascii: Bool do return prefix == "b"
134
135 # Is the expression returning a Code Point ?
136 fun is_code_point: Bool do return prefix == "u"
137
138 redef fun text do return n_char.text
139
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
145 return true
146 end
147
148 redef fun accept_literal(v)
149 do
150 if not is_valid_augmentation then
151 v.toolcontext.error(hot_location, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
152 return
153 end
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}`.")
157 return
158 end
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.")
161 end
162 end
163
164 # Any kind of string form with augmentations from prefixes or suffixes
165 class AugmentedStringFormExpr
166 super AAugmentedLiteral
167
168 redef var delimiter_start = '"'
169 redef var delimiter_end = '"'
170
171 # Is `self` a regular String object ?
172 fun is_string: Bool do return prefix == "" or prefix == "raw"
173
174 # Is `self` a Regular Expression ?
175 fun is_re: Bool do return prefix == "re"
176
177 # Is `self` a Byte String ?
178 fun is_bytestring: Bool do return prefix == "b"
179
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
183 if is_re then
184 var suf = suffix
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
189 return false
190 end
191 return true
192 end
193 if prefix != "" or suffix != "" then return false
194 return true
195 end
196 end
197
198 redef class AStringFormExpr
199 super AugmentedStringFormExpr
200
201 # The value of the literal string once computed.
202 var value: String is noinit
203
204 # The underlying bytes of the String, non-cleaned for UTF-8
205 var bytes: Bytes is noinit
206
207 redef fun text do return n_string.text
208
209 # Returns the raw text read by the lexer
210 var raw_text: String is lazy do
211 var txt = content
212 var behead = 1
213 var betail = 1
214 if txt.chars[0] == txt.chars[1] and txt.length >= 6 then
215 behead = 3
216 betail = 3
217 if txt.chars[0] == delimiter_start and txt.chars[3] == '\n' then behead = 4 # ignore first \n in """
218 end
219 return txt.substring(behead, txt.length - behead - betail)
220 end
221
222 redef fun accept_literal(v) do
223 value = raw_text
224 bytes = raw_text.to_bytes
225 end
226 end
227
228 redef class AEndStringExpr
229 redef var delimiter_end is lazy do return '"'
230 redef fun prefix do return ""
231 end
232
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
237 var c = str[i]
238 if c == '"' or c == '\'' then
239 return c
240 end
241 end
242 # Cannot happen, unless the parser is bugged
243 abort
244 end
245
246 redef fun suffix do return ""
247 end
248
249 redef class AMidStringExpr
250 redef fun prefix do return ""
251 redef fun suffix do return ""
252 end
253
254 redef class AStringExpr
255 redef var delimiter_start is lazy do
256 var str = text
257 for i in [0 .. str.length[ do
258 var c = str[i]
259 if c == '"' or c == '\'' then
260 delimiter_end = c
261 return c
262 end
263 end
264 # Cannot happen, unless the parser is bugged
265 abort
266 end
267
268 redef var delimiter_end is lazy do return delimiter_start
269
270 redef fun accept_literal(v)
271 do
272 super
273 if not is_valid_augmentation then
274 v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}")
275 return
276 end
277 if prefix != "raw" then
278 bytes = raw_text.unescape_to_bytes
279 value = bytes.to_s
280 end
281 end
282 end
283
284 redef class ASuperstringExpr
285 super AugmentedStringFormExpr
286
287 redef var prefix is lazy do
288 var fst = n_exprs.first
289 if fst isa AugmentedStringFormExpr then
290 var prf = fst.prefix
291 delimiter_start = fst.delimiter_start
292 delimiter_end = delimiter_start
293 return prf
294 end
295 return ""
296 end
297
298 redef var suffix is lazy do
299 var lst = n_exprs.last
300 # Forces the system to update the delimiter's value
301 prefix
302 if lst isa AugmentedStringFormExpr then
303 lst.delimiter_end = delimiter_start
304 return lst.suffix
305 end
306 return ""
307 end
308
309 redef fun accept_literal(v)
310 do
311 if is_bytestring then
312 v.toolcontext.error(hot_location, "Error: cannot produce a ByteString on a Superstring")
313 return
314 end
315 if not is_valid_augmentation then
316 v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}")
317 return
318 end
319 end
320
321 redef fun visit_all(v) do
322 super
323 if prefix != "raw" then
324 for i in n_exprs do
325 if not i isa AStringFormExpr then continue
326 i.bytes = i.raw_text.unescape_to_bytes
327 i.value = i.bytes.to_s
328 end
329 end
330 end
331 end