1172 lines
35 KiB
C++
1172 lines
35 KiB
C++
|
|
#define gen(...) pctx->gen.addf(__VA_ARGS__)
|
|
#define genln(...) \
|
|
do { \
|
|
gen("\n"); \
|
|
gen_indent(); \
|
|
gen(__VA_ARGS__); \
|
|
} while (0)
|
|
global S32 global_indent;
|
|
global S32 is_inside_struct;
|
|
|
|
CORE_Static void gen_ast(Ast *ast);
|
|
CORE_Static bool gen_expr(Ast_Expr *ast);
|
|
|
|
CORE_Static void
|
|
gen_indent() {
|
|
for (S32 i = 0; i < global_indent; i++) gen(" ");
|
|
}
|
|
|
|
global Intern_String last_filename;
|
|
global int last_line;
|
|
CORE_Static void
|
|
gen_line(Ast *node) {
|
|
if (pctx->emit_line_directives) {
|
|
last_line = node->pos->line + 1;
|
|
genln("#line %d", last_line);
|
|
if (node->pos->file != last_filename) {
|
|
last_filename = node->pos->file;
|
|
gen(" \"%Q\"", last_filename);
|
|
}
|
|
}
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_last_line() {
|
|
if (pctx->emit_line_directives) {
|
|
genln("#line %d", last_line);
|
|
}
|
|
}
|
|
|
|
CORE_Static String
|
|
string_scope_name(Ast_Scope *scope) {
|
|
String string = {};
|
|
if (scope->parent_scope) string = string_scope_name(scope->parent_scope);
|
|
assert_message(scope->scope_id != 0, "Scope id is equal to 0 which is invalid, scope didn't initialize id");
|
|
string = pctx->fmt("%QS%u_", string, scope->scope_id);
|
|
return string;
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_scope_name(Ast_Scope *scope) {
|
|
gen("%Q", string_scope_name(scope));
|
|
}
|
|
|
|
CORE_Static Intern_String
|
|
get_unique_name(Ast *ast) {
|
|
String result = string_scope_name(ast->parent_scope);
|
|
assert(result.len);
|
|
return pctx->internf("%Q%d", result, ast->pos->line);
|
|
}
|
|
|
|
global String prefixed_string_type;
|
|
CORE_Static const char *
|
|
get_ctype_name_for_type(Ast_Type *type) {
|
|
switch (type->kind) {
|
|
case TYPE_VOID: return "void";
|
|
case TYPE_BOOL: return "bool";
|
|
case TYPE_STRING: return (char *)prefixed_string_type.str;
|
|
case TYPE_CHAR: return "char";
|
|
case TYPE_F32: return "float";
|
|
case TYPE_F64: return "double";
|
|
case TYPE_INT: return "int";
|
|
case TYPE_S8: return "int8_t";
|
|
case TYPE_S16: return "int16_t";
|
|
case TYPE_S32: return "int32_t";
|
|
case TYPE_S64: return "int64_t";
|
|
case TYPE_U8: return "uint8_t";
|
|
case TYPE_U16: return "uint16_t";
|
|
case TYPE_U32: return "uint32_t";
|
|
case TYPE_U64: return "uint64_t";
|
|
case TYPE_TUPLE: return "Tuple";
|
|
case TYPE_TYPE: return "int64_t";
|
|
|
|
case TYPE_INCOMPLETE: {
|
|
// This happens in case where we have a procedure in an away module.
|
|
// Meaning that it's not resolved fully on purpose (treeshaking).
|
|
// And that procedure takes a pointer from a struct that is never
|
|
// used elsewhere. In that case this seems fine.
|
|
return "void";
|
|
}
|
|
invalid_default_case;
|
|
}
|
|
return "<unknown_type>";
|
|
}
|
|
|
|
CORE_Static String
|
|
string_simple_decl_prefix(Ast_Type *ast) {
|
|
switch (ast->kind) {
|
|
case TYPE_POINTER: {
|
|
String string = string_simple_decl_prefix(ast->base);
|
|
string = pctx->fmt("%Q*", string);
|
|
return string;
|
|
} break;
|
|
case TYPE_LAMBDA: return {}; break;
|
|
case TYPE_ENUM:
|
|
case TYPE_ARRAY: {
|
|
return string_simple_decl_prefix(ast->base);
|
|
} break;
|
|
case TYPE_SLICE: {
|
|
String string = string_simple_decl_prefix(ast->base);
|
|
string = pctx->fmt("Slice%llu ", ast->type_id);
|
|
return string;
|
|
} break;
|
|
case TYPE_TUPLE: {
|
|
String string = pctx->fmt("Tuple%llu ", ast->type_id);
|
|
return string;
|
|
} break;
|
|
case TYPE_UNION:
|
|
case TYPE_STRUCT: {
|
|
auto constant = (Ast_Decl *)ast->ast;
|
|
auto name = constant->unique_name;
|
|
String string = pctx->fmt("%Q ", name);
|
|
String sc = {};
|
|
return pctx->fmt("%Q%Q", sc, string);
|
|
} break;
|
|
default: {
|
|
String string = pctx->fmt("%s ", get_ctype_name_for_type(ast));
|
|
return string;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
CORE_Static String
|
|
string_simple_decl_postfix(Ast_Type *ast) {
|
|
switch (ast->kind) {
|
|
case TYPE_POINTER:
|
|
return string_simple_decl_postfix(ast->base);
|
|
break;
|
|
case TYPE_ARRAY: {
|
|
String result = string_simple_decl_postfix(ast->arr.base);
|
|
String string = pctx->fmt("[%d]%Q", ast->arr.size, result);
|
|
return string;
|
|
} break;
|
|
case TYPE_LAMBDA: break;
|
|
case TYPE_SLICE:
|
|
case TYPE_ENUM:
|
|
case TYPE_STRUCT: break;
|
|
default:
|
|
break;
|
|
// default: return string_from_cstring((char *)name(ast));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
CORE_Static String
|
|
string_simple_decl(Ast_Type *ast, Intern_String name = {}) {
|
|
if (ast->kind == TYPE_LAMBDA) {
|
|
String prefix = string_simple_decl_prefix(ast->func.ret);
|
|
String string = pctx->fmt("%Q(*%Q)(", prefix, name);
|
|
For(ast->func.args) {
|
|
String prefix_arg = string_simple_decl_prefix(it);
|
|
string = pctx->fmt("%Q%Q", string, prefix_arg);
|
|
if (&it != ast->func.args.end() - 1)
|
|
string = pctx->fmt("%Q, ", string);
|
|
}
|
|
string = pctx->fmt("%Q)", string);
|
|
return string;
|
|
}
|
|
else {
|
|
String string = string_simple_decl_prefix(ast);
|
|
if (name.len) {
|
|
string = pctx->fmt("%Q%Q", string, name);
|
|
}
|
|
String postfix = string_simple_decl_postfix(ast);
|
|
string = pctx->fmt("%Q%Q", string, postfix);
|
|
return string;
|
|
}
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_simple_decl(Ast_Type *ast, Intern_String name = {}) {
|
|
String string = string_simple_decl(ast, name);
|
|
gen("%Q", string);
|
|
}
|
|
|
|
CORE_Static String
|
|
get_type_postfix(Ast_Type *type) {
|
|
switch (type->kind) {
|
|
case TYPE_F32: return "f"_s; break;
|
|
case TYPE_U64: return "ULL"_s; break;
|
|
case TYPE_S64: return "LL"_s; break;
|
|
case TYPE_F64:
|
|
case TYPE_S8:
|
|
case TYPE_S16:
|
|
case TYPE_S32:
|
|
case TYPE_U8:
|
|
case TYPE_U16:
|
|
case TYPE_INT:
|
|
case TYPE_U32:
|
|
case TYPE_CHAR:
|
|
return ""_s;
|
|
break;
|
|
invalid_default_case;
|
|
}
|
|
assert(!"Unhandled case or error");
|
|
return ""_s;
|
|
}
|
|
|
|
CORE_Static B32
|
|
gen_value(Token *pos, Value a) {
|
|
if (is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage");
|
|
|
|
B32 result = true;
|
|
Ast_Type *type = a.type;
|
|
if (is_enum(a.type)) {
|
|
type = a.type->base;
|
|
}
|
|
|
|
switch (type->kind) {
|
|
CASE_INT : {
|
|
Scoped_Arena scratch(pctx->scratch);
|
|
String postfix = get_type_postfix(type);
|
|
const char *string = bigint_to_error_string(scratch.arena, &a.big_int_val, 10);
|
|
gen("%s%Q", string, postfix);
|
|
} break;
|
|
case TYPE_POINTER: {
|
|
if (a.type == pctx->type_pointer_to_char) {
|
|
gen("\"%Q\"", a.intern_val);
|
|
}
|
|
else {
|
|
U64 pointer_value = bigint_as_unsigned(&a.big_int_val);
|
|
gen("0x%llx", pointer_value);
|
|
}
|
|
} break;
|
|
CASE_BOOL : {
|
|
a.bool_val ? gen("true") : gen("false");
|
|
} break;
|
|
CASE_FLOAT : {
|
|
String postfix = get_type_postfix(type);
|
|
gen("%f%Q", a.f64_val, postfix);
|
|
} break;
|
|
case TYPE_TYPE: {
|
|
gen("%d", a.type_val->type_id);
|
|
} break;
|
|
default: {
|
|
if (is_string(type)) {
|
|
int length = 0;
|
|
gen("(%QString){(uint8_t *)\"", pctx->symbol_prefix);
|
|
for (int i = 0; i < a.intern_val.len; i++) {
|
|
if (a.intern_val.str[i] == '\n') {
|
|
length += 2;
|
|
gen("\\n");
|
|
}
|
|
else if (a.intern_val.str[i] == '\r') {
|
|
length += 2;
|
|
gen("\\r");
|
|
}
|
|
else {
|
|
length += 1;
|
|
gen("%c", a.intern_val.str[i]);
|
|
}
|
|
}
|
|
gen("\", %d}", length);
|
|
}
|
|
else result = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0) {
|
|
gen("{");
|
|
global_indent++;
|
|
For(scope->stmts) {
|
|
gen_line(it);
|
|
genln("");
|
|
gen_ast(it);
|
|
}
|
|
if (switch_case_gen_break == 1) genln("break;");
|
|
global_indent--;
|
|
gen_last_line();
|
|
genln("}");
|
|
}
|
|
|
|
enum {
|
|
ALWAYS_EMIT_VALUE = 0,
|
|
DONT_EMIT_VALUE = 1,
|
|
};
|
|
|
|
CORE_Static void
|
|
gen_pointer(Ast_Expr *expr) {
|
|
gen("&");
|
|
if (is_flag_set(expr->flags, AST_IS_LVALUE)) {
|
|
gen_expr(expr);
|
|
}
|
|
else {
|
|
gen("(");
|
|
gen_simple_decl(expr->resolved_type);
|
|
gen(")");
|
|
gen("{");
|
|
gen_expr(expr);
|
|
gen("}");
|
|
}
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type) {
|
|
|
|
// We want normal values to get boxed as Any pointers
|
|
// but other then that we shouldn't box other Any values
|
|
if (expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)) {
|
|
gen("(Any){");
|
|
gen_pointer(expr);
|
|
gen(", %d}", expr->resolved_type->type_id);
|
|
}
|
|
else if (expr->kind == AST_IDENT && is_slice(decl_type)) {
|
|
Ast_Atom *atom = (Ast_Atom *)expr;
|
|
gen("(");
|
|
gen_simple_decl(decl_type);
|
|
gen(")");
|
|
gen("{%d, ", atom->resolved_type->arr.size);
|
|
gen_expr(expr);
|
|
gen("}");
|
|
}
|
|
else gen_expr(expr);
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names) {
|
|
if (is_flag_set(decl->flags, AST_FOREIGN)) gen("extern ");
|
|
gen_simple_decl(decl->type, decl->name);
|
|
if (is_flag_set(decl->flags, AST_FOREIGN)) return;
|
|
|
|
if (emit_value == DONT_EMIT_VALUE) {
|
|
return;
|
|
}
|
|
|
|
if (decl->expr) {
|
|
gen(" = ");
|
|
gen_try_any_or_slice(decl->expr, decl->type);
|
|
}
|
|
else { // Default zero
|
|
if (is_numeric(decl->type)) {
|
|
gen(" = 0");
|
|
}
|
|
else {
|
|
gen(" = {}");
|
|
}
|
|
}
|
|
}
|
|
|
|
CORE_Static void
|
|
gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true) {
|
|
gen_simple_decl(lambda->resolved_type->func.ret, name);
|
|
gen("(");
|
|
For(lambda->args) {
|
|
gen_var(it, DONT_EMIT_VALUE, true);
|
|
if (&it != (lambda->args.end() - 1))
|
|
gen(", ");
|
|
}
|
|
gen(")");
|
|
|
|
if (generate_block && lambda->scope) {
|
|
gen_stmt_scope(lambda->scope);
|
|
}
|
|
else gen(";");
|
|
}
|
|
|
|
CORE_Static bool
|
|
gen_expr(Ast_Expr *ast) {
|
|
switch (ast->kind) {
|
|
CASE(IDENT, Atom) {
|
|
if (node->resolved_decl->kind == AST_NAMESPACE)
|
|
return false;
|
|
|
|
if (node->resolved_decl->kind == AST_LAMBDA) {
|
|
gen("%Q", node->resolved_decl->unique_name);
|
|
}
|
|
|
|
else {
|
|
gen("%Q", node->intern_val);
|
|
}
|
|
|
|
BREAK();
|
|
}
|
|
|
|
CASE(VALUE, Atom) {
|
|
B32 written = gen_value(node->pos, node->value);
|
|
if (!written) {
|
|
gen("%Q", node->value.intern_val);
|
|
}
|
|
BREAK();
|
|
}
|
|
|
|
CASE(ARRAY, Array) {
|
|
gen("%d", node->resolved_type->type_id);
|
|
BREAK();
|
|
}
|
|
|
|
CASE(INDEX, Index) {
|
|
gen("(");
|
|
gen_expr(node->expr);
|
|
if (node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string) {
|
|
gen(".data");
|
|
}
|
|
else if (is_slice(node->index_original_type)) {
|
|
gen(".data");
|
|
}
|
|
gen("[");
|
|
gen_expr(node->index);
|
|
gen("]");
|
|
gen(")");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(LAMBDA_EXPR, Lambda) {
|
|
gen_lambda({}, node);
|
|
BREAK();
|
|
}
|
|
|
|
CASE(BINARY, Binary) {
|
|
if (node->op == TK_Dot) {
|
|
if (gen_expr(node->left)) {
|
|
if (node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->");
|
|
else gen(".");
|
|
}
|
|
gen_expr(node->right);
|
|
return true;
|
|
}
|
|
|
|
else if (node->op == TK_Arrow) {
|
|
gen("(");
|
|
gen("(");
|
|
gen_simple_decl(node->resolved_type);
|
|
gen(")");
|
|
gen_expr(node->left);
|
|
gen(")");
|
|
return true;
|
|
}
|
|
|
|
else if (node->resolved_operator_overload) {
|
|
gen("%Q(", node->resolved_operator_overload->unique_name);
|
|
gen_expr(node->left);
|
|
gen(", ");
|
|
gen_expr(node->right);
|
|
gen(")");
|
|
}
|
|
|
|
else {
|
|
if (!token_is_assign(node->op)) gen("(");
|
|
gen_expr(node->left);
|
|
gen("%s", name(node->op));
|
|
gen_expr(node->right);
|
|
if (!token_is_assign(node->op)) gen(")");
|
|
}
|
|
|
|
BREAK();
|
|
}
|
|
|
|
CASE(UNARY, Unary) {
|
|
if (node->resolved_operator_overload) {
|
|
gen("%Q(", node->resolved_operator_overload->unique_name);
|
|
gen_expr(node->expr);
|
|
gen(")");
|
|
}
|
|
|
|
else {
|
|
gen("(");
|
|
if (node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op));
|
|
gen_expr(node->expr);
|
|
if (node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op));
|
|
gen(")");
|
|
}
|
|
BREAK();
|
|
}
|
|
|
|
CASE(VAR, Decl) {
|
|
gen_ast(node);
|
|
BREAK();
|
|
}
|
|
|
|
CASE(LENGTH_OF, Call) {
|
|
Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node);
|
|
gen_expr(expr);
|
|
if (is_pointer(expr->resolved_type))
|
|
gen("->len");
|
|
else gen(".len");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(CALL, Call) {
|
|
gen("%Q(", node->resolved_decl->unique_name);
|
|
For(node->exprs) {
|
|
gen_try_any_or_slice(it->item, it->resolved_type);
|
|
if (!node->exprs.is_last(&it)) gen(", ");
|
|
}
|
|
gen(")");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(COMPOUND, Call) {
|
|
gen("(");
|
|
gen_simple_decl(node->resolved_type);
|
|
gen(")");
|
|
gen("{");
|
|
|
|
// We need to double wrap it because Any is a pointer
|
|
// We need to pass it a compound array
|
|
if (is_slice(node->resolved_type)) {
|
|
gen(".len = %d, ", node->exprs.len);
|
|
gen(".data = (");
|
|
gen_simple_decl(node->resolved_type->base);
|
|
gen("[]");
|
|
gen(")");
|
|
gen("{");
|
|
}
|
|
|
|
For(node->exprs) {
|
|
if (is_struct(node->resolved_type) || is_union(node->resolved_type))
|
|
gen(".%Q = ", it->resolved_name);
|
|
else if (is_array(node->resolved_type))
|
|
gen("[%d] = ", (int)it->resolved_index);
|
|
gen_try_any_or_slice(it->item, it->resolved_type);
|
|
|
|
if (!node->exprs.is_last(&it)) gen(", ");
|
|
}
|
|
|
|
if (is_slice(node->resolved_type)) gen("}");
|
|
gen("}");
|
|
BREAK();
|
|
}
|
|
|
|
invalid_default_case;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
- [ ] Assigning arrays to arrays doesn't work in C. Being able to do that seems like a good idea though
|
|
|
|
*/
|
|
|
|
CORE_Static void
|
|
gen_ast(Ast *ast) {
|
|
switch (ast->kind) {
|
|
|
|
CASE(RUNTIME_ASSERT, Builtin) {
|
|
if (node->assert_message.len == 0) gen("%QAssert", pctx->symbol_prefix);
|
|
else gen("%QAssertMessage", pctx->symbol_prefix);
|
|
gen("(");
|
|
gen_expr(node->expr);
|
|
if (node->assert_message.len) {
|
|
gen(", \"%Q\"", node->assert_message);
|
|
}
|
|
gen(");");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(RETURN, Return) {
|
|
if (is_tuple(node->resolved_type)) {
|
|
Scoped_Arena scratch(pctx->scratch);
|
|
|
|
Intern_String tuple_name = get_unique_name(node);
|
|
gen_simple_decl(node->resolved_type, tuple_name);
|
|
gen(";");
|
|
|
|
int i = 0;
|
|
For(node->expr) {
|
|
|
|
// We cant assign to array in C so we need a special case
|
|
if (is_array(it->resolved_type)) {
|
|
genln("%QMemoryCopy(&%Q.m%d, ", pctx->symbol_prefix, tuple_name, i);
|
|
gen_expr(it);
|
|
gen(", sizeof(%Q.m%d));", tuple_name, i);
|
|
}
|
|
|
|
else {
|
|
genln("%Q.m%d = ", tuple_name, i);
|
|
gen_expr(it);
|
|
gen(";");
|
|
}
|
|
|
|
i += 1;
|
|
}
|
|
gen_line(node);
|
|
genln("return %Q;", tuple_name);
|
|
#if 0
|
|
gen("return (");
|
|
gen_simple_decl(node->resolved_type);
|
|
gen("){");
|
|
int i = 0;
|
|
global_indent++;
|
|
For(node->expr){
|
|
gen_line(it);
|
|
genln("");
|
|
gen_expr(it);
|
|
if (!node->expr.is_last(&it)) {
|
|
gen(",");
|
|
}
|
|
}
|
|
global_indent--;
|
|
genln("");
|
|
gen("};");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
assert(node->expr.len <= 1);
|
|
gen("return ");
|
|
For(node->expr) gen_expr(it);
|
|
gen(";");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(VAR, Decl) {
|
|
gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true);
|
|
if (!is_flag_set(ast->flags, AST_EXPR)) gen(";");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(IF, If) {
|
|
For(node->ifs) {
|
|
gen_line(it);
|
|
genln("");
|
|
if (it->init) {
|
|
gen_expr(it->init);
|
|
gen(";");
|
|
genln("");
|
|
}
|
|
if (node->ifs.is_first(&it)) {
|
|
gen("if(");
|
|
gen_expr(it->expr);
|
|
gen(")");
|
|
gen_stmt_scope(it->scope);
|
|
}
|
|
else {
|
|
gen("else");
|
|
if (it->expr) {
|
|
gen(" if(");
|
|
gen_expr(it->expr);
|
|
gen(")");
|
|
}
|
|
gen_stmt_scope(it->scope);
|
|
}
|
|
}
|
|
BREAK();
|
|
}
|
|
|
|
CASE(BREAK, Break) {
|
|
unused(node);
|
|
gen("break;");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(PASS, Pass) {
|
|
unused(node);
|
|
gen("//pass");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(BINARY, Binary) {
|
|
gen_expr(node);
|
|
gen(";");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(FOR, For) {
|
|
|
|
// Array iter
|
|
if (node->is_array_traversal) {
|
|
gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line);
|
|
if (is_array(node->cond->resolved_type)) {
|
|
gen("%QBufferSize(", pctx->symbol_prefix);
|
|
gen_expr(node->cond);
|
|
gen(")");
|
|
}
|
|
else {
|
|
assert(is_slice(node->cond->resolved_type));
|
|
gen_expr(node->cond);
|
|
gen(".len");
|
|
}
|
|
|
|
gen("; _i%d+=1)", node->pos->line);
|
|
gen("{");
|
|
global_indent++;
|
|
genln("");
|
|
gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name);
|
|
gen(" = ");
|
|
gen_expr(node->cond);
|
|
if (node->is_also_slice_traversal) gen(".data");
|
|
gen(" + _i%d;", node->pos->line);
|
|
For(node->scope->stmts) {
|
|
gen_line(it);
|
|
genln("");
|
|
gen_ast(it);
|
|
}
|
|
global_indent--;
|
|
gen_last_line();
|
|
genln("}");
|
|
}
|
|
|
|
// Normal for loop
|
|
else {
|
|
gen("for(");
|
|
if (node->init) gen_expr(node->init);
|
|
gen(";");
|
|
if (node->cond) gen_expr(node->cond);
|
|
gen(";");
|
|
if (node->iter) gen_expr(node->iter);
|
|
gen(")");
|
|
gen_stmt_scope(node->scope);
|
|
}
|
|
|
|
BREAK();
|
|
}
|
|
|
|
CASE(LAMBDA, Decl) {
|
|
gen_line(node);
|
|
genln("");
|
|
|
|
if (is_flag_set(node->expr->flags, AST_FOREIGN)) {
|
|
gen("/*foreign*/");
|
|
}
|
|
gen_lambda(node->unique_name, node->lambda);
|
|
BREAK();
|
|
}
|
|
|
|
case AST_UNION:
|
|
CASE(STRUCT, Decl) {
|
|
gen("%s ", node->kind == AST_STRUCT ? "struct" : "union");
|
|
gen("%Q{", node->unique_name);
|
|
global_indent++;
|
|
is_inside_struct++;
|
|
For(node->scope->decls) {
|
|
genln("");
|
|
gen_ast(it);
|
|
}
|
|
|
|
is_inside_struct--;
|
|
global_indent--;
|
|
genln("};");
|
|
BREAK();
|
|
}
|
|
|
|
CASE(ENUM, Decl) {
|
|
gen("/*enum %Q{", node->name);
|
|
// @todo add typespec
|
|
global_indent++;
|
|
For(node->scope->decls) {
|
|
genln("%Q", it->name);
|
|
gen(" = ");
|
|
gen_value(it->pos, it->value);
|
|
gen(",");
|
|
}
|
|
global_indent--;
|
|
genln("};*/");
|
|
BREAK();
|
|
}
|
|
|
|
case AST_TYPE:
|
|
CASE(CONST, Decl) {
|
|
unused(node);
|
|
BREAK();
|
|
}
|
|
|
|
CASE(SWITCH, Switch) {
|
|
gen("switch(");
|
|
gen_expr(node->value);
|
|
gen("){");
|
|
global_indent++;
|
|
|
|
For(node->cases) {
|
|
For_Named(it->labels, label) {
|
|
gen_line(it);
|
|
genln("");
|
|
gen("case ");
|
|
gen_expr(label);
|
|
gen(":");
|
|
}
|
|
|
|
gen_stmt_scope(it->scope, it->fallthrough ? false : true);
|
|
}
|
|
|
|
if (node->default_scope) {
|
|
genln("default: ");
|
|
gen_stmt_scope(node->default_scope);
|
|
}
|
|
|
|
global_indent--;
|
|
gen_last_line();
|
|
genln("}");
|
|
|
|
BREAK();
|
|
}
|
|
|
|
CASE(VAR_UNPACK, Var_Unpack) {
|
|
For(node->vars)
|
|
gen_ast(it);
|
|
|
|
Scoped_Arena scratch(pctx->scratch);
|
|
|
|
Intern_String var_name = get_unique_name(node);
|
|
gen_simple_decl(node->resolved_type, var_name);
|
|
gen(" = ");
|
|
gen_expr(node->expr);
|
|
gen(";");
|
|
|
|
int i = 0;
|
|
For(node->vars) {
|
|
gen("%QMemoryCopy((void *)&%Q, (void *)&%Q.m%d, sizeof(%Q));", pctx->symbol_prefix, it->name, var_name, i++, it->name);
|
|
}
|
|
BREAK();
|
|
}
|
|
|
|
case AST_CONSTANT_ASSERT:
|
|
case AST_NAMESPACE: break;
|
|
|
|
default: {
|
|
assert(is_flag_set(ast->flags, AST_EXPR));
|
|
gen_expr((Ast_Expr *)ast);
|
|
gen(";");
|
|
}
|
|
}
|
|
}
|
|
|
|
CORE_Static String
|
|
core_type_to_string(Ast_Type *type) {
|
|
switch (type->kind) {
|
|
case TYPE_NONE: return "<NONE>"_s; break;
|
|
case TYPE_S64: return "S64"_s; break;
|
|
case TYPE_S32: return "S32"_s; break;
|
|
case TYPE_S16: return "S16"_s; break;
|
|
case TYPE_S8: return "S8"_s; break;
|
|
case TYPE_INT: return "int"_s; break;
|
|
case TYPE_CHAR: return "char"_s; break;
|
|
case TYPE_U64: return "U64"_s; break;
|
|
case TYPE_U32: return "U32"_s; break;
|
|
case TYPE_U16: return "U16"_s; break;
|
|
case TYPE_U8: return "U8"_s; break;
|
|
case TYPE_F32: return "F32"_s; break;
|
|
case TYPE_F64: return "F64"_s; break;
|
|
case TYPE_BOOL: return "Bool"_s; break;
|
|
case TYPE_STRING: return "String"_s; break;
|
|
case TYPE_VOID: return "void"_s; break;
|
|
case TYPE_POINTER: {
|
|
String base = core_type_to_string(type->base);
|
|
return pctx->fmt("*%Q", base);
|
|
} break;
|
|
|
|
case TYPE_LAMBDA: {
|
|
String_Builder *b = &pctx->helper_builder;
|
|
b->addf("(");
|
|
Array<Ast_Type *> &args = type->func.args;
|
|
For(args) {
|
|
String t = core_type_to_string(it);
|
|
b->addf("%Q", t);
|
|
if (!args.is_last(&it)) b->addf(", ");
|
|
}
|
|
b->addf(")");
|
|
if (type->func.ret) {
|
|
String t = core_type_to_string(type->func.ret);
|
|
b->addf("%Q", t);
|
|
}
|
|
String result = string_flatten(pctx->perm, b);
|
|
return result;
|
|
} break;
|
|
|
|
case TYPE_STRUCT:
|
|
case TYPE_UNION:
|
|
case TYPE_ENUM: {
|
|
// @fixme: we probably want a string on Ast_Type
|
|
// so that we don't have to reach into Ast_Decl
|
|
// for Structs Enums etc.
|
|
Ast_Decl *decl = (Ast_Decl *)type->ast;
|
|
return decl->name.s;
|
|
} break;
|
|
|
|
case TYPE_ARRAY: {
|
|
String base = core_type_to_string(type->base);
|
|
return pctx->fmt("[%u]%Q", type->arr.size, base);
|
|
} break;
|
|
|
|
case TYPE_SLICE: {
|
|
String base = core_type_to_string(type->base);
|
|
return pctx->fmt("[]%Q", base);
|
|
} break;
|
|
case TYPE_TUPLE: {
|
|
invalid_codepath;
|
|
} break;
|
|
|
|
case TYPE_TYPE: return "Type"_s; break;
|
|
case TYPE_UNTYPED_BOOL: return "UntypedBool"_s; break;
|
|
case TYPE_UNTYPED_INT: return "UntypedInt"_s; break;
|
|
case TYPE_UNTYPED_FLOAT: return "UntypedFloat"_s; break;
|
|
case TYPE_UNTYPED_STRING: return "UntypedString"_s; break;
|
|
|
|
case TYPE_COMPLETING: invalid_codepath; break;
|
|
case TYPE_INCOMPLETE: invalid_codepath; break;
|
|
case TYPE_POLYMORPH:
|
|
invalid_codepath;
|
|
break;
|
|
|
|
invalid_default_case;
|
|
}
|
|
invalid_return;
|
|
}
|
|
|
|
CORE_Static String
|
|
compile_to_c_code() {
|
|
pctx->time.code_generation = os_time();
|
|
|
|
#if 0
|
|
int di = 0;
|
|
For(pctx->ordered_decls) {
|
|
for (Ast_Iter iter = iterate_depth_first(pctx->heap, it); iter.ast; next(&iter)) {
|
|
Ast_Decl *decl = (Ast_Decl *)iter.ast;
|
|
Ast *ast = iter.ast;
|
|
Ast_Atom *atom = (Ast_Atom *)iter.ast;
|
|
|
|
switch (iter.kind) {
|
|
case AST_STRUCT: {
|
|
genln("%Q :: struct", decl->name);
|
|
global_indent += 1;
|
|
} break;
|
|
case AST_STRUCT + AST_NODE_END: {
|
|
global_indent -= 1;
|
|
} break;
|
|
case AST_LAMBDA: {
|
|
Ast_Lambda *lambda = decl->lambda;
|
|
genln("%Q :: (", decl->name);
|
|
For(lambda->args) {
|
|
gen("%Q: %Q, ", it->name, core_type_to_string(it->type));
|
|
}
|
|
gen(")");
|
|
|
|
// @cleanup @refactor: return value shouldn't be a array of expressions.
|
|
// It should be a single expression. So probably need a special type
|
|
// for that.
|
|
if (lambda->args.len) {
|
|
gen(": ");
|
|
For(lambda->args) {
|
|
gen("%Q ", core_type_to_string(it->type));
|
|
}
|
|
}
|
|
|
|
iter.skip_children = true;
|
|
iter.stack.add(decl);
|
|
if (lambda->scope) iter.stack.add(lambda->scope);
|
|
global_indent += 1;
|
|
} break;
|
|
case AST_LAMBDA + AST_NODE_END: {
|
|
global_indent -= 1;
|
|
} break;
|
|
case AST_VAR: {
|
|
genln("%Q: %Q", decl->name, core_type_to_string(decl->type));
|
|
} break;
|
|
case AST_VAR + AST_NODE_END: {
|
|
} break;
|
|
}
|
|
}
|
|
String str = string_flatten(pctx->perm, &pctx->gen);
|
|
printf("%s", str.str);
|
|
pctx->gen.reset();
|
|
di += 1;
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
prefixed_string_type = string_fmt(pctx->perm, "%QString", pctx->symbol_prefix);
|
|
if (pctx->single_header_library_mode) {
|
|
gen(R"(
|
|
/*
|
|
Do this:
|
|
#define %Q_IMPLEMENTATION
|
|
before you include this file in *one* C or C++ file to create the implementation.
|
|
|
|
// i.e. it should look like this:
|
|
#include ...
|
|
#include ...
|
|
#include ...
|
|
#define %Q_IMPLEMENTATION
|
|
#include "%Q.h"
|
|
|
|
You can #define %QAssert(x) to avoid using default assert
|
|
You can #define %QAssertMessage(x) to get more comprehensive error info
|
|
You can #define %QMemoryCopy(x) to avoid using default memory copy
|
|
*/
|
|
)",
|
|
pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name,
|
|
pctx->symbol_prefix, pctx->symbol_prefix, pctx->symbol_prefix);
|
|
genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name);
|
|
genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name);
|
|
}
|
|
|
|
gen(R"(
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
#ifndef Assert
|
|
#define Assert(x) do{if(!(x))(*(volatile int *)0 = 0);}while(0)
|
|
#endif
|
|
|
|
#ifndef AssertMessage
|
|
#define AssertMessage(x,...) Assert(x)
|
|
#endif
|
|
|
|
#ifndef MemoryCopy
|
|
#define MemoryCopy MemoryCopy_
|
|
|
|
static void
|
|
MemoryCopy_(void *dst, void *src, size_t size){
|
|
uint8_t *d = (uint8_t*)dst;
|
|
uint8_t *s = (uint8_t*)src;
|
|
for(size_t i = 0; i < size; i++){
|
|
d[i] = s[i];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define BufferSize(x) (sizeof(x)/sizeof((x)[0]))
|
|
|
|
)");
|
|
|
|
// Generate struct forward decls
|
|
For(pctx->ordered_decls) {
|
|
if (it->kind == AST_STRUCT) {
|
|
genln("typedef struct %Q %Q;", it->unique_name, it->unique_name);
|
|
}
|
|
else if (it->kind == AST_UNION) {
|
|
genln("typedef union %Q %Q;", it->unique_name, it->unique_name);
|
|
}
|
|
}
|
|
|
|
// Generate slice and tuple types
|
|
For_Named(pctx->all_types, type) {
|
|
Scoped_Arena scratch(pctx->scratch);
|
|
|
|
if (type->kind == TYPE_SLICE) {
|
|
genln("typedef struct Slice%llu{", type->type_id);
|
|
global_indent++;
|
|
genln("int64_t len;");
|
|
genln("");
|
|
gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s));
|
|
gen(";");
|
|
global_indent--;
|
|
genln("} Slice%llu;", type->type_id);
|
|
}
|
|
|
|
else if (type->kind == TYPE_TUPLE) {
|
|
genln("typedef struct Tuple%llu{", type->type_id);
|
|
global_indent++;
|
|
For(type->agg.members) {
|
|
genln("");
|
|
// @todo remove intern from gen
|
|
Intern_String name = pctx->internf("m%llu", type->agg.members.get_index(&it));
|
|
gen_simple_decl(it.type, name);
|
|
gen(";");
|
|
}
|
|
global_indent--;
|
|
genln("} Tuple%llu;", type->type_id);
|
|
}
|
|
}
|
|
|
|
Intern_String intern_main = pctx->intern("main"_s);
|
|
Intern_String intern_win_main = pctx->intern("WinMain"_s);
|
|
|
|
Ast_Decl *main = 0;
|
|
Ast_Decl *win_main = 0;
|
|
|
|
// Generate lambda forward decls
|
|
For(pctx->ordered_decls) {
|
|
if (it->kind == AST_LAMBDA) {
|
|
if (it->name == intern_main) {
|
|
main = it;
|
|
it->unique_name = it->name;
|
|
}
|
|
|
|
if (it->name == intern_win_main) {
|
|
win_main = it;
|
|
it->unique_name = it->name;
|
|
}
|
|
|
|
genln("");
|
|
|
|
gen_lambda(it->unique_name, it->lambda, false);
|
|
}
|
|
}
|
|
|
|
if (!main && !win_main) {
|
|
compiler_error(0, "Entry point is not defined! Try main or WinMain");
|
|
}
|
|
|
|
if (pctx->emit_type_info) {
|
|
// Generate language.core
|
|
for (S32 i = 0; i < pctx->base_language_ordered_decl_len; i++) {
|
|
Ast_Decl *it = get(&pctx->ordered_decls, i);
|
|
genln("");
|
|
gen_ast(it);
|
|
}
|
|
|
|
// Generate type info
|
|
genln("int64_t type_infos_len = %d;", length(&pctx->all_types));
|
|
genln("Type_Info *type_infos = (Type_Info[]){");
|
|
global_indent++;
|
|
For_Named(pctx->all_types, t) {
|
|
genln("{/*%Q*/.kind = %d, .size = %d, .align = %d, .is_unsigned = %s, .type = %d, ", typestring(t),
|
|
(S32)t->kind, (S32)t->size, (S32)t->align, t->is_unsigned ? "true" : "false", t->type_id);
|
|
switch (t->kind) {
|
|
case TYPE_POINTER:
|
|
case TYPE_SLICE: {
|
|
gen(".base_type = %d", t->base->type_id);
|
|
} break;
|
|
case TYPE_ARRAY: {
|
|
gen(".base_type = %d, ", t->base->type_id);
|
|
gen(".array_size = %d", t->arr.size);
|
|
} break;
|
|
case TYPE_LAMBDA: {
|
|
gen(".lambda_return = %d, ", t->func.ret->type_id);
|
|
gen(".lambda_arguments.len = %d, ", t->func.args.len);
|
|
gen(".lambda_arguments.data = (Type_Info[%d]){", t->func.args.len);
|
|
For_Named(t->func.args, arg) {
|
|
gen("{.type = %d}, ", arg->type_id);
|
|
}
|
|
gen("}");
|
|
} break;
|
|
case TYPE_UNION:
|
|
case TYPE_STRUCT: {
|
|
gen(".struct_members.len = %d, ", t->agg.members.len);
|
|
gen(".struct_members.data = (Type_Info_Struct_Member[]){");
|
|
For_Named(t->agg.members, m) {
|
|
gen("{.name = (%Q){(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", prefixed_string_type, m.name, m.name.len, m.type->type_id, m.offset);
|
|
}
|
|
gen("}");
|
|
} break;
|
|
default: {
|
|
}
|
|
// invalid_default_case;
|
|
}
|
|
gen("},");
|
|
}
|
|
global_indent--;
|
|
gen("};");
|
|
}
|
|
|
|
if (pctx->single_header_library_mode) {
|
|
genln("#endif");
|
|
genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name);
|
|
}
|
|
|
|
// Generate actual code
|
|
int index = 0;
|
|
For(pctx->ordered_decls) {
|
|
if (index >= pctx->base_language_ordered_decl_len) {
|
|
genln("");
|
|
gen_ast(it);
|
|
}
|
|
index += 1;
|
|
}
|
|
|
|
if (pctx->single_header_library_mode) {
|
|
genln("#endif");
|
|
}
|
|
|
|
String string_result = string_flatten(pctx->perm, &pctx->gen);
|
|
pctx->time.code_generation = os_time() - pctx->time.code_generation;
|
|
return string_result;
|
|
}
|