src: Compiler, interpreter and parser updates for UTF-8
[nit.git] / src / compiler / abstract_compiler.nit
index 7e17187..3dcfab1 100644 (file)
@@ -104,15 +104,12 @@ redef class ToolContext
 end
 
 redef class ModelBuilder
-       # The compilation directory
-       var compile_dir: String
-
        # Simple indirection to `Toolchain::write_and_make`
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
                var toolchain = platform.toolchain(toolcontext, compiler)
-               compile_dir = toolchain.compile_dir
+               compiler.toolchain = toolchain
                toolchain.write_and_make
        end
 end
@@ -134,14 +131,21 @@ class Toolchain
        # Compiler of the target program
        var compiler: AbstractCompiler
 
-       # Directory where to generate all C files
-       fun compile_dir: String
+       # Directory where to generate all files
+       #
+       # The option `--compile_dir` change this directory.
+       fun root_compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
-               if compile_dir == null then compile_dir = ".nit_compile"
+               if compile_dir == null then compile_dir = "nit_compile"
                return compile_dir
        end
 
+       # Directory where to generate all C files
+       #
+       # By default it is `root_compile_dir` but some platform may require that it is a subdirectory.
+       fun compile_dir: String do return root_compile_dir
+
        # Write all C files and compile them
        fun write_and_make is abstract
 end
@@ -154,12 +158,16 @@ class MakefileToolchain
        do
                var compile_dir = compile_dir
 
+               # Remove the compilation directory unless explicitly set
+               var auto_remove = toolcontext.opt_compile_dir.value == null
+
                # Generate the .h and .c files
                # A single C file regroups many compiled rumtime functions
                # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
                var time0 = get_time
                self.toolcontext.info("*** WRITING C ***", 1)
 
+               root_compile_dir.mkdir
                compile_dir.mkdir
 
                var cfiles = new Array[String]
@@ -181,6 +189,10 @@ class MakefileToolchain
 
                compile_c_code(compile_dir)
 
+               if auto_remove then
+                       sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
+               end
+
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
@@ -208,7 +220,7 @@ class MakefileToolchain
 
                # Copy original .[ch] files to compile_dir
                for src in compiler.files_to_copy do
-                       var basename = src.basename("")
+                       var basename = src.basename
                        var dst = "{compile_dir}/{basename}"
                        src.file_copy_to dst
                end
@@ -316,7 +328,7 @@ class MakefileToolchain
                var outpath = real_outpath.escape_to_mk
                if outpath != real_outpath then
                        # If the name is crazy and need escaping, we will do an indirection
-                       # 1. generate the binary in the .nit_compile dir under an escaped name
+                       # 1. generate the binary in the nit_compile dir under an escaped name
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
@@ -432,7 +444,7 @@ endif
                # Compile each required extern body into a specific .o
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
-                       var ff = f.filename.basename("")
+                       var ff = f.filename.basename
                        makefile.write("{o}: {ff}\n")
                        makefile.write("\t{f.makefile_rule_content}\n\n")
                        dep_rules.add(f.makefile_rule_name)
@@ -510,6 +522,11 @@ abstract class AbstractCompiler
        # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
 
+       # The associated toolchain
+       #
+       # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
+       var toolchain: Toolchain is noinit
+
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
@@ -573,7 +590,7 @@ abstract class AbstractCompiler
        # Binds the generated C function names to Nit function names
        fun build_c_to_nit_bindings
        do
-               var compile_dir = modelbuilder.compile_dir
+               var compile_dir = toolchain.compile_dir
 
                var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
                stream.write("#include <string.h>\n")
@@ -620,6 +637,7 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <string.h>")
                self.header.add_decl("#include <sys/types.h>\n")
                self.header.add_decl("#include <unistd.h>\n")
+               self.header.add_decl("#include <stdint.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
@@ -824,7 +842,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add("signal(SIGINT, sig_handler);")
                v.add("signal(SIGTERM, sig_handler);")
                v.add("signal(SIGSEGV, sig_handler);")
-               v.add("signal(SIGPIPE, sig_handler);")
+               v.add("signal(SIGPIPE, SIG_IGN);")
 
                v.add("glob_argc = argc; glob_argv = argv;")
                v.add("initialize_gc_option();")
@@ -1464,12 +1482,24 @@ 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
                var t = mmodule.char_type
-               var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
-               return res
+
+               if value.ascii < 128 then
+                       return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+               else
+                       return new RuntimeVariable("{value.ascii}", t, t)
+               end
        end
 
        # Generate a float value
@@ -1511,7 +1541,7 @@ abstract class AbstractCompilerVisitor
                var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
-               var length = self.int_instance(string.length)
+               var length = self.int_instance(string.bytelen)
                self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};")
                self.add("{name} = {res};")
                self.add("\}")
@@ -1572,12 +1602,12 @@ abstract class AbstractCompilerVisitor
                file = file.strip_extension(".nit")
                var tryfile = file + ".nit.h"
                if tryfile.file_exists then
