Merge: Augmented literal strings
authorJean Privat <jean@pryen.org>
Thu, 7 Apr 2016 12:50:34 +0000 (08:50 -0400)
committerJean Privat <jean@pryen.org>
Thu, 7 Apr 2016 12:50:34 +0000 (08:50 -0400)
As a follow-up to #1991, here's the support mentioned in #1734 for bytestrings, regex and raw strings

NOTE: Depends on #1991 for integration, you may review the last 3 commits while #1991 is reviewed and eventually merged

TODO: Support degraded mode (Bytes and Byte only) for byte SuperStrings instead of refusing at compile-time

Pull-Request: #1992
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

1  2 
src/compiler/abstract_compiler.nit
src/interpreter/naive_interpreter.nit

@@@ -1273,21 -1273,17 +1273,21 @@@ abstract class AbstractCompilerVisito
        do
                mtype = self.anchor(mtype)
                var valmtype = value.mcasttype
 +
 +              # Do nothing if useless autocast
                if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
  
 +              # Just as_not_null if the target is not nullable.
 +              #
 +              # eg `nullable PreciseType` adapted to `Object` gives precisetype.
                if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
 -                      var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
 -                      return res
 -              else
 -                      var res = new RuntimeVariable(value.name, valmtype, mtype)
 -                      return res
 +                      mtype = valmtype.mtype
                end
 +
 +              var res = new RuntimeVariable(value.name, value.mtype, mtype)
 +              return res
        end
  
        # Generate a super call from a method definition
  
        # Checks
  
 +      # Can value be null? (according to current knowledge)
 +      fun maybenull(value: RuntimeVariable): Bool
 +      do
 +              return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
 +      end
 +
        # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
  
 -              var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
 -              if maybenull then
 +              if maybenull(recv) then
                        self.add("if (unlikely({recv} == NULL)) \{")
                        self.add_abort("Receiver is null")
                        self.add("\}")
                return res
        end
  
