compiler: Added prefix and suffix support for `Char`
[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.as(not null)
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 return text.substring_from(text.index_of(delimiter_start)).substring(0, text.last_index_of(delimiter_end) + 1)
118 end
119
120 redef class ACharExpr
121 super AAugmentedLiteral
122 # The value of the literal char once computed.
123 var value: nullable Char = null
124
125 redef fun delimiter_start do return '\''
126
127 redef fun delimiter_end do return '\''
128
129 # Is the expression returning an ASCII byte value ?
130 fun is_ascii: Bool do return prefix == "b"
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_ascii then return true
140 if is_code_point then return true
141 if prefix != "" then return false
142 return true
143 end
144
145 redef fun accept_literal(v)
146 do
147 if not is_valid_augmentation then
148 v.toolcontext.error(hot_location, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
149 return
150 end
151 var txt = content.unescape_nit
152 if txt.length != 3 then
153 v.toolcontext.error(self.hot_location, "Syntax Error: invalid character literal `{txt}`.")
154 return
155 end
156 self.value = txt.chars[1]
157 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.")
158 end
159 end
160
161 redef class AStringFormExpr
162 # The value of the literal string once computed.
163 var value: nullable String
164 redef fun accept_literal(v)
165 do
166 var txt = self.n_string.text
167 var behead = 1
168 var betail = 1
169 if txt.chars[0] == txt.chars[1] and txt.length >= 6 then
170 behead = 3
171 betail = 3
172 if txt.chars[0] == '"' and txt.chars[3] == '\n' then behead = 4 # ignore first \n in """
173 end
174 self.value = txt.substring(behead, txt.length - behead - betail).unescape_nit
175 end
176 end