X-Git-Url: http://nitlanguage.org diff --git a/src/literal.nit b/src/literal.nit index 5d69035..5146a91 100644 --- a/src/literal.nit +++ b/src/literal.nit @@ -17,13 +17,10 @@ # Parsing of literal values in the abstract syntax tree. module literal -import parser -import toolcontext import phase -import modelbuilder #FIXME useless - redef class ToolContext + # Parses literal values in the whole AST and produces errors if needed var literal_phase: Phase = new LiteralPhase(self, null) end @@ -48,17 +45,10 @@ private class LiteralVisitor var toolcontext: ToolContext - init(toolcontext: ToolContext) - do - self.toolcontext = toolcontext - end - redef fun visit(n) do - if n != null then - n.accept_literal(self) - n.visit_all(self) - end + n.accept_literal(self) + n.visit_all(self) end end @@ -66,12 +56,33 @@ redef class ANode private fun accept_literal(v: LiteralVisitor) do end end -redef class AIntExpr - # The value of the literal int once computed. - var value: nullable Int - redef fun accept_literal(v) +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 + end + + # Get `self` as an `Int`. + # Return null if not an integer. + fun as_int: nullable Int do - self.value = self.n_number.text.to_i + if not self isa AIntegerExpr then return null + return self.value.as(not null).to_i + end +end + +redef class AIntegerExpr + # The value of the literal int once computed. + var value: nullable Numeric + + redef fun accept_literal(v) do + value = n_integer.text.to_num + if value == null then + v.toolcontext.error(hot_location, "Error: invalid literal `{n_integer.text}`") + end end end @@ -84,74 +95,232 @@ redef class AFloatExpr end end +# Any kind of literal which supports a prefix or a suffix +class AAugmentedLiteral + # Returns the text of the token + private fun text: String is abstract + + # Is the combination of prefixes and suffixes in `self` valid ? + fun is_valid_augmentation: Bool is abstract + + private fun delimiter_start: Char is abstract + + private fun delimiter_end: Char is abstract + + # Prefix for the entity, "" if no prefix is found + protected var prefix: String is lazy do return text.substring(0, text.index_of(delimiter_start)) + + # Suffix for the entity, "" if no prefix is found + protected var suffix: String is lazy do return text.substring_from(text.last_index_of(delimiter_end) + 1) + + # Content of the entity, without prefix nor suffix + protected var content: String is lazy do + var npr = text.substring_from(prefix.length) + return npr.substring(0, npr.length - suffix.length) + end +end + redef class ACharExpr + super AAugmentedLiteral # The value of the literal char once computed. - var value: nullable Char + var value: nullable Char = null + + redef fun delimiter_start do return '\'' + + redef fun delimiter_end do return '\'' + + # Is the expression returning a Code Point ? + fun is_code_point: Bool do return prefix == "u" + + redef fun text do return n_char.text + + redef fun is_valid_augmentation do + if suffix != "" then return false + if is_code_point then return true + if prefix != "" then return false + return true + end + redef fun accept_literal(v) do - var txt = self.n_char.text.unescape_nit + if not is_valid_augmentation then + v.toolcontext.error(hot_location, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}") + return + end + var txt = content.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 + +# Any kind of string form with augmentations from prefixes or suffixes +class AugmentedStringFormExpr + super AAugmentedLiteral + + redef var delimiter_start = '"' + redef var delimiter_end = '"' + + # Is `self` a regular String object ? + fun is_string: Bool do return prefix == "" or prefix == "raw" + + # Is `self` a Regular Expression ? + fun is_re: Bool do return prefix == "re" + + # Is `self` a Byte String ? + fun is_bytestring: Bool do return prefix == "b" + + redef fun is_valid_augmentation do + if is_string and suffix == "" then return true + if is_bytestring and suffix == "" then return true + if is_re then + var suf = suffix + for i in suf.chars do + if i == 'i' then continue + if i == 'm' then continue + if i == 'b' then continue + return false + end + return true + end + if prefix != "" or suffix != "" then return false + return true end end redef class AStringFormExpr + super AugmentedStringFormExpr + # The value of the literal string once computed. - var value: nullable String + var value: String is noinit + + # The underlying bytes of the String, non-cleaned for UTF-8 + var bytes: Bytes is noinit + + redef fun text do return n_string.text + + # Returns the raw text read by the lexer + var raw_text: String is lazy do + var txt = content + 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] == delimiter_start and txt.chars[3] == '\n' then behead = 4 # ignore first \n in """ + end + return txt.substring(behead, txt.length - behead - betail) + end + + redef fun accept_literal(v) do + value = raw_text + bytes = raw_text.to_bytes + end +end + +redef class AEndStringExpr + redef var delimiter_end is lazy do return '"' + redef fun prefix do return "" +end + +redef class AStartStringExpr + redef var delimiter_start is lazy do + var str = n_string.text + for i in [0 .. str.length[ do + var c = str[i] + if c == '"' or c == '\'' then + return c + end + end + # Cannot happen, unless the parser is bugged + abort + end + + redef fun suffix do return "" +end + +redef class AMidStringExpr + redef fun prefix do return "" + redef fun suffix do return "" +end + +redef class AStringExpr + redef var delimiter_start is lazy do + var str = text + for i in [0 .. str.length[ do + var c = str[i] + if c == '"' or c == '\'' then + delimiter_end = c + return c + end + end + # Cannot happen, unless the parser is bugged + abort + end + + redef var delimiter_end is lazy do return delimiter_start + 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 + super + if not is_valid_augmentation then + v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}") + return + end + if prefix != "raw" then + bytes = raw_text.unescape_to_bytes + value = bytes.to_s + end + end +end + +redef class ASuperstringExpr + super AugmentedStringFormExpr + + redef var prefix is lazy do + var fst = n_exprs.first + if fst isa AugmentedStringFormExpr then + var prf = fst.prefix + delimiter_start = fst.delimiter_start + delimiter_end = delimiter_start + return prf + end + return "" + end + + redef var suffix is lazy do + var lst = n_exprs.last + # Forces the system to update the delimiter's value + prefix + if lst isa AugmentedStringFormExpr then + lst.delimiter_end = delimiter_start + return lst.suffix + end + return "" + end + + redef fun accept_literal(v) 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) + if is_bytestring then + v.toolcontext.error(hot_location, "Error: cannot produce a ByteString on a Superstring") + return + end + if not is_valid_augmentation then + v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}") + return + end + end + + redef fun visit_all(v) do + super + if prefix != "raw" then + for i in n_exprs do + if not i isa AStringFormExpr then continue + i.bytes = i.raw_text.unescape_to_bytes + i.value = i.bytes.to_s end end - return res.to_s end end