+       # Generates a NativeString instance fully escaped in C-style \xHH fashion
+       fun native_string_instance(ns: NativeString, len: Int): RuntimeVariable do
+               var mtype = mmodule.native_string_type
+               var nat = new_var(mtype)
+               var byte_esc = new Buffer.with_cap(len * 4)
+               for i in [0 .. len[ do
+                       byte_esc.append("\\x{ns[i].to_s.substring_from(2)}")
+               end
+               self.add("{nat} = \"{byte_esc}\";")
+               return nat
+       end
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
@@@ -3107,7 -3110,7 +3119,7 @@@ redef class AAttrPropde
                        assert arguments.length == 2
                        var recv = arguments.first
                        var arg = arguments[1]
 -                      if is_optional then
 +                      if is_optional and v.maybenull(arg) then
                                var value = v.new_var(self.mpropdef.static_mtype.as(not null))
                                v.add("if ({arg} == NULL) \{")
                                v.assign(value, evaluate_expr(v, recv))
@@@ -3529,9 -3532,6 +3541,9 @@@ redef class AOrElseExp
        do
                var res = v.new_var(self.mtype.as(not null))
                var i1 = v.expr(self.n_expr, null)
 +
 +              if not v.maybenull(i1) then return i1
 +
                v.add("if ({i1}!=NULL) \{")
                v.assign(res, i1)
                v.add("\} else \{")
@@@ -3586,14 -3586,75 +3598,75 @@@ redef class AArrayExp
        end
  end
  
+ redef class AugmentedStringFormExpr
+       # Factorize the making of a `Regex` object from a literal prefixed string
+       protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
+               var re = to_re
+               assert re != null
+               var res = v.compile_callsite(re, [rs])
+               if res == null then
+                       print "Cannot call property `to_re` on {self}"
+                       abort
+               end
+               for i in suffix.chars do
+                       if i == 'i' then
+                               var ign = ignore_case
+                               assert ign != null
+                               v.compile_callsite(ign, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'm' then
+                               var nl = newline
+                               assert nl != null
+                               v.compile_callsite(nl, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'b' then
+                               var ext = extended
+                               assert ext != null
+                               v.compile_callsite(ext, [res, v.bool_instance(false)])
+                               continue
+                       end
+                       # Should not happen, this needs to be updated
+                       # along with the addition of new suffixes
+                       abort
+               end
+               return res
+       end
+ end
  redef class AStringFormExpr
-       redef fun expr(v) do return v.string_instance(self.value.as(not null))
+       redef fun expr(v) do return v.string_instance(value)
+ end
+ redef class AStringExpr
+       redef fun expr(v) do
+               var s = v.string_instance(value)
+               if is_string then return s
+               if is_bytestring then
+                       var ns = v.native_string_instance(bytes.items, bytes.length)
+                       var ln = v.int_instance(bytes.length)
+                       var cs = to_bytes_with_copy
+                       assert cs != null
+                       var res = v.compile_callsite(cs, [ns, ln])
+                       assert res != null
+                       s = res
+               else if is_re then
+                       var res = make_re(v, s)
+                       assert res != null
+                       s = res
+               else
+                       print "Unimplemented prefix or suffix for {self}"
+                       abort
+               end
+               return s
+       end
  end
  
  redef class ASuperstringExpr
        redef fun expr(v)
        do
-               var type_string = mtype.as(not null)
+               var type_string = v.mmodule.string_type
  
                # Collect elements of the superstring
                var array = new Array[AExpr]
  
                # Fast join the native string to get the result
                var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+               assert res != null
+               if is_re then res = make_re(v, res)
  
                # We finish to work with the native array,
                # so store it so that it can be reused
                v.add("{varonce} = {a};")
                return res
        end
  end
@@@ -3718,7 -3783,7 +3795,7 @@@ redef class AAsNotnullExp
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
  
 -              if i.mtype.is_c_primitive then return i
 +              if not v.maybenull(i) then return i
  
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
@@@ -328,6 -328,16 +328,16 @@@ class NaiveInterprete
                return instance
        end
  
+       # Return a new native string initialized with `txt`
+       fun native_string_instance_from_ns(txt: NativeString, len: Int): Instance
+       do
+               var instance = native_string_instance_len(len)
+               var val = instance.val
+               txt.copy_to(val, len, 0, 0)
+               return instance
+       end
        # Return a new native string initialized of `length`
        fun native_string_instance_len(length: Int): PrimitiveInstance[NativeString]
        do
@@@ -1002,6 -1012,9 +1012,6 @@@ redef class AMethPropde
                                return v.int32_instance(recvval.to_i32)
                        else if pname == "to_u32" then
                                return v.uint32_instance(recvval.to_u32)
 -                      else if pname == "rand" then
 -                              var res = recvval.rand
 -                              return v.int_instance(res)
                        end
                else if cname == "Byte" then
                        var recvval = args[0].to_b
                                return v.float_instance(args[0].to_f.log)
                        else if pname == "pow" then
                                return v.float_instance(args[0].to_f.pow(args[1].to_f))
 -                      else if pname == "rand" then
 -                              return v.float_instance(args[0].to_f.rand)
                        else if pname == "abs" then
                                return v.float_instance(args[0].to_f.abs)
                        else if pname == "hypot_with" then
@@@ -1833,13 -1848,7 +1843,13 @@@ redef class AWithExp
                v.callsite(method_start, [expr])
                v.stmt(self.n_block)
                v.is_escape(self.break_mark) # Clear the break
 +
 +              # Execute the finally without an escape
 +              var old_mark = v.escapemark
 +              v.escapemark = null
                v.callsite(method_finish, [expr])
 +              # Restore the escape unless another escape was provided
 +              if v.escapemark == null then v.escapemark = old_mark
        end
  end
  
@@@ -1963,11 -1972,71 +1973,71 @@@ redef class AArrayExp
        end
  end
  
+ redef class AugmentedStringFormExpr
+       # Factorize the making of a `Regex` object from a literal prefixed string
+       fun make_re(v: NaiveInterpreter, rs: Instance): nullable Instance do
+               var tore = to_re
+               assert tore != null
+               var res = v.callsite(tore, [rs])
+               if res == null then
+                       print "Cannot call property `to_re` on {self}"
+                       abort
+               end
+               for j in suffix.chars do
+                       if j == 'i' then
+                               var prop = ignore_case
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if j == 'm' then
+                               var prop = newline
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if j == 'b' then
+                               var prop = extended
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(false)])
+                               continue
+                       end
+                       # Should not happen, this needs to be updated
+                       # along with the addition of new suffixes
+                       abort
+               end
+               return res
+       end
+ end
  redef class AStringFormExpr
-       redef fun expr(v)
-       do
-               var txt = self.value.as(not null)
-               return v.string_instance(txt)
+       redef fun expr(v) do return v.string_instance(value)
+ end
+ redef class AStringExpr
+       redef fun expr(v) do
+               var s = v.string_instance(value)
+               if is_string then return s
+               if is_bytestring then
+                       var ns = v.native_string_instance_from_ns(bytes.items, bytes.length)
+                       var ln = v.int_instance(bytes.length)
+                       var prop = to_bytes_with_copy
+                       assert prop != null
+                       var res = v.callsite(prop, [ns, ln])
+                       if res == null then
+                               print "Cannot call property `to_bytes` on {self}"
+                               abort
+                       end
+                       s = res
+               else if is_re then
+                       var res = make_re(v, s)
+                       assert res != null
+                       s = res
+               else
+                       print "Unimplemented prefix or suffix for {self}"
+                       abort
+               end
+               return s
        end
  end
  
@@@ -1983,6 -2052,7 +2053,7 @@@ redef class ASuperstringExp
                var i = v.array_instance(array, v.mainmodule.object_type)
                var res = v.send(v.force_get_primitive_method("plain_to_s", i.mtype), [i])
                assert res != null
+               if is_re then res = make_re(v, res)
                return res
        end
  end