# Parsing of literal values in the abstract syntax tree.
module literal
-import parser
-import toolcontext
+import phase
+
+redef class ToolContext
+ # Parses literal values in the whole AST and produces errors if needed
+ var literal_phase: Phase = new LiteralPhase(self, null)
+end
+
+private class LiteralPhase
+ super Phase
+
+ redef fun process_nmodule(nmodule) do nmodule.do_literal(toolcontext)
+end
redef class AModule
# Visit the module to compute the real value of the literal-related node of the AST.
var toolcontext: ToolContext
- init(toolcontext: ToolContext)
+ redef fun visit(n)
do
- self.toolcontext = toolcontext
+ n.accept_literal(self)
+ n.visit_all(self)
end
+end
- redef fun visit(n)
+redef class ANode
+ private fun accept_literal(v: LiteralVisitor) do end
+end
+
+redef class AExpr
+ # Get `self` as a `String`.
+ # Return null if not a string.
+ fun as_string: nullable String
+ do
+ if not self isa AStringFormExpr then return null
+ return self.value.as(not null)
+ end
+
+ # Get `self` as an `Int`.
+ # Return null if not an integer.
+ fun as_int: nullable Int
do
- if n != null then
- n.accept_literal(self)
- n.visit_all(self)
+ if not self isa AIntExpr then return null
+ return self.value.as(not null)
+ end
+
+ # Get `self` as a single identifier.
+ # Return null if not a single identifier.
+ fun as_id: nullable String
+ do
+ if self isa AMethidExpr then
+ return self.collect_text
end
+ if not self isa ACallExpr then return null
+ if not self.n_expr isa AImplicitSelfExpr then return null
+ if not self.n_args.n_exprs.is_empty then return null
+ return self.n_id.text
end
end
-redef class ANode
- private fun accept_literal(v: LiteralVisitor) do end
+redef class Text
+ private fun remove_underscores: Text do
+ var b = new FlatBuffer
+ for i in chars do
+ if i == '_' then continue
+ b.add i
+ end
+ return b
+ end
end
redef class AIntExpr
# The value of the literal int once computed.
var value: nullable Int
+end
+
+redef class ADecIntExpr
+ redef fun accept_literal(v)
+ do
+ value = self.n_number.text.to_i
+ end
+end
+
+redef class AHexIntExpr
+ redef fun accept_literal(v)
+ do
+ var s = self.n_hex_number.text.substring_from(2).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid hexadecimal literal")
+ return
+ end
+ value = s.to_hex
+ end
+end
+
+redef class ABinIntExpr
+ redef fun accept_literal(v)
+ do
+ var s = self.n_bin_number.text.substring_from(2).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid binary literal")
+ return
+ end
+ value = s.to_bin
+ end
+end
+
+redef class AOctIntExpr
redef fun accept_literal(v)
do
- self.value = self.n_number.text.to_i
+ var s = self.n_oct_number.text.substring_from(2).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid octal literal")
+ return
+ end
+ value = s.to_oct
+ end
+end
+
+redef class AByteExpr
+ # The value of the literal int once computed.
+ var value: nullable Byte
+end
+
+redef class ADecByteExpr
+ redef fun accept_literal(v)
+ do
+ var t = self.n_bytenum.text
+ value = t.substring(0, t.length - 2).to_i.to_b
+ end
+end
+
+redef class AHexByteExpr
+ redef fun accept_literal(v)
+ do
+ var t = self.n_hex_bytenum.text
+ var s = t.substring(2, t.length - 4).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid hexadecimal literal")
+ return
+ end
+ value = s.to_hex.to_b
+ end
+end
+
+redef class ABinByteExpr
+ redef fun accept_literal(v)
+ do
+ var t = self.n_bin_bytenum.text
+ var s = t.substring(2, t.length - 4).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid binary literal")
+ return
+ end
+ value = s.to_bin.to_b
+ end
+end
+
+redef class AOctByteExpr
+ redef fun accept_literal(v)
+ do
+ var t = self.n_oct_bytenum.text
+ var s = t.substring(2, t.length - 4).remove_underscores
+ if s.is_empty then
+ v.toolcontext.error(location, "Error: invalid octal literal")
+ return
+ end
+ value = s.to_oct.to_b
end
end
do
var txt = self.n_char.text.unescape_nit
if txt.length != 3 then
- v.toolcontext.error(self.hot_location, "Invalid character literal {txt}")
+ v.toolcontext.error(self.hot_location, "Syntax Error: invalid character literal `{txt}`.")
return
end
- self.value = txt[1]
+ self.value = txt.chars[1]
end
end
var value: nullable String
redef fun accept_literal(v)
do
- var txt
- if self isa AStringExpr then
- txt = self.n_string.text
- else if self isa AStartStringExpr then
- txt = self.n_string.text
- else if self isa AMidStringExpr then
- txt = self.n_string.text
- else if self isa AEndStringExpr then
- txt = self.n_string.text
- else abort
- self.value = txt.substring(1, txt.length-2).unescape_nit
- end
-end
-
-redef class String
- # Return a string where Nit escape sequences are transformed.
- #
- # Example:
- # var s = "\\n"
- # print s.length # -> 2
- # var u = s.unescape_nit
- # print s.length # -> 1
- # print s[0].ascii # -> 10 (the ASCII value of the "new line" character)
- fun unescape_nit: String
- do
- var res = new Buffer.with_capacity(self.length)
- var was_slash = false
- for c in self do
- if not was_slash then
- if c == '\\' then
- was_slash = true
- else
- res.add(c)
- end
- continue
- end
- was_slash = false
- if c == 'n' then
- res.add('\n')
- else if c == 'r' then
- res.add('\r')
- else if c == 't' then
- res.add('\t')
- else if c == '0' then
- res.add('\0')
- else
- res.add(c)
- end
+ var txt = self.n_string.text
+ var behead = 1
+ var betail = 1
+ if txt.chars[0] == txt.chars[1] and txt.length >= 6 then
+ behead = 3
+ betail = 3
+ if txt.chars[0] == '"' and txt.chars[3] == '\n' then behead = 4 # ignore first \n in """
end
- return res.to_s
+ self.value = txt.substring(behead, txt.length - behead - betail).unescape_nit
end
end