Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / literal.nit
index f9eafa0..5146a91 100644 (file)
@@ -62,154 +62,265 @@ redef class AExpr
        fun as_string: nullable String
        do
                if not self isa AStringFormExpr then return null
-               return self.value.as(not null)
+               return self.value
        end
 
        # Get `self` as an `Int`.
        # Return null if not an integer.
        fun as_int: nullable Int
        do
-               if not self isa AIntExpr then return null
-               return self.value.as(not null)
+               if not self isa AIntegerExpr then return null
+               return self.value.as(not null).to_i
        end
 end
 
-redef class AIntExpr
+redef class AIntegerExpr
        # The value of the literal int once computed.
-       var value: nullable Int
+       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
 
-redef class ADecIntExpr
+redef class AFloatExpr
+       # The value of the literal float once computed.
+       var value: nullable Float
        redef fun accept_literal(v)
        do
-               value = self.n_number.text.remove_underscores.to_i
+               self.value = self.n_float.text.to_f
        end
 end
 
-redef class AHexIntExpr
+# 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 = 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 s = self.n_hex_number.text.substring_from(2).remove_underscores
-               if s.is_empty then
-                       v.toolcontext.error(location, "Error: invalid hexadecimal literal")
+               if not is_valid_augmentation then
+                       v.toolcontext.error(hot_location, "Syntax Error: invalid prefix/suffix combination {prefix}/{suffix}")
                        return
                end
-               value = s.to_hex
+               var txt = content.unescape_nit
+               if txt.length != 3 then
+                       v.toolcontext.error(self.hot_location, "Syntax Error: invalid character literal `{txt}`.")
+                       return
+               end
+               self.value = txt.chars[1]
        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
+# 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
-               value = s.to_bin
+               if prefix != "" or suffix != "" then return false
+               return true
        end
 end
 
-redef class AOctIntExpr
-       redef fun accept_literal(v)
-       do
-               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
+redef class AStringFormExpr
+       super AugmentedStringFormExpr
+
+       # The value of the literal string once computed.
+       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
-               value = s.to_oct
+               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 AByteExpr
-       # The value of the literal int once computed.
-       var value: nullable Byte
+redef class AEndStringExpr
+       redef var delimiter_end is lazy do return '"'
+       redef fun prefix do return ""
 end
 
-redef class ADecByteExpr
-       redef fun accept_literal(v)
-       do
-               var t = self.n_bytenum.text
-               value = t.substring(0, t.length - 2).remove_underscores.to_i.to_b
+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 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
+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
-               value = s.to_hex.to_b
+               # Cannot happen, unless the parser is bugged
+               abort
        end
-end
 
-redef class ABinByteExpr
+       redef var delimiter_end is lazy do return delimiter_start
+
        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")
+               super
+               if not is_valid_augmentation then
+                       v.toolcontext.error(hot_location, "Error: invalid prefix/suffix combination {prefix}/{suffix}")
                        return
                end
-               value = s.to_bin.to_b
+               if prefix != "raw" then
+                       bytes = raw_text.unescape_to_bytes
+                       value = bytes.to_s
+               end
        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
+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
-               value = s.to_oct.to_b
+               return ""
        end
-end
 
-redef class AFloatExpr
-       # The value of the literal float once computed.
-       var value: nullable Float
-       redef fun accept_literal(v)
-       do
-               self.value = self.n_float.text.to_f
+       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
-end
 
-redef class ACharExpr
-       # The value of the literal char once computed.
-       var value: nullable Char
        redef fun accept_literal(v)
        do
-               var txt = self.n_char.text.unescape_nit
-               if txt.length != 3 then
-                       v.toolcontext.error(self.hot_location, "Syntax Error: invalid character literal `{txt}`.")
+               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
-               self.value = txt.chars[1]
        end
-end
 
-redef class AStringFormExpr
-       # The value of the literal string once computed.
-       var value: nullable String
-       redef fun accept_literal(v)
-       do
-               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 """
+       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
-               self.value = txt.substring(behead, txt.length - behead - betail).unescape_nit
        end
 end