-                       self.declare_once("#include \"{tryfile.basename("")}\"")
+                       self.declare_once("#include \"{tryfile.basename}\"")
                        self.compiler.files_to_copy.add(tryfile)
                end
                tryfile = file + "_nit.h"
                if tryfile.file_exists then
-                       self.declare_once("#include \"{tryfile.basename("")}\"")
+                       self.declare_once("#include \"{tryfile.basename}\"")
                        self.compiler.files_to_copy.add(tryfile)
                end
 
@@ -1588,7 +1618,7 @@ abstract class AbstractCompilerVisitor
                        tryfile = file + "_nit.c"
                        if not tryfile.file_exists then return
                end
-               var f = new ExternCFile(tryfile.basename(""), "")
+               var f = new ExternCFile(tryfile.basename, "")
                self.compiler.extern_bodies.add(f)
                self.compiler.files_to_copy.add(tryfile)
        end
@@ -1646,8 +1676,9 @@ abstract class AbstractCompilerVisitor
                if nexpr == null then return
                if nexpr.mtype == null and not nexpr.is_typed then
                        # Untyped expression.
-                       # Might mean dead code
-                       # So just return
+                       # Might mean dead code or invalid code
+                       # so aborts
+                       add_abort("FATAL: bad statement executed.")
                        return
                end
 
@@ -1669,16 +1700,27 @@ abstract class AbstractCompilerVisitor
        # `mtype` is the expected return type, pass null if no specific type is expected.
        fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
        do
-               if nexpr.mtype == null then
+               var old = self.current_node
+               self.current_node = nexpr
+
+               var res = null
+               if nexpr.mtype != null then
+                       res = nexpr.expr(self)
+               end
+
+               if res == null then
                        # Untyped expression.
-                       # Might mean dead code
-                       # so return a placebo result
+                       # Might mean dead code or invalid code.
+                       # so aborts
+                       add_abort("FATAL: bad expression executed.")
+                       # and return a placebo result to please the C compiler
                        if mtype == null then mtype = compiler.mainmodule.object_type
-                       return new_var(mtype)
+                       res = new_var(mtype)
+
+                       self.current_node = old
+                       return res
                end
-               var old = self.current_node
-               self.current_node = nexpr
-               var res = nexpr.expr(self).as(not null)
+
                if mtype != null then
                        mtype = self.anchor(mtype)
                        res = self.autobox(res, mtype)
@@ -1844,9 +1886,11 @@ redef class MClassType
                else if mclass.name == "Bool" then
                        return "short int"
                else if mclass.name == "Char" then
-                       return "char"
+                       return "uint32_t"
                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
@@ -1877,6 +1921,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
@@ -2107,15 +2153,15 @@ 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)))
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
                                return true
                        end
                else if cname == "Char" then
-                       if pname == "output" then
-                               v.add("printf(\"%c\", {arguments.first});")
-                               return true
-                       else if pname == "object_id" then
+                       if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                                return true
                        else if pname == "successor" then
@@ -2147,7 +2193,70 @@ redef class AMethPropdef
                                v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
                                return true
                        else if pname == "ascii" then
-                               v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+                               v.ret(v.new_expr("(long){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
@@ -2218,13 +2327,16 @@ 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
-                               v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
+                               v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null)))
                                return true
                        else if pname == "[]=" then
-                               v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
+                               v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
                                return true
                        else if pname == "copy_to" then
                                v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
@@ -2355,7 +2467,7 @@ redef class AAttrPropdef
                        var res
                        if is_lazy then
                                var set
-                               var ret = self.mpropdef.static_mtype
+                               var ret = self.mtype
                                var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
@@ -2383,7 +2495,7 @@ redef class AAttrPropdef
                        assert arguments.length == 2
                        v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
                        if is_lazy then
-                               var ret = self.mpropdef.static_mtype
+                               var ret = self.mtype
                                var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                if not useiset then
                                        v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
@@ -2405,11 +2517,11 @@ redef class AAttrPropdef
                var oldnode = v.current_node
                v.current_node = self
                var old_frame = v.frame
-               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
                v.frame = frame
 
                var value
-               var mtype = self.mpropdef.static_mtype
+               var mtype = self.mtype
                assert mtype != null
 
                var nexpr = self.n_expr
@@ -2794,6 +2906,10 @@ redef class AIntExpr
        redef fun expr(v) do return v.int_instance(self.value.as(not null))
 end
 
+redef class AByteExpr
+       redef fun expr(v) do return v.byte_instance(self.value.as(not null))
+end
+
 redef class AFloatExpr
        redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
 end
@@ -2929,7 +3045,9 @@ redef class AIsaExpr
        redef fun expr(v)
        do
                var i = v.expr(self.n_expr, null)
-               return v.type_test(i, self.cast_type.as(not null), "isa")
+               var cast_type = self.cast_type
+               if cast_type == null then return null # no-no on broken node
+               return v.type_test(i, cast_type, "isa")
        end
 end