lib/standard: Introduce Byte type in kernel
authorLucas Bajolet <r4pass@hotmail.com>
Tue, 26 May 2015 20:36:07 +0000 (16:36 -0400)
committerLucas Bajolet <r4pass@hotmail.com>
Fri, 29 May 2015 14:05:02 +0000 (10:05 -0400)
Signed-off-by: Lucas Bajolet <r4pass@hotmail.com>

lib/standard/kernel.nit
lib/standard/string.nit
lib/standard/string_nit.c
lib/standard/string_nit.h
src/compiler/abstract_compiler.nit
src/compiler/separate_compiler.nit
src/ffi/java.nit
src/interpreter/naive_interpreter.nit
src/model/model.nit
src/nitni/nitni_base.nit
src/rapid_type_analysis.nit

index ffd684a..c596bb3 100644 (file)
@@ -275,6 +275,12 @@ interface Numeric
        #     assert 5.to_f         != 5 # Float and Int are not equals
        fun to_f: Float is abstract
 
+       # The byte equivalent of `self`
+       #
+       #     assert (-1).to_b == 0xFF.to_b
+       #     assert (1.9).to_b == 1.to_b
+       fun to_b: Byte is abstract
+
        # Is this the value of zero in its domain?
        fun is_zero: Bool do return self == zero
 
@@ -344,6 +350,7 @@ universal Float
 
        redef fun to_i is intern
        redef fun to_f do return self
+       redef fun to_b is intern
 
        redef fun zero do return 0.0
        redef fun value_of(val) do return val.to_f
@@ -402,6 +409,110 @@ universal Float
        end
 end
 
+# Native bytes.
+# Same as a C `unsigned char`
+universal Byte
+       super Discrete
+       super Numeric
+
+       redef type OTHER: Byte
+
+       redef fun successor(i) do return self + i.to_b
+       redef fun predecessor(i) do return self - i.to_b
+
+       redef fun object_id is intern
+       redef fun hash do return self.to_i
+       redef fun ==(i) is intern
+       redef fun !=(i) is intern
+       redef fun output is intern
+
+       redef fun <=(i) is intern
+       redef fun <(i) is intern
+       redef fun >=(i) is intern
+       redef fun >(i) is intern
+       redef fun +(i) is intern
+
+       # On an Byte, unary minus will return `(256 - self) % 256`
+       #
+       #     assert -(1.to_b) == 0xFF.to_b
+       #     assert -(0.to_b) == 0x00.to_b
+       redef fun - is intern
+       redef fun -(i) is intern
+       redef fun *(i) is intern
+       redef fun /(i) is intern
+
+       # Modulo of `self` with `i`.
+       #
+       # Finds the remainder of division of `self` by `i`.
+       #
+       #     assert 5.to_b % 2.to_b            == 1.to_b
+       #     assert 10.to_b % 2.to_b           == 0.to_b
+       fun %(i: Byte): Byte is intern
+
+       redef fun zero do return 0.to_b
+       redef fun value_of(val) do return val.to_b
+
+       # `i` bits shift fo the left (aka <<)
+       #
+       #     assert 5.to_b.lshift(1)    == 10.to_b
+       fun lshift(i: Int): Byte is intern
+
+       # alias of `lshift`
+       fun <<(i: Int): Byte do return lshift(i)
+
+       # `i` bits shift fo the right (aka >>)
+       #
+       #     assert 5.to_b.rshift(1)    == 2.to_b
+       fun rshift(i: Int): Byte is intern
+
+       # alias of `rshift`
+       fun >>(i: Int): Byte do return rshift(i)
+
+       redef fun to_i is intern
+       redef fun to_f is intern
+       redef fun to_b do return self
+
+       redef fun distance(i) do return (self - i).to_i
+
+       redef fun <=>(other)
+       do
+               if self < other then
+                       return -1
+               else if other < self then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       redef fun is_between(c, d)
+       do
+               if self < c or d < self then
+                       return false
+               else
+                       return true
+               end
+       end
+
+       redef fun max(other)
+       do
+               if self < other then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       redef fun min(c)
+       do
+               if c < self then
+                       return c
+               else
+                       return self
+               end
+       end
+end
+
 # Native integer numbers.
 # Correspond to C int.
 universal Int
