Merge: doc: fixed some typos and other misc. corrections
[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 a Code Point ?
133 fun is_code_point: Bool do return prefix == "u"
134
135 redef fun text do return n_char.text
136
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
141 return true
142 end
143
144 redef fun accept_literal(v)
145 do
146 if not is_valid_augmentation then
147 v.toolcontext.error(hot_location, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
148 return
149 end
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}`.")
153 return
154 end
155 self.value = txt.chars[1]
156 end
157 end
158
159 # Any kind of string form with augmentations from prefixes or suffixes
160 class AugmentedStringFormExpr
161 super AAugmentedLiteral
162
163 redef var delimiter_start = '"'
164 redef var delimiter_end = '"'
165
166 # Is `self` a regular String object ?
167 fun is_string: Bool do return prefix == "" or prefix == "raw"
168
169 # Is `self` a Regular Expression ?
170 fun is_re: Bool do return prefix == "re"
171
172 # Is `self` a Byte String ?
173 fun is_bytestring: Bool do return prefix == "b"
174
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
178 if is_re then
179 var suf = suffix
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
184 return false
185 end
186 return true
187 end
188 if prefix != "" or suffix != "" then return false
189 return true
190 end
191 end
192
193 redef class AStringFormExpr
194 super AugmentedStringFormExpr
195
196 # The value of the literal string once computed.
197 var value: String is noinit
198
199 # The underlying bytes of the String, non-cleaned for UTF-8
200 var bytes: Bytes is noinit
201
202 redef fun text do return n_string.text
203
204 # Returns the raw text read by the lexer
205 var raw_text: String is lazy do
206 var txt = content
207 var behead = 1
208 var betail = 1
209 if txt.chars[0] == txt.chars[1] and txt.length >= 6 then
210 behead = 3
211 betail = 3
212 if txt.chars[0] == delimiter_start and txt.chars[3] == '\n' then behead = 4 # ignore first \n in """
213 end
214 return txt.substring(behead, txt.length - behead - betail)
215 end
216
217 redef fun accept_literal(v) do
218 value = raw_text
219 bytes = raw_text.to_bytes
220 end
221 end
222
223 redef class AEndStringExpr
224 redef var delimiter_end is lazy do return '"'
225 redef fun prefix do return ""
226 end
227
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
232 var c = str[i]
233 if c == '"' or c == '\'' then
234 return c
235 end
236 end
237 # Cannot happen, unless the parser is bugged
238 abort
239 end
240
241 redef fun suffix do return ""
242 end
243
244 redef class AMidStringExpr
245 redef fun prefix do return ""
246 redef fun suffix do return ""
247 end
248
249 redef class AStringExpr
250 redef var delimiter_start is lazy do
251 var str = text
252 for i in [0 .. str.length[ do
253 var c = str[i]
254 if c == '"' or c == '\'' then
255 delimiter_end = c
256 return c
257 end
258 end
259 # Cannot happen, unless the parser is bugged
260 abort
261 end
262
263 redef var delimiter_end is lazy do return delimiter_start
264
265 redef fun accept_literal(v)
266 do
267 super
268 if not is_valid_augmentation then
269 v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}")
270 return
271 end
272 if prefix != "raw" then
273 bytes = raw_text.unescape_to_bytes
274 value = bytes.to_s
275 end
276 end
277 end
278
279 redef class ASuperstringExpr
280 super AugmentedStringFormExpr
281
282 redef var prefix is lazy do
283 var fst = n_exprs.first
284 if fst isa AugmentedStringFormExpr then
285 var prf = fst.prefix
286 delimiter_start = fst.delimiter_start
287 delimiter_end = delimiter_start
288 return prf
289 end
290 return ""
291 end
292
293 redef var suffix is lazy do
294 var lst = n_exprs.last
295 # Forces the system to update the delimiter's value
296 prefix
297 if lst isa AugmentedStringFormExpr then
298 lst.delimiter_end = delimiter_start
299 return lst.suffix
300 end
301 return ""
302 end
303
304 redef fun accept_literal(v)
305 do
306 if is_bytestring then
307 v.toolcontext.error(hot_location, "Error: cannot produce a ByteString on a Superstring")
308 return
309 end
310 if not is_valid_augmentation then
311 v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}")
312 return
313 end
314 end
315
316 redef fun visit_all(v) do
317 super
318 if prefix != "raw" then
319 for i in n_exprs do
320 if not i isa AStringFormExpr then continue
321 i.bytes = i.raw_text.unescape_to_bytes
322 i.value = i.bytes.to_s
323 end
324 end
325 end
326 end