From 2908e6fcdc5fdbd1d4ba7956c27014738f7798f9 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Tue, 16 Jun 2020 20:04:53 -0700 Subject: [PATCH] jaf: initial compiler implementation Supports reading from local variables (no assignment) and arithmetic. --- include/system4/ain.h | 4 +- include/system4/buffer.h | 1 + src/ain.c | 8 + src/buffer.c | 7 + src/tools/ainedit/jaf.h | 15 +- src/tools/ainedit/jaf_ast.c | 3 +- src/tools/ainedit/jaf_compile.c | 754 ++++++++++++------------ src/tools/ainedit/jaf_eval.c | 142 +++-- src/tools/ainedit/jaf_static_analysis.c | 451 ++++++++++++++ src/tools/ainedit/jaf_types.c | 42 +- src/tools/ainedit/meson.build | 1 + 11 files changed, 970 insertions(+), 458 deletions(-) create mode 100644 src/tools/ainedit/jaf_static_analysis.c diff --git a/include/system4/ain.h b/include/system4/ain.h index 89519c1..29d7b0e 100644 --- a/include/system4/ain.h +++ b/include/system4/ain.h @@ -124,7 +124,8 @@ enum ain_data_type { enum ain_variable_type { AIN_VAR_LOCAL, - AIN_VAR_MEMBER + AIN_VAR_MEMBER, + AIN_VAR_GLOBAL }; struct ain_type { @@ -327,6 +328,7 @@ int ain_get_struct_no(struct ain *ain, char *name); int ain_add_struct(struct ain *ain, char *name); struct ain_variable *ain_add_global(struct ain *ain, char *name); struct ain_variable *ain_get_global(struct ain *ain, char *name); +int ain_get_global_no(struct ain *ain, char *name); void ain_add_initval(struct ain *ain, struct ain_initval *init); int ain_add_function(struct ain *ain, struct ain_function *fun); diff --git a/include/system4/buffer.h b/include/system4/buffer.h index e6504e1..b94d131 100644 --- a/include/system4/buffer.h +++ b/include/system4/buffer.h @@ -38,6 +38,7 @@ char *buffer_strdata(struct buffer *r); void buffer_write_int32(struct buffer *b, uint32_t v); void buffer_write_int32_at(struct buffer *buf, size_t index, uint32_t v); +void buffer_write_int16(struct buffer *b, uint16_t v); void buffer_write_float(struct buffer *b, float f); void buffer_write_bytes(struct buffer *b, const uint8_t *bytes, size_t len); void buffer_write_string(struct buffer *b, struct string *s); diff --git a/src/ain.c b/src/ain.c index 385c579..734adcf 100644 --- a/src/ain.c +++ b/src/ain.c @@ -223,6 +223,7 @@ struct ain_variable *ain_add_global(struct ain *ain, char *name) ain->globals[no].name = strdup(name); if (ain->version >= 12) ain->globals[no].name2 = strdup(""); + ain->globals[no].var_type = AIN_VAR_GLOBAL; ain->nr_globals++; return &ain->globals[no]; } @@ -237,6 +238,12 @@ struct ain_variable *ain_get_global(struct ain *ain, char *name) return NULL; } +int ain_get_global_no(struct ain *ain, char *name) +{ + struct ain_variable *v = ain_get_global(ain, name); + return v ? v - ain->globals : -1; +} + void ain_add_initval(struct ain *ain, struct ain_initval *init) { ain->global_initvals = xrealloc_array(ain->global_initvals, ain->nr_initvals, ain->nr_initvals+1, @@ -629,6 +636,7 @@ static struct ain_variable *read_globals(struct ain_reader *r, int count, struct read_variable_type(r, &globals[i].type); if (ain->version >= 5) globals[i].group_index = read_int32(r); + globals[i].var_type = AIN_VAR_GLOBAL; } return globals; } diff --git a/src/buffer.c b/src/buffer.c index bcd32f0..ab96a32 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -113,6 +113,13 @@ void buffer_write_int32_at(struct buffer *buf, size_t index, uint32_t v) buf->index = tmp; } +void buffer_write_int16(struct buffer *b, uint16_t v) +{ + alloc_buffer(b, 2); + b->buf[b->index++] = (v & 0x00FF); + b->buf[b->index++] = (v & 0xFF00) >> 8; +} + void buffer_write_float(struct buffer *b, float f) { union { float f; uint32_t u; } v; diff --git a/src/tools/ainedit/jaf.h b/src/tools/ainedit/jaf.h index c06b301..ff59dab 100644 --- a/src/tools/ainedit/jaf.h +++ b/src/tools/ainedit/jaf.h @@ -35,8 +35,9 @@ enum jaf_type { }; enum jaf_type_qualifier { - JAF_QUAL_CONST = 1, - JAF_QUAL_REF = 2 + JAF_QUAL_CONST = 1, + JAF_QUAL_REF = 2, + JAF_QUAL_GLOBAL = 4, }; enum jaf_expression_type { @@ -122,6 +123,11 @@ struct jaf_expression { int i; float f; struct string *s; + struct { + struct string *name; + enum ain_variable_type var_type; + int var_no; + } ident; // unary operators struct jaf_expression *expr; // binary operators @@ -315,13 +321,16 @@ struct ain *jaf_ain_out; struct jaf_block *jaf_toplevel; void jaf_compile(struct ain *out, const char *path); void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type); -struct ain_variable *jaf_env_lookup(struct jaf_env *env, struct string *name); // jaf_eval.c struct jaf_expression *jaf_simplify(struct jaf_expression *in); // jaf_types.c +const char *jaf_typestr(enum jaf_type type); void jaf_derive_types(struct jaf_env *env, struct jaf_expression *expr); void jaf_check_type(struct jaf_expression *expr, struct jaf_type_specifier *type); +// jaf_static_analysis.c +struct jaf_block *jaf_static_analyze(struct ain *ain, struct jaf_block *block); + #endif /* AINEDIT_JAF_H */ diff --git a/src/tools/ainedit/jaf_ast.c b/src/tools/ainedit/jaf_ast.c index ce1f5c0..2390dee 100644 --- a/src/tools/ainedit/jaf_ast.c +++ b/src/tools/ainedit/jaf_ast.c @@ -79,7 +79,7 @@ struct jaf_expression *jaf_string(struct string *text) struct jaf_expression *jaf_identifier(struct string *name) { struct jaf_expression *e = jaf_expr(JAF_EXP_IDENTIFIER, 0); - e->s = name; + e->ident.name = name; return e; } @@ -135,6 +135,7 @@ struct jaf_expression *jaf_cast_expression(enum jaf_type type, struct jaf_expres struct jaf_expression *e = jaf_expr(JAF_EXP_CAST, 0); e->cast.type = type; e->cast.expr = expr; + e->value_type.type = type; return e; } diff --git a/src/tools/ainedit/jaf_compile.c b/src/tools/ainedit/jaf_compile.c index 3e534bf..6c339bc 100644 --- a/src/tools/ainedit/jaf_compile.c +++ b/src/tools/ainedit/jaf_compile.c @@ -21,6 +21,8 @@ #include #include "system4.h" #include "system4/ain.h" +#include "system4/buffer.h" +#include "system4/instructions.h" #include "system4/string.h" #include "ainedit.h" #include "jaf.h" @@ -44,438 +46,418 @@ int sym_type(char *name) return IDENTIFIER; } -static void define_types(struct ain *ain, struct jaf_block_item *decl); - -void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type) +static void write_opcode(struct buffer *out, uint16_t opcode) { - if (!type->name) - ERROR("Anonymous structs not supported"); - - char *u = encode_text_to_input_format(type->name->text); - if (ain_get_struct(ain, u)) { - ERROR("Redefining structs not supported"); - } - - type->struct_no = ain_add_struct(ain, u); - free(u); - - for (size_t i = 0; i < type->def->nr_items; i++) { - define_types(ain, type->def->items[i]); - } + buffer_write_int16(out, opcode); } -static void define_types(struct ain *ain, struct jaf_block_item *decl) +static void write_argument(struct buffer *out, uint32_t arg) { - if (!decl) - ERROR("DECL IS NULL"); - if (!decl->decl.type) - ERROR("TYPE IS NULL"); - if (decl->decl.type->type != JAF_STRUCT) - return; - - jaf_define_struct(ain, decl->decl.type); + buffer_write_int32(out, arg); } -static enum ain_data_type jaf_to_ain_data_type(enum jaf_type type) +static void write_instruction0(struct buffer *out, uint16_t opcode) { - switch (type) { - case JAF_VOID: return AIN_VOID; - case JAF_INT: return AIN_INT; - case JAF_FLOAT: return AIN_FLOAT; - case JAF_STRING: return AIN_STRING; - case JAF_STRUCT: return AIN_STRUCT; - case JAF_ENUM: ERROR("Enums not supported"); - default: ERROR("Unknown type: %d", type); - } + write_opcode(out, opcode); } -static void jaf_to_ain_type(possibly_unused struct ain *ain, struct ain_type *out, struct jaf_type_specifier *in) +static void write_instruction1(struct buffer *out, uint16_t opcode, uint32_t arg0) { - out->data = jaf_to_ain_data_type(in->type); - if (in->type == JAF_STRUCT) { - out->struc = in->struct_no; - } + write_opcode(out, opcode); + write_argument(out, arg0); } -static void resolve_typedef(struct ain *ain, struct jaf_type_specifier *type) +static void write_instruction2(struct buffer *out, uint16_t opcode, uint32_t arg0, uint32_t arg1) { - char *u = encode_text_to_input_format(type->name->text); - int sno = ain_get_struct_no(ain, u); - if (sno) { - type->type = JAF_STRUCT; - type->struct_no = sno; + write_opcode(out, opcode); + write_argument(out, arg0); + write_argument(out, arg1); +} + +static uint32_t flo2int(float f) +{ + union { uint32_t i; float f; } v = { .f = f}; + return v.i; +} + +static enum opcode jaf_op_to_opcode(enum jaf_operator op, enum jaf_type type) +{ + if (type == JAF_FLOAT) { + switch (op) { + case JAF_MULTIPLY: return F_MUL; + case JAF_DIVIDE: return F_DIV; + case JAF_PLUS: return F_ADD; + case JAF_MINUS: return F_SUB; + case JAF_LT: return F_LT; + case JAF_GT: return F_GT; + case JAF_LTE: return F_LTE; + case JAF_GTE: return F_GTE; + case JAF_EQ: return F_EQUALE; + case JAF_ASSIGN: return F_ASSIGN; + case JAF_MUL_ASSIGN: return F_MULA; + case JAF_DIV_ASSIGN: return F_DIVA; + case JAF_ADD_ASSIGN: return F_PLUSA; + case JAF_SUB_ASSIGN: return F_MINUSA; + case JAF_REF_ASSIGN: // TODO + default: ERROR("Invalid floating point operator"); + } + } else if (type == JAF_INT) { + switch (op) { + case JAF_MULTIPLY: return MUL; + case JAF_DIVIDE: return DIV; + case JAF_REMAINDER: return MOD; + case JAF_PLUS: return ADD; + case JAF_MINUS: return SUB; + case JAF_LSHIFT: return LSHIFT; + case JAF_RSHIFT: return RSHIFT; + case JAF_LT: return LT; + case JAF_GT: return GT; + case JAF_LTE: return LTE; + case JAF_GTE: return GTE; + case JAF_EQ: return EQUALE; + case JAF_BIT_AND: return AND; + case JAF_BIT_XOR: return XOR; + case JAF_BIT_IOR: return OR; + //case JAF_LOG_AND: return AND; + //case JAF_LOG_OR: return OR; + case JAF_ASSIGN: return ASSIGN; + case JAF_MUL_ASSIGN: return MULA; + case JAF_DIV_ASSIGN: return DIVA; + case JAF_MOD_ASSIGN: return MODA; + case JAF_ADD_ASSIGN: return PLUSA; + case JAF_SUB_ASSIGN: return MINUSA; + case JAF_LSHIFT_ASSIGN: return LSHIFTA; + case JAF_RSHIFT_ASSIGN: return RSHIFTA; + case JAF_AND_ASSIGN: return ANDA; + case JAF_XOR_ASSIGN: return XORA; + case JAF_OR_ASSIGN: return ORA; + case JAF_REF_ASSIGN: // TODO + default: ERROR("Invalid integer operator"); + } } else { - ERROR("Failed to resolve typedef \"%s\"", type->name->text); + ERROR("Invalid operator type"); } - - free(u); } -static void jaf_to_initval(struct ain_initval *dst, struct jaf_expression *expr) +static void compile_block(struct ain *ain, struct buffer *out, struct jaf_block *block); +static void compile_expression(struct ain *ain, struct buffer *out, struct jaf_expression *expr); + +static void compile_identifier(possibly_unused struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + if (expr->ident.var_type == AIN_VAR_LOCAL) { + write_instruction0(out, PUSHLOCALPAGE); + } else if (expr->ident.var_type == AIN_VAR_GLOBAL) { + write_instruction0(out, PUSHGLOBALPAGE); + } else { + ERROR("Invalid variable type"); + } + write_instruction1(out, PUSH, expr->ident.var_no); + write_instruction0(out, REF); +} + +static void compile_unary(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + switch (expr->op) { + case JAF_AMPERSAND: + ERROR("Function types not supported"); + case JAF_UNARY_PLUS: + compile_expression(ain, out, expr->expr); + break; + case JAF_UNARY_MINUS: + write_instruction0(out, INV); + break; + case JAF_BIT_NOT: + write_instruction0(out, COMPL); + break; + case JAF_LOG_NOT: + write_instruction0(out, NOT); + break; + case JAF_PRE_INC: + case JAF_PRE_DEC: + case JAF_POST_INC: + case JAF_POST_DEC: + ERROR("Increment/decrement not supported"); // TODO: need location type + default: + ERROR("Invalid unary operator"); + } +} + +static void compile_binary(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + switch (expr->op) { + case JAF_MULTIPLY: + case JAF_DIVIDE: + case JAF_REMAINDER: + case JAF_PLUS: + case JAF_MINUS: + case JAF_LSHIFT: + case JAF_RSHIFT: + case JAF_LT: + case JAF_GT: + case JAF_LTE: + case JAF_GTE: + case JAF_EQ: + case JAF_BIT_AND: + case JAF_BIT_XOR: + case JAF_BIT_IOR: + assert(expr->lhs->value_type.type == expr->rhs->value_type.type); + compile_expression(ain, out, expr->lhs); + compile_expression(ain, out, expr->rhs); + write_instruction0(out, jaf_op_to_opcode(expr->op, expr->value_type.type)); + break; + case JAF_LOG_AND: + ERROR("Logical AND not supported"); + break; + case JAF_LOG_OR: + ERROR("Logical OR not supported"); + break; + case JAF_ASSIGN: + case JAF_MUL_ASSIGN: + case JAF_DIV_ASSIGN: + case JAF_MOD_ASSIGN: + case JAF_ADD_ASSIGN: + case JAF_SUB_ASSIGN: + case JAF_LSHIFT_ASSIGN: + case JAF_RSHIFT_ASSIGN: + case JAF_AND_ASSIGN: + case JAF_XOR_ASSIGN: + case JAF_OR_ASSIGN: + case JAF_REF_ASSIGN: + ERROR("Assignment not supported"); + default: + ERROR("Invalid binary operator"); + } +} + +static void compile_ternary(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + ERROR("Ternary operator not supported"); +} + +static void compile_funcall(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + for (size_t i = 0; i < expr->call.args->nr_items; i++) { + compile_expression(ain, out, expr->call.args->items[i]); + } + write_instruction1(out, CALLFUNC, expr->call.func_no); +} + +static void compile_cast(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + enum jaf_type src_type = expr->cast.expr->value_type.type; + enum jaf_type dst_type = expr->cast.type; + compile_expression(ain, out, expr->cast.expr); + + if (src_type == dst_type) + return; + if (src_type == JAF_INT) { + if (dst_type == JAF_FLOAT) { + write_instruction0(out, ITOF); + } else if (dst_type == JAF_STRING) { + write_instruction0(out, I_STRING); + } else { + goto invalid_cast; + } + } else if (src_type == JAF_FLOAT) { + if (dst_type == JAF_INT) { + write_instruction0(out, FTOI); + } else if (dst_type == JAF_STRING) { + write_instruction0(out, FTOS); + } else { + goto invalid_cast; + } + } else if (src_type == JAF_STRING) { + if (dst_type == JAF_INT) { + write_instruction0(out, STOI); + } else { + goto invalid_cast; + } + } + return; +invalid_cast: + ERROR("Unsupported cast: %s to %s", jaf_typestr(src_type), jaf_typestr(dst_type)); +} + +static void compile_member(struct ain *ain, struct buffer *out, struct jaf_expression *expr) +{ + ERROR("struct member access not supported"); +} + +static void compile_expression(struct ain *ain, struct buffer *out, struct jaf_expression *expr) { switch (expr->type) { + case JAF_EXP_VOID: + break; case JAF_EXP_INT: - dst->data_type = AIN_INT; - dst->int_value = expr->i; + write_instruction1(out, PUSH, expr->i); break; case JAF_EXP_FLOAT: - dst->data_type = AIN_FLOAT; - dst->float_value = expr->f; + write_instruction1(out, F_PUSH, flo2int(expr->f)); break; case JAF_EXP_STRING: - dst->data_type = AIN_STRING; - dst->string_value = strdup(expr->s->text); + ERROR("strings not supported"); + case JAF_EXP_IDENTIFIER: + compile_identifier(ain, out, expr); break; - default: - ERROR("Initval is not constant"); + case JAF_EXP_UNARY: + compile_unary(ain, out, expr); + break; + case JAF_EXP_BINARY: + compile_binary(ain, out, expr); + break; + case JAF_EXP_TERNARY: + compile_ternary(ain, out, expr); + break; + //case JAF_EXP_SUBSCRIPT: + //compile_subscript(ain, out, expr); + //break; + case JAF_EXP_FUNCALL: + compile_funcall(ain, out, expr); + break; + case JAF_EXP_CAST: + compile_cast(ain, out, expr); + break; + case JAF_EXP_MEMBER: + compile_member(ain, out, expr); + break; + //case JAF_EXP_SEQ: + //compile_seq(ain, out, expr); + //break; } } -static void analyze_expression(struct jaf_env *env, struct jaf_expression **expr) +static void compile_vardecl(struct ain *ain, struct buffer *out, struct jaf_declaration *decl) { - if (!*expr) - return; - jaf_derive_types(env, *expr); - *expr = jaf_simplify(*expr); -} - -static void _analyze_block(struct jaf_env *env, struct jaf_block *block); -static void analyze_block(struct jaf_env *env, struct jaf_block *block); - -static void analyze_global_declaration(struct jaf_env *env, struct jaf_declaration *decl) -{ - if (!decl->init) - return; - analyze_expression(env, &decl->init); - assert(decl->type); - jaf_check_type(decl->init, decl->type); - // add initval to ain object - struct ain_initval init = { .global_index = decl->var_no }; - jaf_to_initval(&init, decl->init); - ain_add_initval(env->ain, &init); -} - -static void analyze_local_declaration(struct jaf_env *env, struct jaf_declaration *decl) -{ - assert(env->func_no >= 0 && env->func_no < env->ain->nr_functions); - assert(decl->var_no >= 0 && decl->var_no < env->ain->functions[env->func_no].nr_vars); - // add local to environment - env->locals = xrealloc_array(env->locals, env->nr_locals, env->nr_locals+1, sizeof(struct ain_variable*)); - env->locals[env->nr_locals++] = &env->ain->functions[env->func_no].vars[decl->var_no]; -} - -static void analyze_function(struct jaf_env *env, struct jaf_declaration *decl) -{ - // create new scope with function arguments - assert(decl->func_no < env->ain->nr_functions); - struct ain_function *fun = &env->ain->functions[decl->func_no]; - struct jaf_env *funenv = xcalloc(1, sizeof(struct jaf_env)); - funenv->ain = env->ain; - funenv->parent = env; - funenv->func_no = decl->func_no; - funenv->fundecl = decl; - funenv->nr_locals = fun->nr_args; - funenv->locals = xcalloc(funenv->nr_locals, sizeof(struct ain_variable*)); - for (size_t i = 0; i < funenv->nr_locals; i++) { - funenv->locals[i] = &fun->vars[i]; - } - - _analyze_block(funenv, decl->body); - free(funenv->locals); - free(funenv); -} - -static void analyze_statement(struct jaf_env *env, struct jaf_block_item *item) -{ - if (!item) - return; - switch (item->kind) { - case JAF_DECLARATION: - if (env->parent) - analyze_local_declaration(env, &item->decl); - else - analyze_global_declaration(env, &item->decl); + switch (decl->type->type) { + case JAF_VOID: + ERROR("void variable declaration"); + case JAF_INT: + write_instruction0(out, PUSHLOCALPAGE); + write_instruction1(out, PUSH, decl->var_no); + if (decl->init) { + compile_expression(ain, out, decl->init); + } else { + write_instruction1(out, PUSH, 0); + } + write_instruction0(out, ASSIGN); break; - case JAF_FUNDECL: - analyze_function(env, &item->decl); + case JAF_FLOAT: + write_instruction0(out, PUSHLOCALPAGE); + write_instruction1(out, PUSH, decl->var_no); + if (decl->init) { + compile_expression(ain, out, decl->init); + } else { + write_instruction1(out, F_PUSH, 0); + } break; - case JAF_STMT_LABELED: - analyze_statement(env, item->label.stmt); - break; - case JAF_STMT_COMPOUND: - analyze_block(env, item->block); - break; - case JAF_STMT_EXPRESSION: - analyze_expression(env, &item->expr); - break; - case JAF_STMT_IF: - analyze_expression(env, &item->cond.test); - analyze_statement(env, item->cond.consequent); - analyze_statement(env, item->cond.alternative); - break; - case JAF_STMT_SWITCH: - analyze_expression(env, &item->swi.expr); - analyze_block(env, item->swi.body); - break; - case JAF_STMT_WHILE: - case JAF_STMT_DO_WHILE: - analyze_expression(env, &item->while_loop.test); - analyze_statement(env, item->while_loop.body); - break; - case JAF_STMT_FOR: - analyze_block(env, item->for_loop.init); - analyze_expression(env, &item->for_loop.test); - analyze_expression(env, &item->for_loop.after); - analyze_statement(env, item->for_loop.body); - break; - case JAF_STMT_RETURN: - analyze_expression(env, &item->expr); - jaf_check_type(item->expr, env->fundecl->type); - break; - case JAF_STMT_CASE: - case JAF_STMT_DEFAULT: - analyze_statement(env, item->swi_case.stmt); - break; - case JAF_STMT_GOTO: - case JAF_STMT_CONTINUE: - case JAF_STMT_BREAK: + case JAF_STRING: + ERROR("strings not supported"); + case JAF_STRUCT: + ERROR("structs not supported"); + case JAF_ENUM: + ERROR("enums not supported"); + case JAF_TYPEDEF: + ERROR("Unresolved typedef"); + case JAF_FUNCTION: + ERROR("Function types not supported"); break; } } -static void _analyze_block(struct jaf_env *env, struct jaf_block *block) -{ - for (size_t i = 0; i < block->nr_items; i++) { - analyze_statement(env, block->items[i]); - } -} - -static void analyze_block(struct jaf_env *env, struct jaf_block *block) -{ - struct jaf_env *blockenv = xcalloc(1, sizeof(struct jaf_env)); - blockenv->ain = env->ain; - blockenv->parent = env; - - _analyze_block(blockenv, block); - free(blockenv->locals); - free(blockenv); -} - -static void resolve_types(struct ain *ain, struct jaf_block *block); - -static void resolve_decl_types(struct ain *ain, struct jaf_declaration *decl) -{ - if (decl->type->type == JAF_TYPEDEF) - resolve_typedef(ain, decl->type); - if (decl->type->type != JAF_STRUCT || !decl->type->def) - return; - - assert(decl->type->struct_no >= 0); // set in parse phase - assert(decl->type->struct_no < ain->nr_structures); - - // create struct definition in ain object - struct jaf_block *def = decl->type->def; - struct ain_variable *members = xcalloc(def->nr_items, sizeof(struct ain_variable)); - for (size_t i = 0; i < def->nr_items; i++) { - members[i].name = encode_text_to_input_format(def->items[i]->decl.name->text); - if (ain->version >= 12) - members[i].name2 = strdup(""); - jaf_to_ain_type(ain, &members[i].type, def->items[i]->decl.type); - } - - struct ain_struct *s = &ain->structures[decl->type->struct_no]; - s->nr_members = def->nr_items; - s->members = members; -} - -static void resolve_statement_types(struct ain *ain, struct jaf_block_item *item) +static void compile_statement(struct ain *ain, struct buffer *out, struct jaf_block_item *item) { switch (item->kind) { case JAF_DECLARATION: - resolve_decl_types(ain, &item->decl); + compile_vardecl(ain, out, &item->decl); break; - case JAF_FUNDECL: - if (item->decl.params) - resolve_types(ain, item->decl.params); - resolve_types(ain, item->decl.body); - break; - case JAF_STMT_LABELED: - resolve_statement_types(ain, item->label.stmt); - break; - case JAF_STMT_COMPOUND: - resolve_types(ain, item->block); - break; - case JAF_STMT_IF: - resolve_statement_types(ain, item->cond.consequent); - resolve_statement_types(ain, item->cond.alternative); - break; - case JAF_STMT_SWITCH: - resolve_types(ain, item->swi.body); - break; - case JAF_STMT_WHILE: - case JAF_STMT_DO_WHILE: - resolve_statement_types(ain, item->while_loop.body); - break; - case JAF_STMT_FOR: - resolve_types(ain, item->for_loop.init); - resolve_statement_types(ain, item->for_loop.body); - break; - case JAF_STMT_CASE: - case JAF_STMT_DEFAULT: - resolve_statement_types(ain, item->swi_case.stmt); - break; - case JAF_STMT_EXPRESSION: - case JAF_STMT_GOTO: - case JAF_STMT_CONTINUE: - case JAF_STMT_BREAK: - case JAF_STMT_RETURN: - break; - } -} - -static void resolve_types(struct ain *ain, struct jaf_block *block) -{ - for (size_t i = 0; i < block->nr_items; i++) { - resolve_statement_types(ain, block->items[i]); - } -} - -static void init_variable(struct ain *ain, struct ain_variable *var, struct string *name, struct jaf_type_specifier *type) -{ - var->name = encode_text_to_input_format(name->text); - if (ain->version >= 12) - var->name2 = strdup(""); - jaf_to_ain_type(ain, &var->type, type); -} - -static struct ain_variable *block_get_vars(struct ain *ain, struct jaf_block *block, struct ain_variable *vars, int *nr_vars); - -static struct ain_variable *block_item_get_vars(struct ain *ain, struct jaf_block_item *item, struct ain_variable *vars, int *nr_vars) -{ - switch (item->kind) { - case JAF_DECLARATION: - if (!item->decl.name) - break; - vars = xrealloc_array(vars, *nr_vars, *nr_vars + 1, sizeof(struct ain_variable)); - init_variable(ain, vars + *nr_vars, item->decl.name, item->decl.type); - (*nr_vars)++; - break; - case JAF_STMT_LABELED: - vars = block_item_get_vars(ain, item->label.stmt, vars, nr_vars); - break; - case JAF_STMT_COMPOUND: - vars = block_get_vars(ain, item->block, vars, nr_vars); - break; - case JAF_STMT_IF: - vars = block_item_get_vars(ain, item->cond.consequent, vars, nr_vars); - vars = block_item_get_vars(ain, item->cond.alternative, vars, nr_vars); - break; - case JAF_STMT_SWITCH: - vars = block_get_vars(ain, item->swi.body, vars, nr_vars); - break; - case JAF_STMT_WHILE: - case JAF_STMT_DO_WHILE: - vars = block_item_get_vars(ain, item->while_loop.body, vars, nr_vars); - break; - case JAF_STMT_FOR: - vars = block_get_vars(ain, item->for_loop.init, vars, nr_vars); - vars = block_item_get_vars(ain, item->for_loop.body, vars, nr_vars); - break; - case JAF_STMT_CASE: - case JAF_STMT_DEFAULT: - vars = block_item_get_vars(ain, item->swi_case.stmt, vars, nr_vars); - break; - - case JAF_STMT_EXPRESSION: - case JAF_STMT_GOTO: - case JAF_STMT_CONTINUE: - case JAF_STMT_BREAK: - case JAF_STMT_RETURN: - break; - case JAF_FUNDECL: ERROR("Nested functions not supported"); + case JAF_STMT_LABELED: + ERROR("Labels not supported"); + break; + case JAF_STMT_COMPOUND: + compile_block(ain, out, item->block); + break; + case JAF_STMT_EXPRESSION: + compile_expression(ain, out, item->expr); + switch (item->expr->value_type.type) { + case JAF_VOID: + break; + case JAF_INT: + case JAF_FLOAT: + case JAF_STRING: + case JAF_STRUCT: // FIXME: this also needs a DELETE I think... + case JAF_ENUM: + case JAF_FUNCTION: + write_instruction0(out, POP); + break; + // TODO: immediate ref types will need additional POP + case JAF_TYPEDEF: + ERROR("Unresolved typedef"); + } + break; + case JAF_STMT_IF: + ERROR("if not supported"); + break; + case JAF_STMT_SWITCH: + ERROR("switch not supported"); + break; + case JAF_STMT_WHILE: + ERROR("while loops not supported"); + break; + case JAF_STMT_DO_WHILE: + ERROR("do-while loops not supported"); + break; + case JAF_STMT_FOR: + ERROR("for loops not supported"); + break; + case JAF_STMT_GOTO: + ERROR("goto not supported"); + case JAF_STMT_CONTINUE: + break; + case JAF_STMT_BREAK: + ERROR("break not supported"); + break; + case JAF_STMT_RETURN: + if (item->expr) + compile_expression(ain, out, item->expr); + write_instruction0(out, RETURN); + break; + case JAF_STMT_CASE: + ERROR("switch not supported"); + break; + case JAF_STMT_DEFAULT: + ERROR("switch not supported"); + break; } - return vars; } -static struct ain_variable *block_get_vars(struct ain *ain, struct jaf_block *block, struct ain_variable *vars, int *nr_vars) +static void compile_block(struct ain *ain, struct buffer *out, struct jaf_block *block) { for (size_t i = 0; i < block->nr_items; i++) { - vars = block_item_get_vars(ain, block->items[i], vars, nr_vars); + compile_statement(ain, out, block->items[i]); } - return vars; } -static void function_init_vars(struct ain *ain, struct ain_function *f, struct jaf_declaration *decl) +static void compile_function(struct ain *ain, struct buffer *out, struct jaf_declaration *decl) { - f->nr_args = decl->params ? decl->params->nr_items : 0; - f->nr_vars = f->nr_args; - f->vars = xcalloc(f->nr_args, sizeof(struct ain_variable)); - for (int i = 0; i < f->nr_args; i++) { - assert(decl->params->items[i]->kind == JAF_DECLARATION); - assert(decl->params->items[i]->decl.name); - struct jaf_declaration *param = &decl->params->items[i]->decl; - init_variable(ain, &f->vars[i], param->name, param->type); - } - f->vars = block_get_vars(ain, decl->body, f->vars, &f->nr_vars); + assert(decl->func_no >= 0 && decl->func_no < ain->nr_functions); + ain->functions[decl->func_no].address = out->index; + write_instruction1(out, FUNC, decl->func_no); + compile_block(ain, out, decl->body); + write_instruction1(out, ENDFUNC, decl->func_no); } -static void add_function(struct ain *ain, struct jaf_declaration *decl) -{ - struct ain_function f = {0}; - f.name = strdup(decl->name->text); - jaf_to_ain_type(ain, &f.return_type, decl->type); - function_init_vars(ain, &f, decl); - - decl->func_no = ain_add_function(ain, &f); -} - -static void add_global(struct ain *ain, struct jaf_declaration *decl) -{ - char *u = encode_text_to_input_format(decl->name->text); - struct ain_variable *v = ain_add_global(ain, u); - jaf_to_ain_type(ain, &v->type, decl->type); - decl->var_no = v - ain->globals; - free(u); -} - -static void compile_declaration(possibly_unused struct ain *ain, struct jaf_block_item *decl) +static void compile_declaration(struct ain *ain, struct buffer *code, struct jaf_block_item *decl) { if (decl->kind == JAF_FUNDECL) { - WARNING("Functions not supported"); + compile_function(ain, code, &decl->decl); return; } // TODO: global constructors/array allocations } -static struct ain_variable *jaf_scope_loopup(struct jaf_env *env, char *name) -{ - for (size_t i = 0; i < env->nr_locals; i++) { - if (!strcmp(env->locals[i]->name, name)) - return env->locals[i]; - } - return NULL; -} - -struct ain_variable *jaf_env_lookup(struct jaf_env *env, struct string *name) -{ - struct ain_variable *r; - char *u = encode_text_to_input_format(name->text); - struct jaf_env *scope = env; - while (scope) { - if ((r = jaf_scope_loopup(env, u))) - break; - scope = scope->parent; - } - if (!r) { - r = ain_get_global(env->ain, u); - } - free(u); - return r; -} - void jaf_compile(struct ain *out, const char *path) { if (path) { @@ -492,28 +474,18 @@ void jaf_compile(struct ain *out, const char *path) if (!jaf_toplevel) ERROR("Failed to parse .jaf file"); - struct jaf_env env = { - .ain = out, - .parent = NULL - }; + jaf_toplevel = jaf_static_analyze(out, jaf_toplevel); - // pass 1: typedefs && struct definitions - resolve_types(out, jaf_toplevel); - // pass 2: register globals (names, types) + struct buffer buf = { + .buf = out->code, + .size = out->code_size, + .index = out->code_size + }; for (size_t i = 0; i < jaf_toplevel->nr_items; i++) { - if (jaf_toplevel->items[i]->decl.name) { - if (jaf_toplevel->items[i]->kind == JAF_FUNDECL) - add_function(out, &jaf_toplevel->items[i]->decl); - else - add_global(out, &jaf_toplevel->items[i]->decl); - } - } - // pass 3: type analysis & simplification & global initvals (static analysis) - _analyze_block(&env, jaf_toplevel); - // pass 4: generate bytecode - for (size_t i = 0; i < jaf_toplevel->nr_items; i++) { - compile_declaration(out, jaf_toplevel->items[i]); + compile_declaration(out, &buf, jaf_toplevel->items[i]); } + out->code = buf.buf; + out->code_size = buf.index; // XXX: ain_add_initval adds initval to GSET section of the ain file. // If the original ain file did not have a GSET section, init diff --git a/src/tools/ainedit/jaf_eval.c b/src/tools/ainedit/jaf_eval.c index d1c1cda..cac1cda 100644 --- a/src/tools/ainedit/jaf_eval.c +++ b/src/tools/ainedit/jaf_eval.c @@ -22,102 +22,125 @@ struct jaf_expression *jaf_simplify(struct jaf_expression *in); -static struct jaf_expression *jaf_simplify_negation(struct jaf_expression *expr) +static struct jaf_expression *jaf_simplify_negation(struct jaf_expression *in) { + struct jaf_expression *expr = in->expr; if (expr->type == JAF_EXP_INT) { + free(in); expr->i = -expr->i; return expr; } if (expr->type == JAF_EXP_FLOAT) { + free(in); expr->f = -expr->f; return expr; } - return jaf_unary_expr(JAF_UNARY_MINUS, expr); + return in; } -static struct jaf_expression *jaf_simplify_bitnot(struct jaf_expression *expr) +static struct jaf_expression *jaf_simplify_bitnot(struct jaf_expression *in) { + struct jaf_expression *expr = in->expr; if (expr->type == JAF_EXP_INT) { + free(in); expr->i = ~expr->i; return expr; } - return jaf_unary_expr(JAF_BIT_NOT, expr); + return in; } -static struct jaf_expression *jaf_simplify_lognot(struct jaf_expression *expr) +static struct jaf_expression *jaf_simplify_lognot(struct jaf_expression *in) { + struct jaf_expression *expr = in->expr; if (expr->type == JAF_EXP_INT) { + free(in); expr->i = !expr->i; return expr; } - return jaf_unary_expr(JAF_LOG_NOT, expr); + return in; } static struct jaf_expression *jaf_simplify_unary(struct jaf_expression *in) { - struct jaf_expression *out = jaf_simplify(in->expr); + struct jaf_expression *r = in->expr = jaf_simplify(in->expr); switch (in->op) { case JAF_UNARY_PLUS: - return out; + free(in); + return r; case JAF_UNARY_MINUS: - return jaf_simplify_negation(out); + return jaf_simplify_negation(in); case JAF_BIT_NOT: - return jaf_simplify_bitnot(out); + return jaf_simplify_bitnot(in); case JAF_LOG_NOT: - return jaf_simplify_lognot(out); + return jaf_simplify_lognot(in); case JAF_AMPERSAND: case JAF_PRE_INC: case JAF_PRE_DEC: case JAF_POST_INC: case JAF_POST_DEC: - return jaf_unary_expr(in->op, out); + return in; default: break; } ERROR("Invalid unary operator"); } -static void jaf_normalize_for_arithmetic(struct jaf_expression *lhs, struct jaf_expression *rhs) +static struct jaf_expression *simplify_cast_to_float(struct jaf_expression *e) { - if (lhs->type == JAF_EXP_FLOAT && rhs->type == JAF_EXP_INT) { - rhs->type = JAF_EXP_FLOAT; - rhs->f = rhs->i; - } else if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_FLOAT) { - lhs->type = JAF_EXP_FLOAT; - lhs->f = lhs->i; + if (e->value_type.type == JAF_FLOAT) + return e; + if (e->type == JAF_EXP_INT) { + float f = e->i; + e->type = JAF_EXP_FLOAT; + e->f = f; + return e; + } + return jaf_cast_expression(JAF_FLOAT, e); +} + +static void jaf_normalize_for_arithmetic(struct jaf_expression *e) +{ + if (e->lhs->value_type.type == JAF_FLOAT || e->rhs->value_type.type == JAF_FLOAT) { + e->lhs = simplify_cast_to_float(e->lhs); + e->rhs = simplify_cast_to_float(e->rhs); } } #define SIMPLIFY_ARITHMETIC_FUN(name, op_name, op) \ - static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \ + static struct jaf_expression *name(struct jaf_expression *e) \ { \ - jaf_normalize_for_arithmetic(lhs, rhs); \ - if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_INT) { \ - lhs->i = lhs->i op rhs->i; \ - free(rhs); \ - return lhs; \ + jaf_normalize_for_arithmetic(e); \ + if (e->lhs->type == JAF_EXP_INT && e->rhs->type == JAF_EXP_INT) { \ + struct jaf_expression *r = e->lhs; \ + r->i = r->i op e->rhs->i; \ + free(e->rhs); \ + free(e); \ + return r; \ } \ - if (lhs->type == JAF_EXP_FLOAT && rhs->type == JAF_EXP_FLOAT) { \ - lhs->f = lhs->f op rhs->f; \ - free(rhs); \ - return lhs; \ + if (e->lhs->type == JAF_EXP_FLOAT && e->rhs->type == JAF_EXP_FLOAT) { \ + struct jaf_expression *r = e->lhs; \ + r->f = r->f op e->rhs->f; \ + free(e->rhs); \ + free(e); \ + return r; \ } \ - return jaf_binary_expr(op_name, lhs, rhs); \ + return e; \ } #define SIMPLIFY_INTEGER_FUN(name, op_name, op) \ - static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \ + static struct jaf_expression *name(struct jaf_expression *e) \ { \ - jaf_normalize_for_arithmetic(lhs, rhs); \ - if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_INT) { \ - lhs->i = lhs->i op rhs->i; \ - free(rhs); \ - return lhs; \ + if (e->lhs->type == JAF_EXP_INT && e->rhs->type == JAF_EXP_INT) { \ + struct jaf_expression *r = e->lhs; \ + r->i = r->i op e->rhs->i; \ + free(e->rhs); \ + free(e); \ + return r; \ } \ - return jaf_binary_expr(op_name, lhs, rhs); \ + return e; \ } SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_multiply, JAF_MULTIPLY, *) @@ -138,48 +161,47 @@ SIMPLIFY_INTEGER_FUN (jaf_simplify_bitior, JAF_BIT_IOR, |) SIMPLIFY_INTEGER_FUN (jaf_simplify_logand, JAF_LOG_AND, &&) SIMPLIFY_INTEGER_FUN (jaf_simplify_logor, JAF_LOG_OR, ||) -static struct jaf_expression *jaf_simplify_binary(struct jaf_expression *in) +static struct jaf_expression *jaf_simplify_binary(struct jaf_expression *e) { - enum jaf_operator op = in->op; - struct jaf_expression *lhs = jaf_simplify(in->lhs); - struct jaf_expression *rhs = jaf_simplify(in->rhs); - free(in); + enum jaf_operator op = e->op; + e->lhs = jaf_simplify(e->lhs); + e->rhs = jaf_simplify(e->rhs); switch (op) { case JAF_MULTIPLY: - return jaf_simplify_multiply(lhs, rhs); + return jaf_simplify_multiply(e); case JAF_DIVIDE: - return jaf_simplify_divide(lhs, rhs); + return jaf_simplify_divide(e); case JAF_REMAINDER: - return jaf_simplify_remainder(lhs, rhs); + return jaf_simplify_remainder(e); case JAF_PLUS: - return jaf_simplify_plus(lhs, rhs); + return jaf_simplify_plus(e); case JAF_MINUS: - return jaf_simplify_minus(lhs, rhs); + return jaf_simplify_minus(e); case JAF_LSHIFT: - return jaf_simplify_lshift(lhs, rhs); + return jaf_simplify_lshift(e); case JAF_RSHIFT: - return jaf_simplify_rshift(lhs, rhs); + return jaf_simplify_rshift(e); case JAF_LT: - return jaf_simplify_lt(lhs, rhs); + return jaf_simplify_lt(e); case JAF_GT: - return jaf_simplify_gt(lhs, rhs); + return jaf_simplify_gt(e); case JAF_LTE: - return jaf_simplify_lte(lhs, rhs); + return jaf_simplify_lte(e); case JAF_GTE: - return jaf_simplify_gte(lhs, rhs); + return jaf_simplify_gte(e); case JAF_EQ: - return jaf_simplify_eq(lhs, rhs); + return jaf_simplify_eq(e); case JAF_BIT_AND: - return jaf_simplify_bitand(lhs, rhs); + return jaf_simplify_bitand(e); case JAF_BIT_XOR: - return jaf_simplify_bitxor(lhs, rhs); + return jaf_simplify_bitxor(e); case JAF_BIT_IOR: - return jaf_simplify_bitior(lhs, rhs); + return jaf_simplify_bitior(e); case JAF_LOG_AND: - return jaf_simplify_logand(lhs, rhs); + return jaf_simplify_logand(e); case JAF_LOG_OR: - return jaf_simplify_logor(lhs, rhs); + return jaf_simplify_logor(e); case JAF_ASSIGN: case JAF_MUL_ASSIGN: case JAF_DIV_ASSIGN: @@ -192,7 +214,7 @@ static struct jaf_expression *jaf_simplify_binary(struct jaf_expression *in) case JAF_XOR_ASSIGN: case JAF_OR_ASSIGN: case JAF_REF_ASSIGN: - return jaf_binary_expr(op, lhs, rhs); + return e; default: ERROR("Invalid binary operator"); } diff --git a/src/tools/ainedit/jaf_static_analysis.c b/src/tools/ainedit/jaf_static_analysis.c new file mode 100644 index 0000000..d8fa041 --- /dev/null +++ b/src/tools/ainedit/jaf_static_analysis.c @@ -0,0 +1,451 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include "system4.h" +#include "system4/ain.h" +#include "system4/string.h" +#include "ainedit.h" +#include "jaf.h" + +static void define_types(struct ain *ain, struct jaf_block_item *decl); +static void jaf_resolve_types(struct ain *ain, struct jaf_block *block); +static void jaf_process_declarations(struct ain *ain, struct jaf_block *block); +static void jaf_analyze_block(struct jaf_env *env, struct jaf_block *block); + +void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type) +{ + if (!type->name) + ERROR("Anonymous structs not supported"); + + char *u = encode_text_to_input_format(type->name->text); + if (ain_get_struct(ain, u)) { + ERROR("Redefining structs not supported"); + } + + type->struct_no = ain_add_struct(ain, u); + free(u); + + for (size_t i = 0; i < type->def->nr_items; i++) { + define_types(ain, type->def->items[i]); + } +} + +static void define_types(struct ain *ain, struct jaf_block_item *decl) +{ + if (!decl) + ERROR("DECL IS NULL"); + if (!decl->decl.type) + ERROR("TYPE IS NULL"); + if (decl->decl.type->type != JAF_STRUCT) + return; + + jaf_define_struct(ain, decl->decl.type); +} + +static enum ain_data_type jaf_to_ain_data_type(enum jaf_type type) +{ + switch (type) { + case JAF_VOID: return AIN_VOID; + case JAF_INT: return AIN_INT; + case JAF_FLOAT: return AIN_FLOAT; + case JAF_STRING: return AIN_STRING; + case JAF_STRUCT: return AIN_STRUCT; + case JAF_ENUM: ERROR("Enums not supported"); + default: ERROR("Unknown type: %d", type); + } +} + +static void jaf_to_ain_type(possibly_unused struct ain *ain, struct ain_type *out, struct jaf_type_specifier *in) +{ + out->data = jaf_to_ain_data_type(in->type); + if (in->type == JAF_STRUCT) { + out->struc = in->struct_no; + } +} + +static void resolve_typedef(struct ain *ain, struct jaf_type_specifier *type) +{ + char *u = encode_text_to_input_format(type->name->text); + int sno = ain_get_struct_no(ain, u); + if (sno) { + type->type = JAF_STRUCT; + type->struct_no = sno; + } else { + ERROR("Failed to resolve typedef \"%s\"", type->name->text); + } + + free(u); +} + +static void jaf_to_initval(struct ain_initval *dst, struct jaf_expression *expr) +{ + switch (expr->type) { + case JAF_EXP_INT: + dst->data_type = AIN_INT; + dst->int_value = expr->i; + break; + case JAF_EXP_FLOAT: + dst->data_type = AIN_FLOAT; + dst->float_value = expr->f; + break; + case JAF_EXP_STRING: + dst->data_type = AIN_STRING; + dst->string_value = strdup(expr->s->text); + break; + default: + ERROR("Initval is not constant"); + } +} + +static void analyze_expression(struct jaf_env *env, struct jaf_expression **expr) +{ + if (!*expr) + return; + jaf_derive_types(env, *expr); + *expr = jaf_simplify(*expr); +} + +static void analyze_block(struct jaf_env *env, struct jaf_block *block); + +static void analyze_global_declaration(struct jaf_env *env, struct jaf_declaration *decl) +{ + if (!decl->init) + return; + analyze_expression(env, &decl->init); + assert(decl->type); + jaf_check_type(decl->init, decl->type); + // add initval to ain object + struct ain_initval init = { .global_index = decl->var_no }; + jaf_to_initval(&init, decl->init); + ain_add_initval(env->ain, &init); +} + +static void analyze_local_declaration(struct jaf_env *env, struct jaf_declaration *decl) +{ + assert(env->func_no >= 0 && env->func_no < env->ain->nr_functions); + assert(decl->var_no >= 0 && decl->var_no < env->ain->functions[env->func_no].nr_vars); + // add local to environment + env->locals = xrealloc_array(env->locals, env->nr_locals, env->nr_locals+1, sizeof(struct ain_variable*)); + env->locals[env->nr_locals++] = &env->ain->functions[env->func_no].vars[decl->var_no]; +} + +static void analyze_function(struct jaf_env *env, struct jaf_declaration *decl) +{ + // create new scope with function arguments + assert(decl->func_no < env->ain->nr_functions); + struct ain_function *fun = &env->ain->functions[decl->func_no]; + struct jaf_env *funenv = xcalloc(1, sizeof(struct jaf_env)); + funenv->ain = env->ain; + funenv->parent = env; + funenv->func_no = decl->func_no; + funenv->fundecl = decl; + funenv->nr_locals = fun->nr_args; + funenv->locals = xcalloc(funenv->nr_locals, sizeof(struct ain_variable*)); + for (size_t i = 0; i < funenv->nr_locals; i++) { + funenv->locals[i] = &fun->vars[i]; + } + + jaf_analyze_block(funenv, decl->body); + free(funenv->locals); + free(funenv); +} + +static void analyze_statement(struct jaf_env *env, struct jaf_block_item *item) +{ + if (!item) + return; + switch (item->kind) { + case JAF_DECLARATION: + if (env->parent) + analyze_local_declaration(env, &item->decl); + else + analyze_global_declaration(env, &item->decl); + break; + case JAF_FUNDECL: + analyze_function(env, &item->decl); + break; + case JAF_STMT_LABELED: + analyze_statement(env, item->label.stmt); + break; + case JAF_STMT_COMPOUND: + analyze_block(env, item->block); + break; + case JAF_STMT_EXPRESSION: + analyze_expression(env, &item->expr); + break; + case JAF_STMT_IF: + analyze_expression(env, &item->cond.test); + analyze_statement(env, item->cond.consequent); + analyze_statement(env, item->cond.alternative); + break; + case JAF_STMT_SWITCH: + analyze_expression(env, &item->swi.expr); + analyze_block(env, item->swi.body); + break; + case JAF_STMT_WHILE: + case JAF_STMT_DO_WHILE: + analyze_expression(env, &item->while_loop.test); + analyze_statement(env, item->while_loop.body); + break; + case JAF_STMT_FOR: + analyze_block(env, item->for_loop.init); + analyze_expression(env, &item->for_loop.test); + analyze_expression(env, &item->for_loop.after); + analyze_statement(env, item->for_loop.body); + break; + case JAF_STMT_RETURN: + analyze_expression(env, &item->expr); + jaf_check_type(item->expr, env->fundecl->type); + break; + case JAF_STMT_CASE: + case JAF_STMT_DEFAULT: + analyze_statement(env, item->swi_case.stmt); + break; + case JAF_STMT_GOTO: + case JAF_STMT_CONTINUE: + case JAF_STMT_BREAK: + break; + } +} + +static void jaf_analyze_block(struct jaf_env *env, struct jaf_block *block) +{ + for (size_t i = 0; i < block->nr_items; i++) { + analyze_statement(env, block->items[i]); + } +} + +static void analyze_block(struct jaf_env *env, struct jaf_block *block) +{ + struct jaf_env *blockenv = xcalloc(1, sizeof(struct jaf_env)); + blockenv->ain = env->ain; + blockenv->parent = env; + blockenv->func_no = env->func_no; + blockenv->fundecl = env->fundecl; + + jaf_analyze_block(blockenv, block); + free(blockenv->locals); + free(blockenv); +} + +static void resolve_decl_types(struct ain *ain, struct jaf_declaration *decl) +{ + if (decl->type->type == JAF_TYPEDEF) + resolve_typedef(ain, decl->type); + if (decl->type->type != JAF_STRUCT || !decl->type->def) + return; + + assert(decl->type->struct_no >= 0); // set in parse phase + assert(decl->type->struct_no < ain->nr_structures); + + // create struct definition in ain object + struct jaf_block *def = decl->type->def; + struct ain_variable *members = xcalloc(def->nr_items, sizeof(struct ain_variable)); + for (size_t i = 0; i < def->nr_items; i++) { + members[i].name = encode_text_to_input_format(def->items[i]->decl.name->text); + if (ain->version >= 12) + members[i].name2 = strdup(""); + jaf_to_ain_type(ain, &members[i].type, def->items[i]->decl.type); + } + + struct ain_struct *s = &ain->structures[decl->type->struct_no]; + s->nr_members = def->nr_items; + s->members = members; +} + +static void resolve_statement_types(struct ain *ain, struct jaf_block_item *item) +{ + switch (item->kind) { + case JAF_DECLARATION: + resolve_decl_types(ain, &item->decl); + break; + case JAF_FUNDECL: + if (item->decl.params) + jaf_resolve_types(ain, item->decl.params); + jaf_resolve_types(ain, item->decl.body); + break; + case JAF_STMT_LABELED: + resolve_statement_types(ain, item->label.stmt); + break; + case JAF_STMT_COMPOUND: + jaf_resolve_types(ain, item->block); + break; + case JAF_STMT_IF: + resolve_statement_types(ain, item->cond.consequent); + resolve_statement_types(ain, item->cond.alternative); + break; + case JAF_STMT_SWITCH: + jaf_resolve_types(ain, item->swi.body); + break; + case JAF_STMT_WHILE: + case JAF_STMT_DO_WHILE: + resolve_statement_types(ain, item->while_loop.body); + break; + case JAF_STMT_FOR: + jaf_resolve_types(ain, item->for_loop.init); + resolve_statement_types(ain, item->for_loop.body); + break; + case JAF_STMT_CASE: + case JAF_STMT_DEFAULT: + resolve_statement_types(ain, item->swi_case.stmt); + break; + case JAF_STMT_EXPRESSION: + case JAF_STMT_GOTO: + case JAF_STMT_CONTINUE: + case JAF_STMT_BREAK: + case JAF_STMT_RETURN: + break; + } +} + +static void jaf_resolve_types(struct ain *ain, struct jaf_block *block) +{ + for (size_t i = 0; i < block->nr_items; i++) { + resolve_statement_types(ain, block->items[i]); + } +} + +static void init_variable(struct ain *ain, struct ain_variable *var, struct string *name, struct jaf_type_specifier *type) +{ + var->name = encode_text_to_input_format(name->text); + if (ain->version >= 12) + var->name2 = strdup(""); + jaf_to_ain_type(ain, &var->type, type); +} + +static struct ain_variable *block_get_vars(struct ain *ain, struct jaf_block *block, struct ain_variable *vars, int *nr_vars); + +static struct ain_variable *block_item_get_vars(struct ain *ain, struct jaf_block_item *item, struct ain_variable *vars, int *nr_vars) +{ + switch (item->kind) { + case JAF_DECLARATION: + if (!item->decl.name) + break; + vars = xrealloc_array(vars, *nr_vars, *nr_vars + 1, sizeof(struct ain_variable)); + init_variable(ain, vars + *nr_vars, item->decl.name, item->decl.type); + (*nr_vars)++; + break; + case JAF_STMT_LABELED: + vars = block_item_get_vars(ain, item->label.stmt, vars, nr_vars); + break; + case JAF_STMT_COMPOUND: + vars = block_get_vars(ain, item->block, vars, nr_vars); + break; + case JAF_STMT_IF: + vars = block_item_get_vars(ain, item->cond.consequent, vars, nr_vars); + vars = block_item_get_vars(ain, item->cond.alternative, vars, nr_vars); + break; + case JAF_STMT_SWITCH: + vars = block_get_vars(ain, item->swi.body, vars, nr_vars); + break; + case JAF_STMT_WHILE: + case JAF_STMT_DO_WHILE: + vars = block_item_get_vars(ain, item->while_loop.body, vars, nr_vars); + break; + case JAF_STMT_FOR: + vars = block_get_vars(ain, item->for_loop.init, vars, nr_vars); + vars = block_item_get_vars(ain, item->for_loop.body, vars, nr_vars); + break; + case JAF_STMT_CASE: + case JAF_STMT_DEFAULT: + vars = block_item_get_vars(ain, item->swi_case.stmt, vars, nr_vars); + break; + + case JAF_STMT_EXPRESSION: + case JAF_STMT_GOTO: + case JAF_STMT_CONTINUE: + case JAF_STMT_BREAK: + case JAF_STMT_RETURN: + break; + + case JAF_FUNDECL: + ERROR("Nested functions not supported"); + } + return vars; +} + +static struct ain_variable *block_get_vars(struct ain *ain, struct jaf_block *block, struct ain_variable *vars, int *nr_vars) +{ + for (size_t i = 0; i < block->nr_items; i++) { + vars = block_item_get_vars(ain, block->items[i], vars, nr_vars); + } + return vars; +} + +static void function_init_vars(struct ain *ain, struct ain_function *f, struct jaf_declaration *decl) +{ + f->nr_args = decl->params ? decl->params->nr_items : 0; + f->nr_vars = f->nr_args; + f->vars = xcalloc(f->nr_args, sizeof(struct ain_variable)); + for (int i = 0; i < f->nr_args; i++) { + assert(decl->params->items[i]->kind == JAF_DECLARATION); + assert(decl->params->items[i]->decl.name); + struct jaf_declaration *param = &decl->params->items[i]->decl; + init_variable(ain, &f->vars[i], param->name, param->type); + } + f->vars = block_get_vars(ain, decl->body, f->vars, &f->nr_vars); +} + +static void add_function(struct ain *ain, struct jaf_declaration *decl) +{ + struct ain_function f = {0}; + f.name = strdup(decl->name->text); + jaf_to_ain_type(ain, &f.return_type, decl->type); + function_init_vars(ain, &f, decl); + + decl->func_no = ain_add_function(ain, &f); +} + +static void add_global(struct ain *ain, struct jaf_declaration *decl) +{ + char *u = encode_text_to_input_format(decl->name->text); + struct ain_variable *v = ain_add_global(ain, u); + jaf_to_ain_type(ain, &v->type, decl->type); + decl->var_no = v - ain->globals; + free(u); +} + +static void jaf_process_declarations(struct ain *ain, struct jaf_block *block) +{ + for (size_t i = 0; i < block->nr_items; i++) { + if (block->items[i]->decl.name) { + if (block->items[i]->kind == JAF_FUNDECL) + add_function(ain, &block->items[i]->decl); + else + add_global(ain, &block->items[i]->decl); + } + } +} + +struct jaf_block *jaf_static_analyze(struct ain *ain, struct jaf_block *block) +{ + struct jaf_env env = { + .ain = ain, + .parent = NULL + }; + + // pass 1: typedefs && struct definitions + jaf_resolve_types(ain, block); + // pass 2: register globals (names, types) + jaf_process_declarations(ain, block); + // pass 3: type analysis & simplification & global initvals + jaf_analyze_block(&env, block); + + return block; +} diff --git a/src/tools/ainedit/jaf_types.c b/src/tools/ainedit/jaf_types.c index c06c2dc..a5c4c65 100644 --- a/src/tools/ainedit/jaf_types.c +++ b/src/tools/ainedit/jaf_types.c @@ -27,7 +27,7 @@ #define TYPE_CHECK(expr, expected) { if (expr->value_type.type != expected) TYPE_ERROR(expr, expected); } #define TYPE_CHECK_NUMERIC(expr) { if (expr->derived_type != JAF_INT && expr->derived_type != JAF_FLOAT) TYPE_ERROR(expr, JAF_INT); } -static const char *jaf_typestr(enum jaf_type type) +const char *jaf_typestr(enum jaf_type type) { switch (type) { case JAF_VOID: return "void"; @@ -198,11 +198,49 @@ static void ain_to_jaf_type(struct jaf_type_specifier *dst, struct ain_type *src } } +static struct ain_variable *jaf_scope_lookup(struct jaf_env *env, char *name, int *var_no) +{ + for (size_t i = 0; i < env->nr_locals; i++) { + if (!strcmp(env->locals[i]->name, name)) { + *var_no = i; + return env->locals[i]; + } + } + return NULL; +} + +struct ain_variable *jaf_env_lookup(struct jaf_env *env, struct string *name, int *var_no) +{ + char *u = encode_text_to_input_format(name->text); + struct jaf_env *scope = env; + while (scope) { + struct ain_variable *v = jaf_scope_lookup(env, u, var_no); + if (v) { + free(u); + return v; + } + scope = scope->parent; + } + + int no = ain_get_global_no(env->ain, u); + free(u); + + if (no >= 0) { + *var_no = no; + return &env->ain->globals[no]; + } + + return NULL; +} + static void jaf_check_types_identifier(struct jaf_env *env, struct jaf_expression *expr) { - struct ain_variable *v = jaf_env_lookup(env, expr->s); + int var_no; + struct ain_variable *v = jaf_env_lookup(env, expr->ident.name, &var_no); if (v) { ain_to_jaf_type(&expr->value_type, &v->type); + expr->ident.var_type = v->var_type; + expr->ident.var_no = var_no; return; } diff --git a/src/tools/ainedit/meson.build b/src/tools/ainedit/meson.build index 5fab65c..471d3db 100644 --- a/src/tools/ainedit/meson.build +++ b/src/tools/ainedit/meson.build @@ -6,6 +6,7 @@ ainedit = ['../../cJSON.c', 'jaf_ast.c', 'jaf_compile.c', 'jaf_eval.c', + 'jaf_static_analysis.c', 'jaf_types.c', 'json.c', 'repack.c',