@@ -459,6 +570,7 @@ universal Int
 
        redef fun to_i do return self
        redef fun to_f is intern
+       redef fun to_b is intern
 
        redef fun distance(i)
        do
index ff46e46..b02ff9b 100644 (file)
@@ -2035,6 +2035,26 @@ redef class Bool
        end
 end
 
+redef class Byte
+       # C function to calculate the length of the `NativeString` to receive `self`
+       private fun byte_to_s_len: Int is extern "native_byte_length_str"
+
+       # C function to convert an nit Int to a NativeString (char*)
+       private fun native_byte_to_s(nstr: NativeString, strlen: Int) is extern "native_byte_to_s"
+
+       # Displayable byte in its hexadecimal form (0x..)
+       #
+       #     assert 1.to_b.to_s       == "0x01"
+       #     assert (-123).to_b.to_s  == "0x85"
+       redef fun to_s do
+               var nslen = byte_to_s_len
+               var ns = new NativeString(nslen + 1)
+               ns[nslen] = '\0'
+               native_byte_to_s(ns, nslen + 1)
+               return ns.to_s_with_length(nslen)
+       end
+end
+
 redef class Int
 
        # Wrapper of strerror C function
index 686d6be..050ab95 100644 (file)
@@ -20,3 +20,13 @@ long native_int_length_str(long recv){
 void native_int_to_s(long recv, char* str, long buflen){
        snprintf(str, buflen, "%ld", recv);
 }
+
+// Returns the length of `recv` as a `char*` (excluding the null character)
+long native_byte_length_str(unsigned char recv){
+       return snprintf(NULL, 0, "0x%02x", recv);
+}
+
+// Byte to NativeString method
+void native_byte_to_s(unsigned char recv, char* str, long buflen){
+       snprintf(str, buflen, "0x%02x", recv);
+}
index f33580f..c1c7818 100644 (file)
@@ -15,5 +15,7 @@
 
 long native_int_length_str(long recv);
 void native_int_to_s(long recv, char* str, long buflen);
+long native_byte_length_str(unsigned char recv);
+void native_byte_to_s(unsigned char recv, char* str, long buflen);
 
 #endif
index 8d3f7db..259c597 100644 (file)
@@ -1455,6 +1455,14 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # Generate a byte value
+       fun byte_instance(value: Byte): RuntimeVariable
+       do
+               var t = mmodule.byte_type
+               var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t)
+               return res
+       end
+
        # Generate a char value
        fun char_instance(value: Char): RuntimeVariable
        do
@@ -1838,6 +1846,8 @@ redef class MClassType
                        return "char"
                else if mclass.name == "Float" then
                        return "double"
+               else if mclass.name == "Byte" then
+                       return "unsigned char"
                else if mclass.name == "NativeString" then
                        return "char*"
                else if mclass.name == "NativeArray" then
@@ -1868,6 +1878,8 @@ redef class MClassType
                        return "c"
                else if mclass.name == "Float" then
                        return "d"
+               else if mclass.name == "Byte" then
+                       return "b"
                else if mclass.name == "NativeString" then
                        return "str"
                else if mclass.name == "NativeArray" then
@@ -2098,6 +2110,9 @@ redef class AMethPropdef
                        else if pname == "to_f" then
                                v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
                        else if pname == "ascii" then
                                v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
                                return true
@@ -2141,6 +2156,69 @@ redef class AMethPropdef
                                v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
                                return true
                        end
