From 897cbde39cc091090837798321f641ea351381e3 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Wed, 10 Jun 2020 21:15:01 -0700 Subject: [PATCH] ainedit: initial .jaf support Initial support for compiling .jaf (System 4 language) files. So far this is limited to adding struct definitions and declaring new globals. The actual bytecode compilation is not yet implemented. This Bison grammar is based on a C grammar and should cover 95% of the System 4 language syntax already, though many AST nodes are not yet implemented. --- include/system4/ain.h | 5 + src/ain.c | 50 +++- src/tools/ainedit/ainedit.c | 12 + src/tools/ainedit/jaf.h | 194 ++++++++++++++ src/tools/ainedit/jaf_ast.c | 200 ++++++++++++++ src/tools/ainedit/jaf_compile.c | 236 +++++++++++++++++ src/tools/ainedit/jaf_eval.c | 257 ++++++++++++++++++ src/tools/ainedit/jaf_lexer.l | 193 ++++++++++++++ src/tools/ainedit/jaf_parser.y | 452 ++++++++++++++++++++++++++++++++ src/tools/ainedit/jaf_types.c | 181 +++++++++++++ src/tools/ainedit/meson.build | 8 +- 11 files changed, 1781 insertions(+), 7 deletions(-) create mode 100644 src/tools/ainedit/jaf.h create mode 100644 src/tools/ainedit/jaf_ast.c create mode 100644 src/tools/ainedit/jaf_compile.c create mode 100644 src/tools/ainedit/jaf_eval.c create mode 100644 src/tools/ainedit/jaf_lexer.l create mode 100644 src/tools/ainedit/jaf_parser.y create mode 100644 src/tools/ainedit/jaf_types.c diff --git a/include/system4/ain.h b/include/system4/ain.h index d68f0eb..51cd589 100644 --- a/include/system4/ain.h +++ b/include/system4/ain.h @@ -168,6 +168,7 @@ struct ain_initval { union { char *string_value; int32_t int_value; + float float_value; }; }; @@ -321,6 +322,10 @@ void ain_decrypt(uint8_t *buf, size_t len); struct ain_function *ain_get_function(struct ain *ain, char *name); int ain_get_function_index(struct ain *ain, struct ain_function *f); struct ain_struct *ain_get_struct(struct ain *ain, char *name); +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); +void ain_add_initval(struct ain *ain, struct ain_initval *init); void ain_free(struct ain *ain); void ain_free_functions(struct ain *ain); diff --git a/src/ain.c b/src/ain.c index 26f6d2d..d7e799d 100644 --- a/src/ain.c +++ b/src/ain.c @@ -61,15 +61,20 @@ static void init_func_ht(struct ain *ain) } } +static void struct_ht_add(struct ain *ain, struct ain_struct *s) +{ + struct ht_slot *kv = ht_put(ain->_struct_ht, s->name); + if (kv->value) { + ERROR("Duplicate structure names: '%s'", s->name); + } + kv->value = s; +} + static void init_struct_ht(struct ain *ain) { ain->_struct_ht = ht_create(1024); for (int i = 0; i < ain->nr_structures; i++) { - struct ht_slot *kv = ht_put(ain->_struct_ht, ain->structures[i].name); - if (kv->value) { - ERROR("Duplicate structure names: '%s'", ain->structures[i].name); - } - kv->value = &ain->structures[i]; + struct_ht_add(ain, &ain->structures[i]); } } @@ -183,6 +188,41 @@ struct ain_struct *ain_get_struct(struct ain *ain, char *name) return ht_get(ain->_struct_ht, name); } +int ain_get_struct_no(struct ain *ain, char *name) +{ + struct ain_struct *s = ain_get_struct(ain, name); + return s ? s - ain->structures : -1; +} + +int ain_add_struct(struct ain *ain, char *name) +{ + ain->structures = xrealloc_array(ain->structures, ain->nr_structures, ain->nr_structures+1, sizeof(struct ain_struct)); + ain->structures[ain->nr_structures].name = strdup(name); + ain->structures[ain->nr_structures].constructor = -1; + ain->structures[ain->nr_structures].destructor = -1; + struct_ht_add(ain, &ain->structures[ain->nr_structures]); + ain->nr_structures++; + return ain->nr_structures - 1; +} + +struct ain_variable *ain_add_global(struct ain *ain, char *name) +{ + int no = ain->nr_globals; + ain->globals = xrealloc_array(ain->globals, ain->nr_globals, ain->nr_globals+1, sizeof(struct ain_variable)); + ain->globals[no].name = strdup(name); + if (ain->version >= 12) + ain->globals[no].name2 = strdup(""); + ain->nr_globals++; + return &ain->globals[no]; +} + +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, + sizeof(struct ain_initval)); + ain->global_initvals[ain->nr_initvals++] = *init; +} + static const char *errtab[AIN_MAX_ERROR] = { [AIN_SUCCESS] = "Success", [AIN_FILE_ERROR] = "Error opening AIN file", diff --git a/src/tools/ainedit/ainedit.c b/src/tools/ainedit/ainedit.c index dcd31c3..28a0bf3 100644 --- a/src/tools/ainedit/ainedit.c +++ b/src/tools/ainedit/ainedit.c @@ -24,6 +24,7 @@ #include "ainedit.h" #include "system4.h" #include "system4/ain.h" +#include "jaf.h" static void usage(void) { @@ -32,6 +33,7 @@ static void usage(void) puts(""); puts(" -h, --help Display this message and exit"); puts(" -c, --code Update the CODE section (assemble .jam file)"); + puts(" --jaf Update AIN file from JAF source code"); puts(" -j, --json Update AIN file from JSON data"); puts(" -t, --text Update strings/messages"); puts(" --transcode Change the AIN file's text encoding"); @@ -48,6 +50,7 @@ extern int text_parse(void); enum { LOPT_HELP = 256, LOPT_CODE, + LOPT_JAF, LOPT_JSON, LOPT_TEXT, LOPT_TRANSCODE, @@ -86,6 +89,7 @@ int main(int argc, char *argv[]) struct ain *ain; int err = AIN_SUCCESS; const char *code_file = NULL; + const char *jaf_file = NULL; const char *decl_file = NULL; const char *text_file = NULL; const char *output_file = NULL; @@ -98,6 +102,7 @@ int main(int argc, char *argv[]) static struct option long_options[] = { { "help", no_argument, 0, LOPT_HELP }, { "code", required_argument, 0, LOPT_CODE }, + { "jaf", required_argument, 0, LOPT_JAF }, { "json", required_argument, 0, LOPT_JSON }, { "text", required_argument, 0, LOPT_TEXT }, { "transcode", required_argument, 0, LOPT_TRANSCODE }, @@ -123,6 +128,9 @@ int main(int argc, char *argv[]) case LOPT_CODE: code_file = optarg; break; + case LOPT_JAF: + jaf_file = optarg; + break; case 'j': case LOPT_JSON: decl_file = optarg; @@ -192,6 +200,10 @@ int main(int argc, char *argv[]) read_declarations(decl_file, ain); } + if (jaf_file) { + jaf_compile(ain, jaf_file); + } + if (code_file) { asm_assemble_jam(code_file, ain, flags); } diff --git a/src/tools/ainedit/jaf.h b/src/tools/ainedit/jaf.h new file mode 100644 index 0000000..1c9d1f0 --- /dev/null +++ b/src/tools/ainedit/jaf.h @@ -0,0 +1,194 @@ +/* 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 . + */ + +#ifndef AINEDIT_JAF_AST_H +#define AINEDIT_JAF_AST_H + +#include +#include +#include "system4/ain.h" + +struct string; + +enum jaf_type { + JAF_VOID, + JAF_INT, + JAF_FLOAT, + JAF_STRING, + JAF_STRUCT, + JAF_ENUM, + JAF_TYPEDEF, +}; + +enum jaf_type_qualifier { + JAF_QUAL_CONST = 1, + JAF_QUAL_REF = 2 +}; + +enum jaf_expression_type { + JAF_EXP_VOID = 0, + JAF_EXP_INT, + JAF_EXP_FLOAT, + JAF_EXP_STRING, + JAF_EXP_IDENTIFIER, + JAF_EXP_UNARY, + JAF_EXP_BINARY, + JAF_EXP_TERNARY, +}; + +enum jaf_operator { + JAF_NO_OPERATOR = 0, + JAF_AMPERSAND, + JAF_UNARY_PLUS, + JAF_UNARY_MINUS, + JAF_BIT_NOT, + JAF_LOG_NOT, + JAF_PRE_INC, + JAF_PRE_DEC, + JAF_POST_INC, + JAF_POST_DEC, + + JAF_MULTIPLY, + JAF_DIVIDE, + JAF_REMAINDER, + JAF_PLUS, + JAF_MINUS, + JAF_LSHIFT, + JAF_RSHIFT, + JAF_LT, + JAF_GT, + JAF_LTE, + JAF_GTE, + JAF_EQ, + JAF_BIT_AND, + JAF_BIT_XOR, + JAF_BIT_IOR, + JAF_LOG_AND, + JAF_LOG_OR, + JAF_ASSIGN, + JAF_MUL_ASSIGN, + JAF_DIV_ASSIGN, + JAF_MOD_ASSIGN, + JAF_ADD_ASSIGN, + JAF_SUB_ASSIGN, + JAF_LSHIFT_ASSIGN, + JAF_RSHIFT_ASSIGN, + JAF_AND_ASSIGN, + JAF_XOR_ASSIGN, + JAF_OR_ASSIGN, + JAF_REF_ASSIGN, +}; + +struct jaf_expression { + enum jaf_expression_type type; + enum jaf_operator op; + enum jaf_type derived_type; + union { + int i; + float f; + struct string *s; + // unary operators + struct jaf_expression *expr; + // binary operators + struct { + struct jaf_expression *lhs; + struct jaf_expression *rhs; + }; + // ternary operator + struct { + struct jaf_expression *condition; + struct jaf_expression *consequent; + struct jaf_expression *alternative; + }; + }; +}; + +struct jaf_type_specifier { + enum jaf_type type; + unsigned qualifiers; + struct string *name; + struct jaf_declaration_list *def; + int struct_no; +}; + +struct jaf_declarator { + struct string *name; + struct jaf_expression *init; + size_t array_rank; + size_t *array_dims; +}; + +struct jaf_declarator_list { + size_t nr_decls; + struct jaf_declarator **decls; +}; + +struct jaf_function { + // TODO +}; + +struct jaf_declaration { + bool function; + struct string *name; + struct jaf_type_specifier *type; + size_t array_rank; + size_t *array_dims; + struct jaf_expression *init; + struct jaf_function *fun; + int var_no; +}; + +struct jaf_declaration_list { + size_t nr_decls; + struct jaf_declaration **decls; +}; + +struct jaf_expression *jaf_integer(struct string *text); +struct jaf_expression *jaf_float(struct string *text); +struct jaf_expression *jaf_string(struct string *text); +struct jaf_expression *jaf_identifier(struct string *name); +struct jaf_expression *jaf_unary_expr(enum jaf_operator op, struct jaf_expression *expr); +struct jaf_expression *jaf_binary_expr(enum jaf_operator op, struct jaf_expression *lhs, struct jaf_expression *rhs); +struct jaf_expression *jaf_ternary_expr(struct jaf_expression *test, struct jaf_expression *cons, struct jaf_expression *alt); +struct jaf_expression *jaf_seq_expr(struct jaf_expression *head, struct jaf_expression *tail); + +struct jaf_type_specifier *jaf_type(enum jaf_type type); +struct jaf_type_specifier *jaf_struct(struct string *name, struct jaf_declaration_list *fields); +struct jaf_type_specifier *jaf_typedef(struct string *name); + +struct jaf_declarator *jaf_declarator(struct string *name); +struct jaf_declarator_list *jaf_declarators(struct jaf_declarator_list *head, struct jaf_declarator *tail); + +struct jaf_declaration_list *jaf_declaration(struct jaf_type_specifier *type, struct jaf_declarator_list *declarators); +struct jaf_declaration_list *jaf_type_declaration(struct jaf_type_specifier *type); +struct jaf_declaration_list *jaf_declarations(struct jaf_declaration_list *head, struct jaf_declaration_list *tail); + +void jaf_free_expr(struct jaf_expression *expr); + +// jaf_compile.c +struct ain *jaf_ain_out; +struct jaf_declaration_list *jaf_toplevel; +void jaf_compile(struct ain *out, const char *path); +void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type); + +// jaf_eval.c +struct jaf_expression *jaf_simplify(struct jaf_expression *in); +struct jaf_expression *jaf_compute_constexpr(struct jaf_expression *in); + +// jaf_types.c +void jaf_check_types(struct jaf_expression *expr, struct jaf_type_specifier *type); + +#endif /* AINEDIT_JAF_AST_H */ diff --git a/src/tools/ainedit/jaf_ast.c b/src/tools/ainedit/jaf_ast.c new file mode 100644 index 0000000..7816192 --- /dev/null +++ b/src/tools/ainedit/jaf_ast.c @@ -0,0 +1,200 @@ +/* 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/string.h" +#include "jaf.h" + +static struct jaf_expression *jaf_expr(enum jaf_expression_type type, enum jaf_operator op) +{ + struct jaf_expression *e = xcalloc(1, sizeof(struct jaf_expression)); + e->type = type; + e->op = op; + return e; +} + +struct jaf_expression *jaf_integer(struct string *text) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_INT, 0); + char *endptr; + errno = 0; + e->i = strtol(text->text, &endptr, 0); + if (errno || *endptr != '\0') + ERROR("Invalid integer constant"); + free_string(text); + return e; +} + +struct jaf_expression *jaf_float(struct string *text) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_FLOAT, 0); + char *endptr; + errno = 0; + e->f = strtof(text->text, &endptr); + if (errno || *endptr != '\0') + ERROR("Invalid floating point constant"); + free_string(text); + return e; +} + +struct jaf_expression *jaf_string(struct string *text) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_STRING, 0); + e->s = text; + return e; +} + +struct jaf_expression *jaf_identifier(struct string *name) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_IDENTIFIER, 0); + e->s = name; + return e; +} + +struct jaf_expression *jaf_unary_expr(enum jaf_operator op, struct jaf_expression *expr) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_UNARY, op); + e->expr = expr; + return e; +} + +struct jaf_expression *jaf_binary_expr(enum jaf_operator op, struct jaf_expression *lhs, struct jaf_expression *rhs) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_BINARY, op); + e->lhs = lhs; + e->rhs = rhs; + return e; +} + +struct jaf_expression *jaf_ternary_expr(struct jaf_expression *test, struct jaf_expression *cons, struct jaf_expression *alt) +{ + struct jaf_expression *e = jaf_expr(JAF_EXP_TERNARY, 0); + e->condition = test; + e->consequent = cons; + e->alternative = alt; + return e; +} + +struct jaf_expression *jaf_seq_expr(struct jaf_expression *head, struct jaf_expression *tail) +{ + ERROR("Sequence expressions not supported"); +} + +struct jaf_type_specifier *jaf_type(enum jaf_type type) +{ + struct jaf_type_specifier *p = xcalloc(1, sizeof(struct jaf_type_specifier)); + p->type = type; + p->struct_no = -1; + return p; +} + +struct jaf_type_specifier *jaf_struct(struct string *name, struct jaf_declaration_list *fields) +{ + struct jaf_type_specifier *p = jaf_type(JAF_STRUCT); + p->name = name; + p->def = fields; + return p; +} + +struct jaf_type_specifier *jaf_typedef(struct string *name) +{ + struct jaf_type_specifier *p = jaf_type(JAF_TYPEDEF); + p->name = name; + return p; +} + +struct jaf_declarator *jaf_declarator(struct string *name) +{ + struct jaf_declarator *d = xcalloc(1, sizeof(struct jaf_declarator)); + d->name = name; + return d; +} + +struct jaf_declarator_list *jaf_declarators(struct jaf_declarator_list *head, struct jaf_declarator *tail) +{ + if (!head) { + head = xcalloc(1, sizeof(struct jaf_declarator_list)); + } + + head->decls = xrealloc_array(head->decls, head->nr_decls, head->nr_decls+1, sizeof(struct jaf_declarator*)); + head->decls[head->nr_decls++] = tail; + return head; +} + +struct jaf_declaration_list *jaf_declaration(struct jaf_type_specifier *type, struct jaf_declarator_list *declarators) +{ + struct jaf_declaration_list *decls = xcalloc(1, sizeof(struct jaf_declaration_list)); + decls->nr_decls = declarators->nr_decls; + decls->decls = xcalloc(declarators->nr_decls, sizeof(struct jaf_declaration*)); + for (size_t i = 0; i < declarators->nr_decls; i++) { + struct jaf_declaration *decl = xcalloc(1, sizeof(struct jaf_declaration)); + decl->function = false; + decl->name = declarators->decls[i]->name; + decl->type = type; + decl->init = declarators->decls[i]->init; + decls->decls[i] = decl; + + free(declarators->decls[i]); + } + free(declarators); + return decls; +} + +struct jaf_declaration_list *jaf_type_declaration(struct jaf_type_specifier *type) +{ + struct jaf_declaration_list *decls = xcalloc(1, sizeof(struct jaf_declaration_list)); + decls->nr_decls = 1; + decls->decls = xcalloc(1, sizeof(struct jaf_declaration*)); + decls->decls[0] = xcalloc(1, sizeof(struct jaf_declaration)); + decls->decls[0]->type = type; + return decls; +} + + +struct jaf_declaration_list *jaf_declarations(struct jaf_declaration_list *head, struct jaf_declaration_list *tail) +{ + if (!head) + return tail; + + size_t nr_decls = head->nr_decls + tail->nr_decls; + head->decls = xrealloc_array(head->decls, head->nr_decls, nr_decls, sizeof(struct jaf_toplevel_decl*)); + + for (size_t i = 0; i < tail->nr_decls; i++) { + head->decls[head->nr_decls+i] = tail->decls[i]; + } + head->nr_decls = nr_decls; + + free(tail); + return head; +} + +void jaf_free_expr(struct jaf_expression *expr) +{ + if (expr->type == JAF_EXP_UNARY) { + jaf_free_expr(expr->expr); + } else if (expr->type == JAF_EXP_BINARY) { + jaf_free_expr(expr->lhs); + jaf_free_expr(expr->rhs); + } else if (expr->type == JAF_EXP_TERNARY) { + jaf_free_expr(expr->condition); + jaf_free_expr(expr->consequent); + jaf_free_expr(expr->alternative); + } + free(expr); +} diff --git a/src/tools/ainedit/jaf_compile.c b/src/tools/ainedit/jaf_compile.c new file mode 100644 index 0000000..717b418 --- /dev/null +++ b/src/tools/ainedit/jaf_compile.c @@ -0,0 +1,236 @@ +/* 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 +#include +#include "system4.h" +#include "system4/ain.h" +#include "system4/string.h" +#include "ainedit.h" +#include "jaf.h" +#include "jaf_parser.tab.h" + +FILE *yyin; +int yyparse(void); + +struct ain *jaf_ain_out; +struct jaf_declaration_list *jaf_toplevel; + +int sym_type(char *name) +{ + char *u = encode_text_to_input_format(name); + struct ain_struct *s = ain_get_struct(jaf_ain_out, u); + free(u); + + if (s) + return TYPEDEF_NAME; + return IDENTIFIER; +} + +static void define_types(struct ain *ain, struct jaf_declaration *decl); + +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_decls; i++) { + define_types(ain, type->def->decls[i]); + } +} + +static void define_types(struct ain *ain, struct jaf_declaration *decl) +{ + if (!decl) + ERROR("DECL IS NULL"); + if (!decl->type) + ERROR("TYPE IS NULL"); + if (decl->type->type != JAF_STRUCT) + return; + + jaf_define_struct(ain, 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(possibly_unused struct ain *ain, struct jaf_expression **expr, struct jaf_type_specifier *type) +{ + jaf_check_types(*expr, type); + *expr = jaf_simplify(*expr); +} + +static void resolve_types(struct ain *ain, struct jaf_declaration *decl) +{ + if (decl->function) + return; + 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_declaration_list *def = decl->type->def; + struct ain_variable *members = xcalloc(def->nr_decls, sizeof(struct ain_variable)); + for (size_t i = 0; i < def->nr_decls; i++) { + members[i].name = encode_text_to_input_format(def->decls[i]->name->text); + if (ain->version >= 12) + members[i].name2 = strdup(""); + jaf_to_ain_type(ain, &members[i].type, def->decls[i]->type); + } + + struct ain_struct *s = &ain->structures[decl->type->struct_no]; + s->nr_members = def->nr_decls; + s->members = members; +} + +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_declaration *decl) +{ + if (decl->function) { + WARNING("Functions not supported"); + return; + } + + // TODO: global constructors/array allocations +} + +void jaf_compile(struct ain *out, const char *path) +{ + if (path) { + if (!strcmp(path, "-")) + yyin = stdin; + else + yyin = fopen(path, "rb"); + if (!yyin) + ERROR("Opening input file '%s': %s", path, strerror(errno)); + } + jaf_ain_out = out; + yyparse(); + + if (!jaf_toplevel) + ERROR("Failed to parse .jaf file"); + + // pass 1: typedefs && struct definitions + for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) { + resolve_types(out, jaf_toplevel->decls[i]); + } + // pass 2: register globals (names, types) + for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) { + if (jaf_toplevel->decls[i]->name) { + if (jaf_toplevel->decls[i]->function) + ERROR("Functions not supported"); + else + add_global(out, jaf_toplevel->decls[i]); + } + } + // pass 3: type analysis & simplification & global initvals (static analysis) + for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) { + if (jaf_toplevel->decls[i]->init) { + analyze_expression(out, &jaf_toplevel->decls[i]->init, jaf_toplevel->decls[i]->type); + + // add initval to ain object + struct ain_initval init = { .global_index = jaf_toplevel->decls[i]->var_no }; + jaf_to_initval(&init, jaf_toplevel->decls[i]->init); + ain_add_initval(out, &init); + } + } + // pass 4: generate bytecode + for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) { + compile_declaration(out, jaf_toplevel->decls[i]); + } + + // 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 + // code needs to be added to the "0" function instead. + if (out->nr_initvals && !out->GSET.present) { + WARNING("%d global initvals ignored", out->nr_initvals); + } +} diff --git a/src/tools/ainedit/jaf_eval.c b/src/tools/ainedit/jaf_eval.c new file mode 100644 index 0000000..2687ec5 --- /dev/null +++ b/src/tools/ainedit/jaf_eval.c @@ -0,0 +1,257 @@ +/* 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 "system4.h" +#include "system4/string.h" +#include "jaf.h" + +struct jaf_expression *jaf_simplify(struct jaf_expression *in); + +static struct jaf_expression *jaf_simplify_negation(struct jaf_expression *expr) +{ + if (expr->type == JAF_EXP_INT) { + expr->i = -expr->i; + return expr; + } + if (expr->type == JAF_EXP_FLOAT) { + expr->f = -expr->f; + return expr; + } + + return jaf_unary_expr(JAF_UNARY_MINUS, expr); +} + +static struct jaf_expression *jaf_simplify_bitnot(struct jaf_expression *expr) +{ + if (expr->type == JAF_EXP_INT) { + expr->i = ~expr->i; + return expr; + } + + return jaf_unary_expr(JAF_BIT_NOT, expr); +} + +static struct jaf_expression *jaf_simplify_lognot(struct jaf_expression *expr) +{ + if (expr->type == JAF_EXP_INT) { + expr->i = !expr->i; + return expr; + } + + return jaf_unary_expr(JAF_LOG_NOT, expr); +} + +static struct jaf_expression *jaf_simplify_unary(struct jaf_expression *in) +{ + struct jaf_expression *out = jaf_simplify(in->expr); + switch (in->op) { + case JAF_UNARY_PLUS: + return out; + case JAF_UNARY_MINUS: + return jaf_simplify_negation(out); + case JAF_BIT_NOT: + return jaf_simplify_bitnot(out); + case JAF_LOG_NOT: + return jaf_simplify_lognot(out); + 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); + default: + break; + } + ERROR("Invalid unary operator"); +} + +static void jaf_normalize_for_arithmetic(struct jaf_expression *lhs, struct jaf_expression *rhs) +{ + 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; + } +} + +#define SIMPLIFY_ARITHMETIC_FUN(name, op_name, op) \ + static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \ + { \ + 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 (lhs->type == JAF_EXP_FLOAT && rhs->type == JAF_EXP_FLOAT) { \ + lhs->f = lhs->f op rhs->f; \ + free(rhs); \ + return lhs; \ + } \ + return jaf_binary_expr(op_name, lhs, rhs); \ + } + +#define SIMPLIFY_INTEGER_FUN(name, op_name, op) \ + static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \ + { \ + 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; \ + } \ + return jaf_binary_expr(op_name, lhs, rhs); \ + } + +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_multiply, JAF_MULTIPLY, *) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_divide, JAF_DIVIDE, /) +SIMPLIFY_INTEGER_FUN (jaf_simplify_remainder, JAF_REMAINDER, %) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_plus, JAF_PLUS, +) // TODO: string concatentation +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_minus, JAF_MINUS, -) +SIMPLIFY_INTEGER_FUN (jaf_simplify_lshift, JAF_LSHIFT, <<) +SIMPLIFY_INTEGER_FUN (jaf_simplify_rshift, JAF_RSHIFT, >>) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_lt, JAF_LT, <) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_gt, JAF_GT, >) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_lte, JAF_LTE, <=) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_gte, JAF_GTE, >=) +SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_eq, JAF_EQ, ==) +SIMPLIFY_INTEGER_FUN (jaf_simplify_bitand, JAF_BIT_AND, &) +SIMPLIFY_INTEGER_FUN (jaf_simplify_bitxor, JAF_BIT_XOR, ^) +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) +{ + enum jaf_operator op = in->op; + struct jaf_expression *lhs = jaf_simplify(in->lhs); + struct jaf_expression *rhs = jaf_simplify(in->rhs); + free(in); + + switch (op) { + case JAF_MULTIPLY: + return jaf_simplify_multiply(lhs, rhs); + case JAF_DIVIDE: + return jaf_simplify_divide(lhs, rhs); + case JAF_REMAINDER: + return jaf_simplify_remainder(lhs, rhs); + case JAF_PLUS: + return jaf_simplify_plus(lhs, rhs); + case JAF_MINUS: + return jaf_simplify_minus(lhs, rhs); + case JAF_LSHIFT: + return jaf_simplify_lshift(lhs, rhs); + case JAF_RSHIFT: + return jaf_simplify_rshift(lhs, rhs); + case JAF_LT: + return jaf_simplify_lt(lhs, rhs); + case JAF_GT: + return jaf_simplify_gt(lhs, rhs); + case JAF_LTE: + return jaf_simplify_lte(lhs, rhs); + case JAF_GTE: + return jaf_simplify_gte(lhs, rhs); + case JAF_EQ: + return jaf_simplify_eq(lhs, rhs); + case JAF_BIT_AND: + return jaf_simplify_bitand(lhs, rhs); + case JAF_BIT_XOR: + return jaf_simplify_bitxor(lhs, rhs); + case JAF_BIT_IOR: + return jaf_simplify_bitior(lhs, rhs); + case JAF_LOG_AND: + return jaf_simplify_logand(lhs, rhs); + case JAF_LOG_OR: + return jaf_simplify_logor(lhs, rhs); + 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: + return jaf_binary_expr(op, lhs, rhs); + default: + ERROR("Invalid binary operator"); + } +} + +struct jaf_expression *jaf_simplify_ternary(struct jaf_expression *in) +{ + in->condition = jaf_simplify(in->condition); + in->consequent = jaf_simplify(in->consequent); + in->alternative = jaf_simplify(in->alternative); + + if (in->condition->type == JAF_EXP_INT) { + if (in->condition->i) { + jaf_free_expr(in->condition); + jaf_free_expr(in->alternative); + free(in); + return in->consequent; + } else { + jaf_free_expr(in->condition); + jaf_free_expr(in->consequent); + free(in); + return in->alternative; + } + } + + return in; +} + +/* + * Simplify an expression by evaluating the constant parts. + */ +struct jaf_expression *jaf_simplify(struct jaf_expression *in) +{ + switch (in->type) { + case JAF_EXP_VOID: + case JAF_EXP_INT: + case JAF_EXP_FLOAT: + case JAF_EXP_STRING: + case JAF_EXP_IDENTIFIER: + return in; + case JAF_EXP_UNARY: + return jaf_simplify_unary(in); + case JAF_EXP_BINARY: + return jaf_simplify_binary(in); + case JAF_EXP_TERNARY: + return jaf_simplify_ternary(in); + } + ERROR("Invalid expression type"); +} + +struct jaf_expression *jaf_compute_constexpr(struct jaf_expression *in) +{ + struct jaf_expression *out = jaf_simplify(in); + switch (out->type) { + case JAF_EXP_INT: + case JAF_EXP_FLOAT: + case JAF_EXP_STRING: + return out; + default: + return NULL; + } +} + diff --git a/src/tools/ainedit/jaf_lexer.l b/src/tools/ainedit/jaf_lexer.l new file mode 100644 index 0000000..7b20790 --- /dev/null +++ b/src/tools/ainedit/jaf_lexer.l @@ -0,0 +1,193 @@ +/* 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 . + * + * Based on ANSI C grammar from http://www.quut.com/c/ANSI-C-grammar-l-2011.html + */ + +%e 1019 +%p 2807 +%n 371 +%k 284 +%a 1213 +%o 1117 + +O [0-7] +D [0-9] +NZ [1-9] +L [a-zA-Z_] +A [a-zA-Z_0-9] +H [a-fA-F0-9] +HP (0[xX]) +E ([Ee][+-]?{D}+) +P ([Pp][+-]?{D}+) +FS (f|F|l|L) +IS (((u|U)(l|L|ll|LL)?)|((l|L|ll|LL)(u|U)?)) +CP (u|U|L) +SP (u8|u|U|L) +ES (\\(['"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+)) +WS [ \t\v\n\f] + +%{ +#pragma GCC diagnostic ignored "-Wunused-function" +#include +#include "system4/string.h" +#include "jaf_parser.tab.h" + +extern void yyerror(const char *); /* prints grammar violation message */ + +extern int sym_type(const char *); /* returns type from symbol table */ + +#define sym_type(identifier) IDENTIFIER /* with no symbol table, fake it */ + +static void comment(void); +static int check_type(void); + +#define RETURN_STRING(tok_type) yylval.string = make_string(yytext, yyleng); return tok_type +%} + +%% +"/*" { comment(); } +"//".* { /* consume //-comment */ } + +"break" { return(BREAK); } +"case" { return(CASE); } +"char" { return(CHAR); } +"const" { return(CONST); } +"continue" { return(CONTINUE); } +"default" { return(DEFAULT); } +"do" { return(DO); } +"else" { return(ELSE); } +"enum" { return(ENUM); } +"float" { return(FLOAT); } +"for" { return(FOR); } +"goto" { return(GOTO); } +"if" { return(IF); } +"int" { return(INT); } +"long" { return(LONG); } +"return" { return(SYM_RETURN); } +"string" { return(STRING); } +"struct" { return(STRUCT); } +"switch" { return(SYM_SWITCH); } +"union" { return(UNION); } +"void" { return(VOID); } +"while" { return(WHILE); } +"bool" { return BOOL; } +"__func__" { return FUNC_NAME; } +"ref" { return SYM_REF; } + +{L}{A}* { return check_type(); } + +{HP}{H}+{IS}? { RETURN_STRING(I_CONSTANT); } +{NZ}{D}*{IS}? { RETURN_STRING(I_CONSTANT); } +"0"{O}*{IS}? { RETURN_STRING(I_CONSTANT); } +{CP}?"'"([^'\\\n]|{ES})+"'" { RETURN_STRING(I_CONSTANT); } + +{D}+{E}{FS}? { RETURN_STRING(F_CONSTANT); } +{D}*"."{D}+{E}?{FS}? { RETURN_STRING(F_CONSTANT); } +{D}+"."{E}?{FS}? { RETURN_STRING(F_CONSTANT); } +{HP}{H}+{P}{FS}? { RETURN_STRING(F_CONSTANT); } +{HP}{H}*"."{H}+{P}{FS}? { RETURN_STRING(F_CONSTANT); } +{HP}{H}+"."{P}{FS}? { RETURN_STRING(F_CONSTANT); } + +({SP}?\"([^"\\\n]|{ES})*\"{WS}*)+ { RETURN_STRING(STRING_LITERAL); } + +"..." { return ELLIPSIS; } +">>=" { return RIGHT_ASSIGN; } +"<<=" { return LEFT_ASSIGN; } +"+=" { return ADD_ASSIGN; } +"-=" { return SUB_ASSIGN; } +"*=" { return MUL_ASSIGN; } +"/=" { return DIV_ASSIGN; } +"%=" { return MOD_ASSIGN; } +"&=" { return AND_ASSIGN; } +"^=" { return XOR_ASSIGN; } +"|=" { return OR_ASSIGN; } +"<-" { return REF_ASSIGN; } +">>" { return RIGHT_OP; } +"<<" { return LEFT_OP; } +"++" { return INC_OP; } +"--" { return DEC_OP; } +"&&" { return AND_OP; } +"||" { return OR_OP; } +"<=" { return LE_OP; } +">=" { return GE_OP; } +"==" { return EQ_OP; } +"!=" { return NE_OP; } +";" { return ';'; } +("{"|"<%") { return '{'; } +("}"|"%>") { return '}'; } +"," { return ','; } +":" { return ':'; } +"=" { return '='; } +"(" { return '('; } +")" { return ')'; } +("["|"<:") { return '['; } +("]"|":>") { return ']'; } +"." { return '.'; } +"&" { return '&'; } +"!" { return '!'; } +"~" { return '~'; } +"-" { return '-'; } +"+" { return '+'; } +"*" { return '*'; } +"/" { return '/'; } +"%" { return '%'; } +"<" { return '<'; } +">" { return '>'; } +"^" { return '^'; } +"|" { return '|'; } +"?" { return '?'; } + +{WS}+ { /* whitespace separates tokens */ } +. { /* discard bad characters */ } + +%% + +int yywrap(void) /* called at end of input */ +{ + return 1; /* terminate now */ +} + +static void comment(void) +{ + int c; + + while ((c = input()) != 0) + if (c == '*') + { + while ((c = input()) == '*') + ; + + if (c == '/') + return; + + if (c == 0) + break; + } + yyerror("unterminated comment"); +} + +static int check_type(void) +{ + switch (sym_type(yytext)) + { + case TYPEDEF_NAME: /* previously defined */ + RETURN_STRING(TYPEDEF_NAME); + case ENUMERATION_CONSTANT: /* previously defined */ + RETURN_STRING(ENUMERATION_CONSTANT); + default: /* includes undefined */ + RETURN_STRING(IDENTIFIER); + } +} diff --git a/src/tools/ainedit/jaf_parser.y b/src/tools/ainedit/jaf_parser.y new file mode 100644 index 0000000..91150da --- /dev/null +++ b/src/tools/ainedit/jaf_parser.y @@ -0,0 +1,452 @@ +/* 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 . + * + * Based on ANSI C grammar from http://www.quut.com/c/ANSI-C-grammar-l-2011.html + */ + +%{ +#include "system4.h" +#include "system4/ain.h" +#include "jaf.h" +extern int yylex(); +void yyerror(const char *s); +%} + +%union { + int token; + struct string *string; + struct jaf_expression *expression; + struct jaf_type_specifier *type; + struct jaf_declarator *declarator; + struct jaf_declarator_list *declarators; + struct jaf_declaration_list *declarations; +} + +%token I_CONSTANT F_CONSTANT STRING_LITERAL +%token IDENTIFIER TYPEDEF_NAME ENUMERATION_CONSTANT + +%token INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN +%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN +%token XOR_ASSIGN OR_ASSIGN +%token SYM_REF REF_ASSIGN FUNC_NAME + +%token CONST +%token BOOL CHAR INT LONG FLOAT VOID STRING +%token STRUCT UNION ENUM ELLIPSIS + +%token CASE DEFAULT IF ELSE SYM_SWITCH WHILE DO FOR GOTO CONTINUE BREAK SYM_RETURN + +%type unary_operator assignment_operator type_qualifier +%type string param_identifer +%type initializer +%type postfix_expression unary_expression cast_expression +%type multiplicative_expression additive_expression shift_expression +%type relational_expression equality_expression and_expression +%type exclusive_or_expression inclusive_or_expression +%type logical_and_expression logical_or_expression +%type conditional_expression assignment_expression +%type expression constant_expression primary_expression constant +%type type_specifier struct_specifier declaration_specifiers +%type init_declarator declarator +%type init_declarator_list struct_declarator_list +%type translation_unit external_declaration declaration +%type struct_declaration struct_declaration_list + +/* + * Shift-reduce conflicts: + * 1. dangling else + */ +%expect 1 + +%start toplevel +%% + +primary_expression + : IDENTIFIER { $$ = jaf_identifier($1); } + | constant { $$ = $1; } + | string { $$ = jaf_string($1); } + | '(' expression ')' { $$ = $2; } + ; + +constant + : I_CONSTANT { $$ = jaf_integer($1); } /* includes character_constant */ + | F_CONSTANT { $$ = jaf_float($1); } + | ENUMERATION_CONSTANT { ERROR("Enums not supported"); } /* after it has been defined as such */ + ; + +enumeration_constant /* before it has been defined as such */ + : IDENTIFIER + ; + +string + : STRING_LITERAL { $$ = $1; } + | FUNC_NAME { ERROR("__func__ not supported"); } +// | STRING '(' assignment_expression ')' + ; + +postfix_expression + : primary_expression { $$ = $1; } + | postfix_expression '[' expression ']' { ERROR("Arrays not supported"); } + | postfix_expression '(' ')' { ERROR("Function calls not supported"); } + | type_specifier '(' expression ')' { ERROR("Cast not supported"); } + | postfix_expression '(' argument_expression_list ')' { ERROR("Function calls not supported"); } + | postfix_expression '.' IDENTIFIER { ERROR("Struct members not supported"); } + | postfix_expression INC_OP { $$ = jaf_unary_expr(JAF_POST_INC, $1); } + | postfix_expression DEC_OP { $$ = jaf_unary_expr(JAF_POST_DEC, $1); } +// | '(' type_specifier ')' '{' initializer_list '}' +// | '(' type_specifier ')' '{' initializer_list ',' '}' + ; + +argument_expression_list + : assignment_expression + | argument_expression_list ',' assignment_expression + ; + +unary_expression + : postfix_expression { $$ = $1; } + | INC_OP unary_expression { $$ = jaf_unary_expr(JAF_PRE_INC, $2); } + | DEC_OP unary_expression { $$ = jaf_unary_expr(JAF_PRE_DEC, $2); } + | unary_operator cast_expression { $$ = jaf_unary_expr($1, $2); } + ; + +unary_operator + : '&' { $$ = JAF_AMPERSAND; } + | '+' { $$ = JAF_UNARY_PLUS; } + | '-' { $$ = JAF_UNARY_MINUS; } + | '~' { $$ = JAF_BIT_NOT; } + | '!' { $$ = JAF_LOG_NOT; } + ; + +cast_expression + : unary_expression { $$ = $1; } + | '(' type_specifier ')' cast_expression { ERROR("Cast not supported"); } + ; + +multiplicative_expression + : cast_expression { $$ = $1; } + | multiplicative_expression '*' cast_expression { $$ = jaf_binary_expr(JAF_MULTIPLY, $1, $3); } + | multiplicative_expression '/' cast_expression { $$ = jaf_binary_expr(JAF_DIVIDE, $1, $3); } + | multiplicative_expression '%' cast_expression { $$ = jaf_binary_expr(JAF_REMAINDER, $1, $3); } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression '+' multiplicative_expression { $$ = jaf_binary_expr(JAF_PLUS, $1, $3); } + | additive_expression '-' multiplicative_expression { $$ = jaf_binary_expr(JAF_MINUS, $1, $3); } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { $$ = jaf_binary_expr(JAF_LSHIFT, $1, $3); } + | shift_expression RIGHT_OP additive_expression { $$ = jaf_binary_expr(JAF_RSHIFT, $1, $3); } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression '<' shift_expression { $$ = jaf_binary_expr(JAF_LT, $1, $3); } + | relational_expression '>' shift_expression { $$ = jaf_binary_expr(JAF_GT, $1, $3); } + | relational_expression LE_OP shift_expression { $$ = jaf_binary_expr(JAF_LTE, $1, $3); } + | relational_expression GE_OP shift_expression { $$ = jaf_binary_expr(JAF_GTE, $1, $3); } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { $$ = jaf_binary_expr(JAF_EQ, $1, $3); } + | equality_expression NE_OP relational_expression { $$ = jaf_unary_expr(JAF_LOG_NOT, jaf_binary_expr(JAF_EQ, $1, $3)); } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression '&' equality_expression { $$ = jaf_binary_expr(JAF_BIT_AND, $1, $3); } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression '^' and_expression { $$ = jaf_binary_expr(JAF_BIT_XOR, $1, $3); } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression '|' exclusive_or_expression { $$ = jaf_binary_expr(JAF_BIT_IOR, $1, $3); } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { $$ = jaf_binary_expr(JAF_LOG_AND, $1, $3); } + ; + +logical_or_expression + : logical_and_expression { $$ = $1; } + | logical_or_expression OR_OP logical_and_expression { $$ = jaf_binary_expr(JAF_LOG_OR, $1, $3); } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression '?' expression ':' conditional_expression { $$ = jaf_ternary_expr($1, $3, $5); } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { $$ = jaf_binary_expr($2, $1, $3); } + ; + +assignment_operator + : '=' { $$ = JAF_ASSIGN; } + | MUL_ASSIGN { $$ = JAF_MUL_ASSIGN; } + | DIV_ASSIGN { $$ = JAF_DIV_ASSIGN; } + | MOD_ASSIGN { $$ = JAF_MOD_ASSIGN; } + | ADD_ASSIGN { $$ = JAF_ADD_ASSIGN; } + | SUB_ASSIGN { $$ = JAF_SUB_ASSIGN; } + | LEFT_ASSIGN { $$ = JAF_LSHIFT_ASSIGN; } + | RIGHT_ASSIGN { $$ = JAF_RSHIFT_ASSIGN; } + | AND_ASSIGN { $$ = JAF_AND_ASSIGN; } + | XOR_ASSIGN { $$ = JAF_XOR_ASSIGN; } + | OR_ASSIGN { $$ = JAF_OR_ASSIGN; } + | REF_ASSIGN { $$ = JAF_REF_ASSIGN; } + ; + +expression + : assignment_expression { $$ = $1; } + | expression ',' assignment_expression { $$ = jaf_seq_expr($1, $3); } + ; + +constant_expression + : conditional_expression { $$ = $1; } /* with constraints */ + ; + +declaration + : declaration_specifiers ';' { $$ = jaf_type_declaration($1); } + | declaration_specifiers init_declarator_list ';' { $$ = jaf_declaration($1, $2); } + ; + +declaration_specifiers + : type_qualifier type_specifier { $$ = $2; $$->qualifiers = $1; } + | type_specifier { $$ = $1; } + ; + +init_declarator_list + : init_declarator { $$ = jaf_declarators(NULL, $1); } + | init_declarator_list ',' init_declarator { $$ = jaf_declarators($1, $3); } + ; + +init_declarator + : declarator '=' initializer { $$ = $1; $$->init = $3; } + | declarator { $$ = $1; } + ; + +type_specifier + : VOID { $$ = jaf_type(JAF_VOID); } + | CHAR { $$ = jaf_type(JAF_INT); } + | INT { $$ = jaf_type(JAF_INT); } + | LONG { $$ = jaf_type(JAF_INT); } + | FLOAT { $$ = jaf_type(JAF_FLOAT); } + | BOOL { $$ = jaf_type(JAF_INT); } + | STRING { $$ = jaf_type(JAF_STRING); } + | struct_specifier { $$ = $1; } + | enum_specifier { ERROR("Enums not supported"); } + | TYPEDEF_NAME { $$ = jaf_typedef($1); } /* after it has been defined as such */ + ; + +struct_specifier + : STRUCT param_identifer '{' struct_declaration_list '}' { $$ = jaf_struct($2, $4); jaf_define_struct(jaf_ain_out, $$); } + ; + +param_identifer + : IDENTIFIER { $$ = $1; } + | IDENTIFIER '<' type_parameter_list '>' { ERROR("Type parameters not supported"); } + ; + +type_parameter_list + : type_specifier + | type_parameter_list ',' type_specifier + ; + +struct_declaration_list + : struct_declaration { $$ = $1; } + | struct_declaration_list struct_declaration { $$ = jaf_declarations($1, $2); } + ; + +struct_declaration +// : type_specifier ';' /* for anonymous struct/union */ + : type_specifier struct_declarator_list ';' { $$ = jaf_declaration($1, $2); } + ; + +struct_declarator_list + : declarator { $$ = jaf_declarators(NULL, $1); } + | struct_declarator_list ',' declarator { $$ = jaf_declarators($1, $3); } + ; + +enum_specifier + : ENUM '{' enumerator_list '}' + | ENUM '{' enumerator_list ',' '}' + | ENUM IDENTIFIER '{' enumerator_list '}' + | ENUM IDENTIFIER '{' enumerator_list ',' '}' + ; + +enumerator_list + : enumerator + | enumerator_list ',' enumerator + ; + +enumerator /* identifiers must be flagged as ENUMERATION_CONSTANT */ + : enumeration_constant '=' constant_expression + | enumeration_constant + ; + +type_qualifier + : CONST { $$ = JAF_QUAL_CONST; } + | SYM_REF { $$ = JAF_QUAL_REF; } + ; + +declarator + : IDENTIFIER { $$ = jaf_declarator($1); } +// | '(' declarator ')' // XXX: causes shift-reduce conflict with function-like casts (e.g. "string(identifier)") + | array_allocation { ERROR("Arrays not supported"); } + | IDENTIFIER '(' parameter_type_list ')' { ERROR("Function declarator not supported"); } // ??? remove? + | IDENTIFIER '(' ')' { ERROR("Function declarator not supported"); } // ??? remove? + ; + +array_allocation + : IDENTIFIER '[' constant ']' + | array_allocation '[' constant ']' + ; + +parameter_type_list + : parameter_list ',' ELLIPSIS + | parameter_list + ; + +parameter_list + : parameter_declaration + | parameter_list ',' parameter_declaration + ; + +parameter_declaration + : declaration_specifiers declarator + | declaration_specifiers + ; + +initializer + : '{' initializer_list '}' { $$ = NULL; } + | '{' initializer_list ',' '}' { $$ = NULL; } + | assignment_expression { $$ = $1; } + ; + +initializer_list + : designation initializer + | initializer + | initializer_list ',' designation initializer + | initializer_list ',' initializer + ; + +designation + : designator_list '=' + ; + +designator_list + : designator + | designator_list designator + ; + +designator + : '[' constant_expression ']' + | '.' IDENTIFIER + ; + +statement + : labeled_statement + | compound_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + ; + +labeled_statement + : IDENTIFIER ':' statement + | CASE constant_expression ':' statement + | DEFAULT ':' statement + ; + +compound_statement + : '{' '}' + | '{' block_item_list '}' + ; + +block_item_list + : block_item + | block_item_list block_item + ; + +block_item + : declaration + | statement + ; + +expression_statement + : ';' + | expression ';' + ; + +selection_statement + : IF '(' expression ')' statement ELSE statement + | IF '(' expression ')' statement + | SYM_SWITCH '(' expression ')' statement + ; + +iteration_statement + : WHILE '(' expression ')' statement + | DO statement WHILE '(' expression ')' ';' + | FOR '(' expression_statement expression_statement ')' statement + | FOR '(' expression_statement expression_statement expression ')' statement + | FOR '(' declaration expression_statement ')' statement + | FOR '(' declaration expression_statement expression ')' statement + ; + +jump_statement + : GOTO IDENTIFIER ';' + | CONTINUE ';' + | BREAK ';' + | SYM_RETURN ';' + | SYM_RETURN expression ';' + ; + +toplevel + : translation_unit { jaf_toplevel = $1; } + ; + +translation_unit + : external_declaration { $$ = jaf_declarations(NULL, $1); } + | translation_unit external_declaration { $$ = jaf_declarations($1, $2); } + ; + +external_declaration + : function_definition { ERROR("Functions not yet supported"); } + | declaration { $$ = $1; } + ; + +function_definition + : declaration_specifiers declarator compound_statement + ; + +%% +#include + +void yyerror(const char *s) +{ + fflush(stdout); + fprintf(stderr, "*** %s\n", s); +} diff --git a/src/tools/ainedit/jaf_types.c b/src/tools/ainedit/jaf_types.c new file mode 100644 index 0000000..7ba9550 --- /dev/null +++ b/src/tools/ainedit/jaf_types.c @@ -0,0 +1,181 @@ +/* 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 "system4.h" +#include "jaf.h" + +// TODO: better error messages +#define TYPE_ERROR(expr, expected) ERROR("Type error") +#define TYPE_CHECK(expr, expected) { if (expr->derived_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 void jaf_derive_types(struct jaf_expression *expr); + +static enum jaf_type jaf_type_check_numeric(struct jaf_expression *expr) +{ + if (expr->derived_type != JAF_INT && expr->derived_type != JAF_FLOAT) + TYPE_ERROR(expr, JAF_INT); + return expr->derived_type; +} + +static enum jaf_type jaf_type_check_int(struct jaf_expression *expr) +{ + if (expr->derived_type != JAF_INT) + TYPE_ERROR(expr, JAF_INT); + return expr->derived_type; +} + +static enum jaf_type jaf_merge_types(enum jaf_type a, enum jaf_type b) +{ + if (a == b) + return a; + if (a == JAF_INT && b == JAF_FLOAT) + return JAF_FLOAT; + if (a == JAF_FLOAT && b == JAF_INT) + return JAF_FLOAT; + ERROR("Incompatible types"); +} + +static enum jaf_type jaf_check_types_unary(struct jaf_expression *expr) +{ + jaf_derive_types(expr->expr); + switch (expr->op) { + case JAF_UNARY_PLUS: + case JAF_UNARY_MINUS: + case JAF_PRE_INC: + case JAF_PRE_DEC: + case JAF_POST_INC: + case JAF_POST_DEC: + return jaf_type_check_numeric(expr->expr); + case JAF_BIT_NOT: + case JAF_LOG_NOT: + return jaf_type_check_int(expr->expr); + case JAF_AMPERSAND: + ERROR("Function types not supported"); + default: + break; + } + ERROR("Unhandled unary operator"); +} + +static enum jaf_type jaf_check_types_binary(struct jaf_expression *expr) +{ + jaf_derive_types(expr->lhs); + jaf_derive_types(expr->rhs); + switch (expr->op) { + // real ops + case JAF_MULTIPLY: + case JAF_DIVIDE: + case JAF_PLUS: + case JAF_MINUS: + return jaf_merge_types(jaf_type_check_numeric(expr->lhs), jaf_type_check_numeric(expr->rhs)); + // integer ops + case JAF_REMAINDER: + case JAF_LSHIFT: + case JAF_RSHIFT: + case JAF_BIT_AND: + case JAF_BIT_XOR: + case JAF_BIT_IOR: + jaf_type_check_int(expr->lhs); + jaf_type_check_int(expr->rhs); + return JAF_INT; + // comparison ops + case JAF_LT: + case JAF_GT: + case JAF_LTE: + case JAF_GTE: + case JAF_EQ: + jaf_type_check_numeric(expr->lhs); + jaf_type_check_numeric(expr->rhs); + return JAF_INT; + // boolean ops + case JAF_LOG_AND: + case JAF_LOG_OR: + jaf_type_check_int(expr->lhs); + jaf_type_check_int(expr->rhs); + return JAF_INT; + 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 expressions not supported"); + default: + break; + } + ERROR("Unhandled binary operator"); +} + +static enum jaf_type jaf_check_types_ternary(struct jaf_expression *expr) +{ + jaf_derive_types(expr->condition); + jaf_derive_types(expr->consequent); + jaf_derive_types(expr->alternative); + + TYPE_CHECK(expr->condition, JAF_INT); + if (expr->consequent->derived_type != expr->alternative->derived_type) { + // TODO: better error message + ERROR("Mismatched types in conditional expression"); + } + + return expr->consequent->derived_type; +} + +static enum jaf_type _jaf_check_types(struct jaf_expression *expr) +{ + switch (expr->type) { + case JAF_EXP_INT: + return JAF_INT; + case JAF_EXP_FLOAT: + return JAF_FLOAT; + case JAF_EXP_STRING: + return JAF_STRING; + case JAF_EXP_IDENTIFIER: + // TODO: need environment + ERROR("Identifiers not supported"); + case JAF_EXP_UNARY: + return jaf_check_types_unary(expr); + case JAF_EXP_BINARY: + return jaf_check_types_binary(expr); + case JAF_EXP_TERNARY: + return jaf_check_types_ternary(expr); + default: + break; + } + ERROR("Unhandled expression type"); +} + +static void jaf_derive_types(struct jaf_expression *expr) +{ + expr->derived_type = _jaf_check_types(expr); +} + +void jaf_check_types(struct jaf_expression *expr, struct jaf_type_specifier *type) +{ + jaf_derive_types(expr); + // TODO: struct types, etc. + if (expr->derived_type != type->type) { + TYPE_ERROR(expr, type->type); + } +} diff --git a/src/tools/ainedit/meson.build b/src/tools/ainedit/meson.build index ac31f0e..5fab65c 100644 --- a/src/tools/ainedit/meson.build +++ b/src/tools/ainedit/meson.build @@ -3,14 +3,18 @@ ainedit = ['../../cJSON.c', '../conv.c', 'ainedit.c', 'asm.c', + 'jaf_ast.c', + 'jaf_compile.c', + 'jaf_eval.c', + 'jaf_types.c', 'json.c', 'repack.c', 'text.c', 'transcode.c', ] -ainedit += flexgen.process('asm_lexer.l', 'text_lexer.l') -ainedit += bisongen.process('asm_parser.y', 'text_parser.y') +ainedit += flexgen.process('asm_lexer.l', 'text_lexer.l', 'jaf_lexer.l') +ainedit += bisongen.process('asm_parser.y', 'text_parser.y', 'jaf_parser.y') executable('ainedit', ainedit, dependencies : tool_deps,