import io:* import mem:* import map:* import hash_map:* import stack:* import string:* import util:* import tree:* import symbol:* import ast_nodes:* // for error with syntax tree import pass_common:* import poset:* obj c_generator (Object) { var id_counter: int var ast_to_syntax: map<*ast_node, *tree> var ast_name_map: hash_map<*ast_node, string> var used_names: hash_set var function_type_map: map var function_typedef_string: string var closure_struct_definitions: string var c_keyword_avoid: set var replacement_map: map var longest_replacement: int var linker_string: string fun construct(): *c_generator { id_counter = 0 ast_to_syntax.construct() ast_name_map.construct() used_names.construct() function_type_map.construct() function_typedef_string.construct() closure_struct_definitions.construct() linker_string.construct() c_keyword_avoid.construct() c_keyword_avoid.add(string("extern")) replacement_map.construct() replacement_map[string("+")] = string("plus") replacement_map[string("-")] = string("minus") replacement_map[string("*")] = string("star") replacement_map[string("/")] = string("div") replacement_map[string("%")] = string("mod") replacement_map[string("^")] = string("carat") replacement_map[string("&")] = string("amprsd") replacement_map[string("|")] = string("pipe") replacement_map[string("~")] = string("tilde") replacement_map[string("!")] = string("exlmtnpt") replacement_map[string(",")] = string("comma") replacement_map[string("=")] = string("eq") replacement_map[string("++")] = string("dbplus") replacement_map[string("--")] = string("dbminus") replacement_map[string("<<")] = string("dbleft") replacement_map[string(">>")] = string("dbright") replacement_map[string("::")] = string("scopeop") replacement_map[string(":")] = string("colon") replacement_map[string("==")] = string("dbq") replacement_map[string("!=")] = string("notequals") replacement_map[string("&&")] = string("doubleamprsnd") replacement_map[string("||")] = string("doublepipe") replacement_map[string("+=")] = string("plusequals") replacement_map[string("-=")] = string("minusequals") replacement_map[string("/=")] = string("divequals") replacement_map[string("%=")] = string("modequals") replacement_map[string("^=")] = string("caratequals") replacement_map[string("&=")] = string("amprsdequals") replacement_map[string("|=")] = string("pipeequals") replacement_map[string("*=")] = string("starequals") replacement_map[string("<<=")] = string("doublerightequals") replacement_map[string("<")] = string("lt") replacement_map[string(">")] = string("gt") replacement_map[string(">>=")] = string("doubleleftequals") replacement_map[string("(")] = string("openparen") replacement_map[string(")")] = string("closeparen") replacement_map[string("[")] = string("obk") replacement_map[string("]")] = string("cbk") replacement_map[string(" ")] = string("_") replacement_map[string(".")] = string("dot") replacement_map[string("->")] = string("arrow") longest_replacement = 0 replacement_map.for_each(fun(key: string, value: string) { if (key.length() > longest_replacement) longest_replacement = key.length() }) return this } fun copy_construct(old: *c_generator) { id_counter = old->id_counter ast_to_syntax.copy_construct(&old->ast_to_syntax) ast_name_map.copy_construct(&old->ast_name_map) used_names.copy_construct(&old->used_names) function_type_map.copy_construct(&old->function_type_map) function_typedef_string.copy_construct(&old->function_typedef_string) closure_struct_definitions.copy_construct(&old->closure_struct_definitions) c_keyword_avoid.copy_construct(&old->c_keyword_avoid) replacement_map.copy_construct(&old->replacement_map) longest_replacement = old->longest_replacement linker_string.copy_construct(&old->linker_string) } fun operator=(other: ref c_generator) { destruct() copy_construct(&other) } fun destruct() { ast_to_syntax.destruct() ast_name_map.destruct() used_names.destruct() function_type_map.destruct() function_typedef_string.destruct() closure_struct_definitions.destruct() c_keyword_avoid.destruct() replacement_map.destruct() linker_string.destruct() } fun get_id(): string return to_string(id_counter++); fun generate_function_prototype_and_header(child: *ast_node):pair { var backing = child->function var parameter_types = string() var parameters = string() var decorated_name = string() if (backing.is_extern) decorated_name = backing.name else decorated_name = generate_function(child) backing.parameters.for_each(fun(parameter: *ast_node) { if (parameter_types != "") { parameter_types += ", "; parameters += ", ";} parameter_types += type_to_c(parameter->identifier.type) parameters += type_to_c(parameter->identifier.type) + " " + get_name(parameter) }) if (backing.is_variadic) { parameter_types += ", ..." parameters += ", ..." } return make_pair(type_to_c(backing.type->return_type) + " " + decorated_name + "(" + parameter_types + ");\n", type_to_c(backing.type->return_type) + " " + decorated_name + "(" + parameters + ")") } fun generate_c(name_ast_map: map,*ast_node>>, ast_to_syntax_in: map<*ast_node, *tree> ): pair { ast_to_syntax = ast_to_syntax_in var prequal: string = "#include \n" var plain_typedefs: string = "\n/**Plain Typedefs**/\n" var top_level_c_passthrough: string = "" var variable_extern_declarations: string = "" var structs: string = "\n/**Type Structs**/\n" function_typedef_string = "\n/**Typedefs**/\n" closure_struct_definitions = "\n/**Closure Struct Definitions**/\n" var function_prototypes: string = "\n/**Function Prototypes**/\n" var function_definitions: string = "\n/**Function Definitions**/\n" var variable_declarations: string = "\n/**Variable Declarations**/\n" // moved out from below so that it can be used for methods as well as regular functions (and eventually lambdas...) var generate_function_definition = fun(child: *ast_node) { var backing = child->function var prototype_and_header = generate_function_prototype_and_header(child) function_prototypes += prototype_and_header.first if (!backing.is_extern) function_definitions += prototype_and_header.second if (backing.body_statement) { function_definitions += " {\n" + generate(backing.body_statement) function_definitions += ";\n}\n" } } var type_poset = poset<*ast_node>() // iterate through asts name_ast_map.for_each(fun(name: string, tree_pair: pair<*tree,*ast_node>) { // iterate through children for each ast // do lambdas seperatly, so we can reconstitute the enclosing object if it has one tree_pair.second->translation_unit.lambdas.for_each(fun(child: *ast_node) { generate_function_definition(child) }) tree_pair.second->translation_unit.children.for_each(fun(child: *ast_node) { match (*child) { ast_node::if_comp(backing) error("if_comp not currently supported") ast_node::simple_passthrough(backing) error("simple_passthrough removed") ast_node::declaration_statement(backing) variable_declarations += generate_declaration_statement(child) + ";\n" // false - don't do defer // shouldn't need to do anything with return, as the intrinsic should be something like link ast_node::compiler_intrinsic(backing) generate_compiler_intrinsic(child) ast_node::function(backing) { // check for and add to parameters if a closure generate_function_definition(child) } ast_node::template(backing) { backing.instantiated.for_each(fun(node: *ast_node) { match (*node) { ast_node::function(backing) generate_function_definition(node) ast_node::type_def(backing) { type_poset.add_vertex(node) backing.variables.for_each(fun(i: *ast_node) { var var_type = get_ast_type(i->declaration_statement.identifier) if (!var_type->indirection && var_type->type_def) type_poset.add_relationship(node, var_type->type_def) }) } } }) } ast_node::type_def(backing) { type_poset.add_vertex(child) backing.variables.for_each(fun(i: *ast_node) { var var_type = get_ast_type(i->declaration_statement.identifier) if (!var_type->indirection && var_type->type_def) type_poset.add_relationship(child, var_type->type_def) }) } ast_node::adt_def(backing) error("ADT remaining!") } }) }) type_poset.get_sorted().for_each(fun(vert: *ast_node) { var base_name = get_name(vert) plain_typedefs += string("typedef ") if (vert->type_def.is_union) { plain_typedefs += "union " structs += "union " } else { plain_typedefs += "struct " structs += "struct " } plain_typedefs += base_name + "_dummy " + base_name + ";\n" structs += base_name + "_dummy {\n" vert->type_def.variables.for_each(fun(variable_declaration: *ast_node) structs += generate_declaration_statement(variable_declaration) + ";\n";) // generate the methods (note some of these may be templates) vert->type_def.methods.for_each(fun(method: *ast_node) { if (is_template(method)) method->template.instantiated.for_each(fun(m: *ast_node) generate_function_definition(m);) else generate_function_definition(method); }) structs += "};\n" }) return make_pair(prequal+plain_typedefs+function_typedef_string+top_level_c_passthrough+variable_extern_declarations+structs+closure_struct_definitions+function_prototypes+variable_declarations+function_definitions + "\n", linker_string) } fun generate_declaration_statement(node: *ast_node): string { var identifier = node->declaration_statement.identifier var ident_type = identifier->identifier.type var to_ret = type_to_c(identifier->identifier.type) + " " + get_name(identifier) if (identifier->identifier.is_extern) to_ret = "extern " + to_ret if (node->declaration_statement.expression) { // in case of recursive closures, make sure variable is declared before assignment to_ret += ";\n" to_ret += get_name(identifier) + " = " + generate(node->declaration_statement.expression) } if (node->declaration_statement.init_method_call) { error("init_method_call remaining") } return to_ret } fun generate_assignment_statement(node: *ast_node): string { return generate(node->assignment_statement.to) + " = " + generate(node->assignment_statement.from) } fun generate_if_statement(node: *ast_node): string { var if_str = "if (" + generate(node->if_statement.condition) + ") {\n" + generate(node->if_statement.then_part) + "}" if (node->if_statement.else_part) if_str += " else {\n" + generate(node->if_statement.else_part) + "}" return if_str + "\n" } fun generate_while_loop(node: *ast_node): string { return "while (" + generate(node->while_loop.condition) + ")\n" + generate(node->while_loop.statement) } fun generate_for_loop(node: *ast_node): string { var init = string(";") if (node->for_loop.init) init = generate(node->for_loop.init) var cond = string(";") if (node->for_loop.condition) cond = generate(node->for_loop.condition) // gotta take off last semicolon var update = string() if (node->for_loop.update) { update = generate(node->for_loop.update) if (update.length() < 2) error("update less than 2! Likely legal, but need easy compiler mod here") update = update.slice(0,-2) } return "for (" + init + cond + "; " + update + ")\n" + generate(node->for_loop.body) } fun generate_identifier(node: *ast_node): string { if (get_ast_type(node)->is_ref) error("still existin ref in identifier") return get_name(node) } fun generate_return_statement(node: *ast_node): string { if (node->return_statement.return_value) return "return " + generate(node->return_statement.return_value) return string("return") } fun generate_branching_statement(node: *ast_node): string { match(node->branching_statement.b_type) { branching_type::break_stmt() return string("break") branching_type::continue_stmt() return string("continue") } } fun generate_cast(node: *ast_node): string { return "((" + type_to_c(node->cast.to_type) + ")(" + generate(node->cast.value) + "))" } fun generate_value(node: *ast_node): string { var value = node->value.string_value if (value[0] != '"') return value var to_ret = string("\"") var triple_quoted = value.slice(0,3) == "\"\"\"" if (triple_quoted) value = value.slice(3,-4) else value = value.slice(1,-2) value.for_each(fun(c: char) { if (c == '\n') to_ret += "\\n" else if (c == '"' && triple_quoted) to_ret += "\\\"" else to_ret += c }) return to_ret + "\"" } fun generate_code_block(node: *ast_node): string { var to_ret = string("{\n") node->code_block.children.for_each(fun(child: *ast_node) to_ret += generate(child) + ";\n";) return to_ret + "}" } // this generates the function as a value, not the actual function fun generate_function(node: *ast_node): string { return get_name(node) } fun generate_function_call(node: *ast_node): string { var func_name = generate(node->function_call.func) var call_string = string() var func_return_type = get_ast_type(node) var parameters = node->function_call.parameters if ( parameters.size == 2 && (func_name == "+" || func_name == "-" || func_name == "*" || func_name == "/" || func_name == "<" || func_name == ">" || func_name == "<=" || func_name == ">=" || func_name == "==" || func_name == "!=" || func_name == "%" || func_name == "^" || func_name == "|" || func_name == "&" || func_name == ">>" || func_name == "<<" )) return "(" + generate(parameters[0]) + func_name + generate(parameters[1]) + ")" if ( parameters.size == 2 && (func_name == "||" || func_name == "&&")) error("Remaining || or &&") // don't propegate enclosing function down right of access // XXX what about enclosing object? should it be the thing on the left? if (func_name == "." || func_name == "->") return "(" + generate(parameters[0]) + func_name + generate(parameters[1]) + ")" if (func_name == "[]") return "(" + generate(parameters[0]) + "[" + generate(parameters[1]) + "])" // the post ones need to be post-ed specifically, and take the p off if (func_name == "++p" || func_name == "--p") return "(" + generate(parameters[0]) + ")" + func_name.slice(0,-2) // So we don't end up copy_constructing etc, we just handle the unary operators right here if (func_name == "*" || func_name == "&") return "(" + func_name + generate(parameters[0]) + ")" var func_type = get_ast_type(node->function_call.func) // regular parameter generation for (var i = 0; i < parameters.size; i++;) { var param = parameters[i] var in_function_param_type = null() // grab type from param itself if we're out of param types (because variadic function) if (i < func_type->parameter_types.size) in_function_param_type = func_type->parameter_types[i] else in_function_param_type = get_ast_type(param)->clone_without_ref() if (call_string != "") call_string += ", " call_string += generate(param) } call_string = func_name + "(" + call_string + ")" return call_string } fun generate_compiler_intrinsic(node: *ast_node): string { if (node->compiler_intrinsic.intrinsic == "sizeof") { if (node->compiler_intrinsic.parameters.size || node->compiler_intrinsic.type_parameters.size != 1) error("wrong parameters to sizeof compiler intrinsic") return "sizeof(" + type_to_c(node->compiler_intrinsic.type_parameters[0]) + ")" } else if (node->compiler_intrinsic.intrinsic == "link") { node->compiler_intrinsic.parameters.for_each(fun(value: *ast_node) { linker_string += string("-l") + value->value.string_value.slice(1,-2) + " " }) return string() } error(node->compiler_intrinsic.intrinsic + ": unknown intrinsic") return string("ERROR") } fun generate(node: *ast_node): string { if (!node) return string("/*NULL*/") match (*node) { ast_node::declaration_statement(backing) return generate_declaration_statement(node) ast_node::assignment_statement(backing) return generate_assignment_statement(node) ast_node::if_statement(backing) return generate_if_statement(node) ast_node::while_loop(backing) return generate_while_loop(node) ast_node::for_loop(backing) return generate_for_loop(node) ast_node::function(backing) return generate_function(node) ast_node::function_call(backing) return generate_function_call(node) ast_node::compiler_intrinsic(backing) return generate_compiler_intrinsic(node) ast_node::code_block(backing) return generate_code_block(node) ast_node::return_statement(backing) return generate_return_statement(node) ast_node::branching_statement(backing) return generate_branching_statement(node) ast_node::defer_statement(backing) error("unremoved defer") ast_node::match_statement(backing) error("unremoved match") ast_node::cast(backing) return generate_cast(node) ast_node::value(backing) return generate_value(node) ast_node::identifier(backing) return generate_identifier(node) } error(string("COULD NOT GENERATE ") + get_ast_name(node)) return string("/* COULD NOT GENERATE */") } fun type_to_c(type: *type): string { var indirection = string() if (type->is_ref) error("still ref in type_to_c") //indirection += "/*ref*/ *" for (var i = 0; i < type->indirection; i++;) indirection += "*" match (type->base) { base_type::none() return string("none") + indirection base_type::template() return string("template") + indirection base_type::template_type() return string("template_type") + indirection base_type::void_return() return string("void") + indirection base_type::boolean() return string("bool") + indirection base_type::character() return string("char") + indirection base_type::ucharacter() return string("unsigned char") + indirection base_type::short_int() return string("short") + indirection base_type::ushort_int() return string("unsigned short") + indirection base_type::integer() return string("int") + indirection base_type::uinteger() return string("unsigned int") + indirection base_type::long_int() return string("long") + indirection base_type::ulong_int() return string("unsigned long") + indirection base_type::floating() return string("float") + indirection base_type::double_precision() return string("double") + indirection base_type::object() return get_name(type->type_def) + indirection base_type::function() { type = type->clone_with_indirection(0,false) if (!function_type_map.contains_key(*type)) { var temp_name = string("function") + get_id() var temp = string() type->parameter_types.for_each(fun(parameter_type: *type) { temp += string(", ") + type_to_c(parameter_type) + " " temp_name += "_" + cify_name(type_to_c(parameter_type)) }) if (type->is_raw) function_typedef_string += string("typedef ") + type_to_c(type->return_type) + " (*" + temp_name + ")(" + temp.slice(1,-1) + ");\n" else error(type->to_string() + " is not raw!") // again, the indirection function_type_map[*type] = temp_name } return function_type_map[*type] + indirection } } return string("impossible type") + indirection } fun type_decoration(type: *type): string { return cify_name(type_to_c(type)) } fun get_name(node: *ast_node): string { var maybe_it = ast_name_map.get_ptr_or_null(node); if (maybe_it) return *maybe_it var result = string("impossible name") var make_unique = true match (*node) { ast_node::type_def(backing) { var upper = backing.scope[string("~enclosing_scope")][0] result = cify_name(backing.name) if (is_template(upper)) upper->template.instantiated_map.reverse_get(node).for_each(fun(t: ref type) result += string("_") + type_decoration(&t);) } ast_node::function(backing) { // be careful, operators like . come through this if (backing.name == "main" || backing.is_extern || !backing.body_statement) { result = backing.name make_unique = false } else { result = "fun_" var upper = backing.scope.get_with_default(string("~enclosing_scope"), vector(null()))[0] if (upper && is_type_def(upper)) result += get_name(upper) + "_" result += cify_name(node->function.name) node->function.parameters.for_each(fun(param: *ast_node) result += string("_") + type_decoration(param->identifier.type);) } } ast_node::identifier(backing) { if (backing.name == "this" || backing.is_extern) make_unique = false result = backing.name } } if (result == "impossible name") error("HUGE PROBLEMS") // TODO keyword avoid seems not to work if (make_unique && (used_names.contains(result) || c_keyword_avoid.contains(result))) result += get_id() ast_name_map.set(node, result) used_names.add(result) return result } fun cify_name(name: string): string { var to_ret = string() for (var i = 0; i < name.length(); i++;) { var replaced = false for (var j = longest_replacement; j > 0; j--;) { if (i + j <= name.length() && replacement_map.contains_key(name.slice(i,i+j))) { to_ret += replacement_map[name.slice(i,i+j)] replaced = true i += j-1; break } } if (!replaced) to_ret += name[i] } return to_ret } }