+               else if cname == "Byte" then
+                       if pname == "output" then
+                               v.add("printf(\"%x\\n\", {arguments.first});")
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "lshift" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "rshift" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                               return true
+                       end
                else if cname == "Bool" then
                        if pname == "output" then
                                v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");")
@@ -2209,6 +2287,9 @@ redef class AMethPropdef
                        else if pname == "to_i" then
                                v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               return true
                        end
                else if cname == "NativeString" then
                        if pname == "[]" then
index 7231bbb..31a4f20 100644 (file)
@@ -252,7 +252,7 @@ class SeparateCompiler
        do
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
-               for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
+               for classname in ["Int", "Bool", "Byte", "Char", "Float", "NativeString", "Pointer"] do
                        var classes = self.mainmodule.model.get_mclasses_by_name(classname)
                        if classes == null then continue
                        assert classes.length == 1 else print classes.join(", ")
index 41c9c26..3f2a21e 100644 (file)
@@ -483,6 +483,7 @@ redef class MClassType
                if mclass.name == "Char" then return "char"
                if mclass.name == "Int" then return "long"
                if mclass.name == "Float" then return "double"
+               if mclass.name == "Byte" then return "byte"
                return super
        end
 
@@ -494,6 +495,7 @@ redef class MClassType
                if mclass.name == "Char" then return "jchar"
                if mclass.name == "Int" then return "jlong"
                if mclass.name == "Float" then return "jdouble"
+               if mclass.name == "Byte" then return "jbyte"
                return super
        end
 
@@ -553,6 +555,7 @@ redef class MClassType
                if mclass.name == "Char" then return "C"
                if mclass.name == "Int" then return "J"
                if mclass.name == "Float" then return "D"
+               if mclass.name == "Byte" then return "B"
                return super
        end
 
@@ -565,6 +568,7 @@ redef class MClassType
                if mclass.name == "Char" then return "Char"
                if mclass.name == "Int" then return "Long"
                if mclass.name == "Float" then return "Double"
+               if mclass.name == "Byte" then return "Byte"
                return super
        end
 end
index 9708f3f..3626d33 100644 (file)
@@ -207,6 +207,15 @@ class NaiveInterpreter
                return instance
        end
 
+       # Return the byte instance associated with `val`.
+       fun byte_instance(val: Byte): Instance
+       do
+               var t = mainmodule.byte_type
+               var instance = new PrimitiveInstance[Byte](t, val)
+               init_instance_primitive(instance)
+               return instance
+       end
+
        # Return the char instance associated with `val`.
        fun char_instance(val: Char): Instance
        do
@@ -632,6 +641,10 @@ abstract class Instance
        # else aborts
        fun to_f: Float do abort
 
+       # Return the integer value if the instance is a byte.
+       # else aborts
+       fun to_b: Byte do abort
+
        # The real value encapsulated if the instance is primitive.
        # Else aborts.
        fun val: nullable Object do abort
@@ -677,6 +690,8 @@ class PrimitiveInstance[E]
        redef fun to_i do return val.as(Int)
 
        redef fun to_f do return val.as(Float)
+
+       redef fun to_b do return val.as(Byte)
 end
 
 # Information about local variables in a running method
@@ -867,6 +882,8 @@ redef class AMethPropdef
                                return v.char_instance(args[0].to_i.ascii)
                        else if pname == "to_f" then
                                return v.float_instance(args[0].to_i.to_f)
+                       else if pname == "to_b" then
+                               return v.byte_instance(args[0].to_i.to_b)
                        else if pname == "lshift" then
                                return v.int_instance(args[0].to_i.lshift(args[1].to_i))
                        else if pname == "rshift" then
@@ -894,6 +911,50 @@ redef class AMethPropdef
                        else if pname == "strerror_ext" then
                                return v.native_string_instance(recvval.strerror)
                        end
