X-Git-Url: http://nitlanguage.org diff --git a/src/compiling/compiling_base.nit b/src/compiling/compiling_base.nit index cddf846..24d4fcb 100644 --- a/src/compiling/compiling_base.nit +++ b/src/compiling/compiling_base.nit @@ -17,20 +17,110 @@ # Common things for NIT compilation and C generation package compiling_base -import syntax +import mmloader private import utils +import primitive_info +import program +import compiling_writer redef class ToolContext - readable writable var _global: Bool = false readable writable var _compdir: nullable String = null readable writable var _clibdir: nullable String = null readable writable var _bindir: nullable String = null readable writable var _output_file: nullable String = null readable writable var _boost: Bool = false readable writable var _no_cc: Bool = false + readable writable var _cc_link: Bool = false + readable writable var _cc_libs: Array[String] = new Array[String] + readable writable var _cc_lib_paths: Array[String] = new Array[String] + readable writable var _cc_include_paths: Array[String] = new Array[String] readable writable var _ext_prefix: String = "" end +# A program that is compiled to C +class CProgram + init(p: Program) + do + _program = p + _compdir = p.tc.compdir.as(not null) + _build_file = "{compdir}/{program.main_module.name}._build.sh" + end + + # The Nit program compiled to C + readable var _program: Program + + # C files (full path) required to compile + readable var _files: Array[String] = new Array[String] + + # Includes paths (gcc -I) required to find the headers (.h) of native modules + readable var _include_dirs: ArraySet[String] = new ArraySet[String] + + # The path of the building script + readable var _build_file: String + + # The directory where all files are generated + readable var _compdir: String + + # Return the basename of the public header file (.h) of a module + fun module_header_name(m: MMModule): String + do + if _module_include.has_key(m) then + return _module_include[m] + end + var filename = "{m.name}.{get_file_ending}.h" + _module_include[m] = filename + return filename + end + + # Cache for module_header_name + var _module_include: Map[MMModule, String] = new HashMap[MMModule, String] + + # When we are using global compilation, we generate _glob files instead + # of _sep files so that we do not corrupt separate compilation + fun get_file_ending: String do return if program.tc.global then "_glob" else "_sep" + + # Generate the shell script that build the program by calling gccx + fun generate_build_file + do + var f = new OFStream.open(_build_file) + var verbose = "" + var tc = program.tc + + if tc.verbose_level == 1 then + verbose = "-v" + else if tc.verbose_level >= 2 then + # We catch tc.verbose_level >= 2, since 3+ is not valid with gccx + verbose = "-vv" + end + + f.write("#!/bin/sh\n") + f.write("# This shell script is generated by NIT to compile the program {program.main_module.name}.\n") + f.write("CLIBDIR=\"{tc.clibdir.as(not null)}\"\n") + f.write("{tc.bindir.as(not null)}/gccx {verbose} -d {compdir} -I $CLIBDIR {include_dirs.join(" ")}") + if tc.output_file != null then + f.write(" -o {tc.output_file.as(not null)}") + else if tc.ext_prefix.is_empty then + f.write(" -o {program.main_module.name}") + else + f.write(" -o {program.main_module.name}_{tc.ext_prefix}") + end + if tc.boost then f.write(" -O") + if not tc.cc_link then f.write(" -x \"-c\"") + for l in tc.cc_libs do f.write(" -x \"-l {l}\"") + for lp in tc.cc_lib_paths do f.write(" -x \"-L {lp}\"") + for ip in tc.cc_include_paths do f.write(" -x \"-I {ip}\"") + f.write(" \"$@\" \\\n {files.join("\\\n ")}\n") + f.close + end + + # Invoke the build_file + fun run_c_compiler + do + program.tc.info("Building",1) + sys.system("sh {_build_file}") + end +end + # Class used to generate files. # Note that in fact it is not a visitor. # Note also that this class is unefficient and poorly designed thus requires love. @@ -38,20 +128,44 @@ class CompilerVisitor # Add a line in the current declaration block fun add_decl(s: String) do - if _indent_level >= 8 then - _ctx.decls.add("\t\t" + s) - else - _ctx.decls.add(" " * _indent_level + s) - end + add_line_to(_decl_writer, s) end # Add a line in the current instr block fun add_instr(s: String) do + add_line_to(_writer, s) + end + + + fun add_indent(w: Writer) + do if _indent_level >= 8 then - _ctx.instrs.add("\t\t" + s) + w.add("\t\t") else - _ctx.instrs.add(" " * _indent_level + s) + for i in [0.._indent_level[ do + w.add(" ") + end + end + end + + fun add_line_to(w: Writer, s: String) + do + add_indent(w) + w.add(s) + w.add("\n") + end + + # Add a assignment between a variable and an expression + fun add_assignment(v: String, s: String) + do + if v != s then + var w = _writer + add_indent(w) + w.add(v) + w.add(" = ") + w.add(s) + w.add(";\n") end end @@ -76,55 +190,48 @@ class CompilerVisitor if _indent_level < 0 then _indent_level = 0 end - # Return a big string containing all decl and instr - redef fun to_s - do - var out = new Array[String] - out.append(_ctx.decls) - out.append(_ctx.instrs) - out.add("") - return out.join("\n") - end + # The processed mmmodule + readable var _mmmodule: MMModule + + # Where header decl are stored (public stuff) + readable writable var _header_writer: Writer - # The processed module - readable var _module: MMModule + # Where current instr are stored (current function declaration) + readable writable var _writer: Writer - # Where instr and decl are stored - readable writable var _ctx: CContext = new CContext + # Where current decl are stored (current function instructions) + readable writable var _decl_writer: Writer + + # Where body instr are stored (C functions body) + readable writable var _top_writer: Writer + + # Where body decl are stored (private C function proptypes and typedefs) + readable writable var _top_decl_writer: Writer # The current indent lever readable writable var _indent_level: Int = 0 - # The ToolContext info - readable var _tc: ToolContext - - # Create a new CompilerVisitor based on a module - init(module: MMModule, tc: ToolContext) - do - _module = module - _tc = tc - end -end + # The program we are compiling + readable var _program: Program -# Where instr and decl are stored for a module -# Note that this class is as badly designed as CompilerVisitor -class CContext - readable var _decls: Array[String] = new Array[String] - readable var _instrs: Array[String] = new Array[String] + # The cprogram associed with program + readable var _cprogram: CProgram - fun append(c: CContext) - do - _instrs.append(c.decls) - _instrs.append(c.instrs) - end - - fun merge(c: CContext) + # Create a new CompilerVisitor based on a module + init(mod: MMModule, cp: CProgram) do - _decls.append(c.decls) - _instrs.append(c.instrs) + _mmmodule = mod + _cprogram = cp + _program = cp.program + + var w = new Writer + _header_writer = w + _decl_writer = w + w = new Writer + _writer = w + _top_writer = w + _top_decl_writer = w.sub end - - init do end end redef class MMGlobalProperty @@ -161,131 +268,6 @@ redef class MMGlobalClass end end -redef class MMLocalClass - # Cached primitive_info result - var _primitive_info_cache: nullable PrimitiveInfo = null - - # If primitive_info result cached? - var _primitive_info_b: Bool = false - - # Return the primitive information of the class. - # Return null if the class is not primitive - # FIXME: Only here since there is no universal type yet - fun primitive_info: nullable PrimitiveInfo - do - if _primitive_info_b == true then return _primitive_info_cache - - var ctypes = once primitive_ctypes - if ctypes.has_key(name) then - _primitive_info_cache = ctypes[name] - _primitive_info_b = true - return _primitive_info_cache - end - var i = ctypes.iterator - while i.is_ok do - var n = i.key - if module.has_global_class_named(n) then - var c = module.class_by_name(n) - if cshe < c then - _primitive_info_cache = i.item - _primitive_info_b = true - return _primitive_info_cache - end - end - i.next - end - _primitive_info_b = true - return null - end - - # Static information of primitive types - private fun primitive_ctypes: HashMap[Symbol, PrimitiveInfo] - do - var res = new HashMap[Symbol, PrimitiveInfo] - var pnames = ["Int", "Char", "Bool", "Float", "NativeString", "NativeArray", "Pointer"] - var tagged = [true, true, true, false, false, false, false] - var cnames = ["bigint", "char", "int", "float", "char *", "val_t *", "void *"] - for i in [0..pnames.length[ do - var n = pnames[i].to_symbol - var pi = new PrimitiveInfo(n, tagged[i], cnames[i]) - res[n] = pi - end - return res - end -end - -# Information about a primitive class -class PrimitiveInfo - # The name of the class - readable var _name: Symbol - - # Is the class tagged (aka not boxed) - readable var _tagged: Bool - - # The corresponding c type for the primitive value - readable var _cname: String - - private init(n: Symbol, t: Bool, c: String) - do - _name = n - _tagged = t - _cname = c - end -end - -redef class MMType - # The corresponding c type - fun cname: String - do - var pi = local_class.primitive_info - if pi == null then - return "val_t" - else - return pi.cname - end - end - - # The default c value for uninitialized types. - # Return "null" for non primitive types and something more specific for primitive types - fun default_cvalue: String - do - var pi = local_class.primitive_info - if pi != null and pi.tagged then - return "TAG_{local_class.name}(({pi.cname})0)" - else - return "NIT_NULL" - end - end - - # Box (or tag) a primitive value - # Is identity if not primitive - fun boxtype(s: String): String - do - var pi = local_class.primitive_info - if pi == null then - return s - else if pi.tagged then - return "TAG_{local_class.name}({s})" - else - return "BOX_{local_class.name}({s})" - end - end - - # Unbox (or untag) a primitive value - # Is identity if not primitive - fun unboxtype(s: String): String - do - var pi = local_class.primitive_info - if pi == null then - return s - else if pi.tagged then - return "UNTAG_{local_class.name}({s})" - else - return "UNBOX_{local_class.name}({s})" - end - end -end - redef class MMLocalProperty # Cacher result of cname var _cname_cache: nullable String @@ -295,7 +277,7 @@ redef class MMLocalProperty do var cname = _cname_cache if cname == null then - cname = cmangle(module.name, local_class.name, name) + cname = cmangle(mmmodule.name, local_class.name, name) _cname_cache = cname end return cname