+               else if cname == "Byte" then
+                       var recvval = args[0].to_b
+                       if pname == "unary -" then
+                               return v.byte_instance(-args[0].to_b)
+                       else if pname == "unary +" then
+                               return args[0]
+                       else if pname == "+" then
+                               return v.byte_instance(args[0].to_b + args[1].to_b)
+                       else if pname == "-" then
+                               return v.byte_instance(args[0].to_b - args[1].to_b)
+                       else if pname == "*" then
+                               return v.byte_instance(args[0].to_b * args[1].to_b)
+                       else if pname == "%" then
+                               return v.byte_instance(args[0].to_b % args[1].to_b)
+                       else if pname == "/" then
+                               return v.byte_instance(args[0].to_b / args[1].to_b)
+                       else if pname == "<" then
+                               return v.bool_instance(args[0].to_b < args[1].to_b)
+                       else if pname == ">" then
+                               return v.bool_instance(args[0].to_b > args[1].to_b)
+                       else if pname == "<=" then
+                               return v.bool_instance(args[0].to_b <= args[1].to_b)
+                       else if pname == ">=" then
+                               return v.bool_instance(args[0].to_b >= args[1].to_b)
+                       else if pname == "<=>" then
+                               return v.int_instance(args[0].to_b <=> args[1].to_b)
+                       else if pname == "to_f" then
+                               return v.float_instance(args[0].to_b.to_f)
+                       else if pname == "to_i" then
+                               return v.int_instance(args[0].to_b.to_i)
+                       else if pname == "lshift" then
+                               return v.byte_instance(args[0].to_b.lshift(args[1].to_i))
+                       else if pname == "rshift" then
+                               return v.byte_instance(args[0].to_b.rshift(args[1].to_i))
+                       else if pname == "byte_to_s_len" then
+                               return v.int_instance(recvval.to_s.length)
+                       else if pname == "native_byte_to_s" then
+                               var s = recvval.to_s
+                               var srecv = args[1].val.as(Buffer)
+                               srecv.clear
+                               srecv.append(s)
+                               srecv.add('\0')
+                               return null
+                       end
                else if cname == "Char" then
                        var recv = args[0].val.as(Char)
                        if pname == "ascii" then
@@ -937,6 +998,8 @@ redef class AMethPropdef
                                return v.bool_instance(recv >= args[1].to_f)
                        else if pname == "to_i" then
                                return v.int_instance(recv.to_i)
+                       else if pname == "to_b" then
+                               return v.byte_instance(recv.to_b)
                        else if pname == "cos" then
                                return v.float_instance(args[0].to_f.cos)
                        else if pname == "sin" then
index 94f11f2..908cb60 100644 (file)
@@ -206,6 +206,9 @@ redef class MModule
        # The primitive type `Int`
        var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
 
+       # The primitive type `Byte`
+       var byte_type: MClassType = self.get_primitive_class("Byte").mclass_type is lazy
+
        # The primitive type `Char`
        var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
 
index a1f49a7..7cf6bbc 100644 (file)
@@ -92,6 +92,7 @@ redef class MClassType
                if name == "Char" then return "char"
                if name == "Float" then return "double"
                if name == "Int" then return "long"
+               if name == "Byte" then return "unsigned char"
                if name == "NativeString" then return "char*"
                if mclass.kind == extern_kind then
                        var ctype = mclass.ctype
@@ -107,6 +108,7 @@ redef class MClassType
                if name == "Char" then return "char"
                if name == "Float" then return "double"
                if name == "Int" then return "long"
+               if name == "Byte" then return "unsigned char"
                if name == "NativeString" then return "char*"
                if mclass.kind == extern_kind then return "void*"
                return "struct nitni_instance *"
index 8e5565c..bcd9f8e 100644 (file)
@@ -213,6 +213,7 @@ class RapidTypeAnalysis
                force_alive("Float")
                force_alive("Char")
                force_alive("Pointer")
+               force_alive("Byte")
 
                while not todo.is_empty do
                        var mmethoddef = todo.shift