diff --git a/ast/ast.h b/ast/ast.h index b368bfa55..4a5747020 100644 --- a/ast/ast.h +++ b/ast/ast.h @@ -10,6 +10,7 @@ #include "ast/ast_stmts.h" #include "ast/ast_const.h" #include "ast/ast_struct.h" +#include "ast/ast_enum.h" #include "ast/ast_var.h" #include "ast/ast_expr.h" #include "ast/ast_subr.h" @@ -39,6 +40,11 @@ struct Namespace { uint16_t count_includes; char** includes; + // enum declarations + struct EnumDecl** enums; + uint16_t count_enums; + size_t capacity_enums; + //structs must be declared before the subroutines struct StructDecl** structs; uint16_t count_structs; diff --git a/ast/ast/ast_enum.h b/ast/ast/ast_enum.h new file mode 100644 index 000000000..010f42717 --- /dev/null +++ b/ast/ast/ast_enum.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../ast_declare.h" + +struct EnumDecl { + struct ASTNode super; + + // must be all uppercase (underscores are ok) + char* name; + + struct EnumMember** members; + uint16_t count_members; +}; + +struct EnumMember { + struct ASTNode super; + + char* name; + struct ConstValue* value; +}; diff --git a/ast/ast/ast_expr.h b/ast/ast/ast_expr.h index 90d0318bb..3c6fc7f74 100644 --- a/ast/ast/ast_expr.h +++ b/ast/ast/ast_expr.h @@ -78,6 +78,7 @@ enum TERM_KIND { TERM_KIND_VAR, TERM_KIND_STRINGCONST, TERM_KIND_CONSTVALUE, + TERM_KIND_ENUM_VALUE, }; struct Term { @@ -92,6 +93,7 @@ struct Term { struct Variable* var_term; struct StringConst* stringconst_term; struct ConstValue* constvalue_term; + char* enum_value_term; } ptr; enum TERM_KIND kind; diff --git a/ast/ast_declare.h b/ast/ast_declare.h index b1327e221..2bb5b2fd3 100644 --- a/ast/ast_declare.h +++ b/ast/ast_declare.h @@ -11,6 +11,7 @@ struct AST; struct Namespace; struct Method; struct StructDecl; +struct EnumDecl; struct MethodDecl; //--------------- struct StmtBlock; diff --git a/ast/util/free_ast.c b/ast/util/free_ast.c index 537bd9c03..9d5567d6e 100644 --- a/ast/util/free_ast.c +++ b/ast/util/free_ast.c @@ -62,6 +62,10 @@ void free_namespace(struct Namespace* ns) { free(ns->includes[i]); } + for (int i = 0; i < ns->count_enums; i++) { + free_enum_decl(ns->enums[i]); + } + for (int i = 0; i < ns->count_methods; i++) { free_method(ns->methods[i]); } @@ -74,6 +78,7 @@ void free_namespace(struct Namespace* ns) { free(ns->methods); free(ns->structs); + free(ns->enums); free(ns->src_path); free(ns->token_path); @@ -101,6 +106,15 @@ void free_stmt_block(struct StmtBlock* block) { free(block); } +void free_enum_decl(struct EnumDecl* ed) { + + for (int i = 0; i < ed->count_members; i++) { + free(ed->members[i]->name); + free(ed->members[i]); + } + free(ed); +} + void free_struct_decl(struct StructDecl* sd) { free_simple_type(sd->type); @@ -124,8 +138,9 @@ bool free_term(struct Term* t) { case TERM_KIND_VAR: free_variable(t->ptr.var_term); break; case TERM_KIND_STRINGCONST: free_string_const(t->ptr.stringconst_term); break; case TERM_KIND_CONSTVALUE: free_const_value(t->ptr.constvalue_term); break; + case TERM_KIND_ENUM_VALUE: free(t->ptr.enum_value_term); break; default: - fprintf(stderr, "Error in free_term(...)\n"); + fprintf(stderr, "Error in free_term(...), unhandled case %d\n", t->kind); free(t); return false; } diff --git a/ast/util/free_ast.h b/ast/util/free_ast.h index 73da8e65e..c3c614dc7 100644 --- a/ast/util/free_ast.h +++ b/ast/util/free_ast.h @@ -28,6 +28,9 @@ void free_namespace(struct Namespace* ns); void free_stmt_block(struct StmtBlock* block); void free_range(struct Range* range); +// enum +void free_enum_decl(struct EnumDecl* ed); + //struct void free_struct_decl(struct StructDecl* sd); void free_struct_member(struct StructMember* sm); diff --git a/ast/visitor/visitor.c b/ast/visitor/visitor.c index 2b4cd7455..03f1b2f87 100644 --- a/ast/visitor/visitor.c +++ b/ast/visitor/visitor.c @@ -53,6 +53,7 @@ static bool visit_term(struct Term* t, VISITOR, ARG); //const static void visit_const_value(struct ConstValue* cv, VISITOR, ARG); static void visit_string_const(struct StringConst* s, VISITOR, ARG); +static void visit_enum_value(char* s, VISITOR, ARG); //var // @returns false on error @@ -60,6 +61,10 @@ static bool visit_variable(struct Variable* v, VISITOR, ARG); // @returns false on error static bool visit_simple_var(struct SimpleVar* v, VISITOR, ARG); +// enum +static bool visit_enum_decl(struct EnumDecl* ed, VISITOR, ARG); +static bool visit_enum_member(struct EnumMember* em, VISITOR, ARG); + bool visit_ast(struct AST* ast, VISITOR, void* arg) { for (int i = 0; i < ast->count_namespaces; i++) { @@ -78,6 +83,10 @@ bool visit_namespace(struct Namespace* n, VISITOR, void* arg) { //we do not visit the passthrough include declarations as they //are just a workaround for now + for (int i = 0; i < n->count_enums; i++) { + visit_enum_decl(n->enums[i], visitor, arg); + } + for (int i = 0; i < n->count_structs; i++) { visit_struct_decl(n->structs[i], visitor, arg); } @@ -91,6 +100,25 @@ bool visit_namespace(struct Namespace* n, VISITOR, void* arg) { return true; } +static bool visit_enum_decl(struct EnumDecl* ed, VISITOR, ARG) { + + visitor(ed, NODE_ENUM_DECL, arg); + + for (int i = 0; i < ed->count_members; i++) { + if (!visit_enum_member(ed->members[i], visitor, arg)) { + return false; + } + } + + return true; +} + +static bool visit_enum_member(struct EnumMember* em, VISITOR, ARG) { + + visitor(em, NODE_ENUM_MEMBER, arg); + return true; +} + void visit_struct_decl(struct StructDecl* s, VISITOR, void* arg) { visitor(s, NODE_STRUCTDECL, arg); @@ -346,6 +374,9 @@ static bool visit_term(struct Term* t, VISITOR, void* arg) { case TERM_KIND_CONSTVALUE: visit_const_value(t->ptr.constvalue_term, visitor, arg); break; + case TERM_KIND_ENUM_VALUE: + visit_enum_value(t->ptr.enum_value_term, visitor, arg); + break; default: fprintf(stderr, "[Visitor][Error] Fatal(2)\n"); return false; @@ -365,6 +396,11 @@ static void visit_string_const(struct StringConst* s, VISITOR, void* arg) { visitor(s, NODE_STRINGCONST, arg); } +static void visit_enum_value(char* s, VISITOR, ARG) { + + visitor(s, NODE_ENUM_VALUE, arg); +} + static bool visit_variable(struct Variable* v, VISITOR, void* arg) { visitor(v, NODE_VARIABLE, arg); diff --git a/ast/visitor/visitor.h b/ast/visitor/visitor.h index 4f9be3a6f..6e3348997 100644 --- a/ast/visitor/visitor.h +++ b/ast/visitor/visitor.h @@ -24,6 +24,10 @@ enum NODE_TYPE { NODE_AST, NODE_NAMESPACE, + NODE_ENUM_DECL, + NODE_ENUM_MEMBER, + NODE_ENUM_VALUE, + NODE_STRUCTDECL, NODE_STRUCTMEMBER, diff --git a/compiler/main/compiler.c b/compiler/main/compiler.c index a4e5fc8f0..953427bb3 100644 --- a/compiler/main/compiler.c +++ b/compiler/main/compiler.c @@ -116,7 +116,9 @@ bool compile(struct Flags* flags) { struct Ctx* ctx = ctx_ctor(flags, st_ctor()); - fill_tables(ast, ctx); + if (!fill_tables(ast, ctx)) { + return false; + } struct TCError* errors = typecheck_ast(ast, ctx, true); diff --git a/compiler/main/gen_tac/gen_tac.c b/compiler/main/gen_tac/gen_tac.c index d1d0c99b1..b014ee03a 100644 --- a/compiler/main/gen_tac/gen_tac.c +++ b/compiler/main/gen_tac/gen_tac.c @@ -9,18 +9,6 @@ #include "util/ctx.h" #include "tac/tacbuffer.h" -int int_value_from_const(struct ConstValue* cv) { - - switch (cv->kind) { - case 1: return (int)cv->ptr.m1_bool_const; - case 2: return (int)cv->ptr.m2_int_const; - case 3: return (int)cv->ptr.m3_char_const; - case 5: return (int)cv->ptr.m5_hex_const; - case 6: return (int)cv->ptr.m5_hex_const; - } - return -1; -} - void tac_method(struct TACBuffer* buffer, struct Method* m, struct Ctx* ctx) { const uint32_t index = sst_index_of(ctx_tables(ctx)->sst, m->decl->name); diff --git a/compiler/main/gen_tac/gen_tac.h b/compiler/main/gen_tac/gen_tac.h index 5d8c70614..2774b8ab3 100644 --- a/compiler/main/gen_tac/gen_tac.h +++ b/compiler/main/gen_tac/gen_tac.h @@ -52,6 +52,3 @@ void tac_constvalue(struct TACBuffer* buffer, struct ConstValue* c); // @returns false on error bool tac_const_data(struct TACBuffer* buffer, struct StringConst* c, struct Ctx* ctx); - -//---- -int int_value_from_const(struct ConstValue* cv); diff --git a/compiler/main/gen_tac/gen_tac_constvalue.c b/compiler/main/gen_tac/gen_tac_constvalue.c index 1ae689808..d9e7b09cb 100644 --- a/compiler/main/gen_tac/gen_tac_constvalue.c +++ b/compiler/main/gen_tac/gen_tac_constvalue.c @@ -3,6 +3,7 @@ #include "tac/tac.h" #include "tac/tacbuffer.h" #include "gen_tac.h" +#include "tables/enum/enum_table.h" void tac_constvalue(struct TACBuffer* buffer, struct ConstValue* c) { diff --git a/compiler/main/gen_tac/gen_tac_term.c b/compiler/main/gen_tac/gen_tac_term.c index 345023db5..5eb795467 100644 --- a/compiler/main/gen_tac/gen_tac_term.c +++ b/compiler/main/gen_tac/gen_tac_term.c @@ -1,17 +1,49 @@ #include +#include #include "tac/tacbuffer.h" +#include "tables/symtable/symtable.h" +#include "tables/enum/enum_table.h" #include "gen_tac.h" +static bool tac_term_enum_value(struct TACBuffer* buffer, struct Term* t, struct Ctx* ctx) { + + const struct ST* st = ctx_tables(ctx); + assert(st); + + char* name = t->ptr.enum_value_term; + assert(name); + + const int64_t value = enum_table_lookup(st->enum_table, name); + + if (value < 0) { + fprintf(stderr, "could not find value for '%s' in enum table\n", name); + + enum_table_print(st->enum_table); + return false; + } + + tacbuffer_append(buffer, makeTACConst(make_temp(), value)); + + return true; +} + bool tac_term(struct TACBuffer* buffer, struct Term* t, struct Ctx* ctx) { + assert(buffer); + assert(t); + assert(ctx); + switch (t->kind) { case TERM_KIND_CALL: tac_call(buffer, t->ptr.call_term, ctx); break; case TERM_KIND_EXPR: tac_expr(buffer, t->ptr.expr_term, ctx); break; case TERM_KIND_VAR: tac_variable(buffer, t->ptr.var_term, ctx); break; case TERM_KIND_STRINGCONST: return tac_const_data(buffer, t->ptr.stringconst_term, ctx); case TERM_KIND_CONSTVALUE: tac_constvalue(buffer, t->ptr.constvalue_term); break; + case TERM_KIND_ENUM_VALUE: + return tac_term_enum_value(buffer, t, ctx); + break; default: fprintf(stderr, "%s: unsupported: %d\n", __func__, t->kind); return false; diff --git a/compiler/main/typechecker/tc_term.c b/compiler/main/typechecker/tc_term.c index a385a0bec..b85d8619c 100644 --- a/compiler/main/typechecker/tc_term.c +++ b/compiler/main/typechecker/tc_term.c @@ -34,6 +34,7 @@ bool tc_term(struct Term* term, struct TCCtx* tcctx) { case TERM_KIND_STRINGCONST: return true; case TERM_KIND_CONSTVALUE: return tc_constvalue(term->ptr.constvalue_term); + case TERM_KIND_ENUM_VALUE: return true; default: fprintf(stderr, "%s:%s: unhandled case: %d\n", __FILE__, __func__, term->kind); diff --git a/compiler/main/typeinference/typeinfer_term.c b/compiler/main/typeinference/typeinfer_term.c index 5f15fe1aa..b5fd6f021 100644 --- a/compiler/main/typeinference/typeinfer_term.c +++ b/compiler/main/typeinference/typeinfer_term.c @@ -34,6 +34,7 @@ struct Type* infer_type_term(struct ST* st, struct Term* t) { case TERM_KIND_VAR: return infer_type_variable(st, t->ptr.var_term); case TERM_KIND_STRINGCONST: return typeFromStrArray(st, "char"); case TERM_KIND_CONSTVALUE: return infer_type_constvalue(st, t->ptr.constvalue_term); + case TERM_KIND_ENUM_VALUE: return typeFromStrPrimitive(st, "uint64"); default: fprintf(stderr, "[Typeinference][Error] Fatal. (in typeinfer_term.c).\n"); diff --git a/compiler/main/util/fill_tables.c b/compiler/main/util/fill_tables.c index 69945ba52..d40df0580 100644 --- a/compiler/main/util/fill_tables.c +++ b/compiler/main/util/fill_tables.c @@ -4,15 +4,18 @@ #include #include #include "tables/symtable/symtable.h" +#include #include "compiler/main/util/ctx.h" #include "cli/flags/flags.h" #include "fill_tables.h" -void fill_tables(struct AST* ast, struct Ctx* ctx) { +bool fill_tables(struct AST* ast, struct Ctx* ctx) { - if (flags_debug(ctx_flags(ctx))) { + const bool debug = flags_debug(ctx_flags(ctx)); + + if (debug) { printf("[debug] filling SST, STST tables...\n"); } @@ -24,10 +27,16 @@ void fill_tables(struct AST* ast, struct Ctx* ctx) { sst_fill(ctx_tables(ctx), ctx_tables(ctx)->sst, ns); stst_fill(ctx_tables(ctx)->stst, ns); + + if (!enum_table_fill(ctx_tables(ctx)->enum_table, ns, debug)) { + return false; + } } if (flags_debug(ctx_flags(ctx))) { sst_print(ctx_tables(ctx)->sst); stst_print(ctx_tables(ctx)->stst); } + + return true; } diff --git a/compiler/main/util/fill_tables.h b/compiler/main/util/fill_tables.h index b76b9c545..a05e9a8e6 100644 --- a/compiler/main/util/fill_tables.h +++ b/compiler/main/util/fill_tables.h @@ -1,6 +1,8 @@ #pragma once +#include + struct AST; struct Ctx; -void fill_tables(struct AST* ast, struct Ctx* ctx); +bool fill_tables(struct AST* ast, struct Ctx* ctx); diff --git a/compiler/test/gen_tac/test_gen_tac_assignstmt.c b/compiler/test/gen_tac/test_gen_tac_assignstmt.c index 5c4dc5964..3c13238b3 100644 --- a/compiler/test/gen_tac/test_gen_tac_assignstmt.c +++ b/compiler/test/gen_tac/test_gen_tac_assignstmt.c @@ -54,7 +54,7 @@ void test_gen_tac_assignstmt_case_local_struct() { const int8_t value = 0x23; char snippet[200]; - sprintf(snippet, "struct A {int x;} fn main() -> int { local A m; m.x = %d; return m.x; }", value); + sprintf(snippet, "struct Astruct {int x;} fn main() -> int { local Astruct m; m.x = %d; return m.x; }", value); vmcu_system_t* system = prepare_vmcu_system_from_code_snippet(snippet); diff --git a/compiler/test/gen_tac/test_gen_tac_structdecl.c b/compiler/test/gen_tac/test_gen_tac_structdecl.c index d6624e339..f61fbea9d 100644 --- a/compiler/test/gen_tac/test_gen_tac_structdecl.c +++ b/compiler/test/gen_tac/test_gen_tac_structdecl.c @@ -12,7 +12,7 @@ void test_gen_tac_structdecl_case_read_struct() { const uint8_t value = 0xc7; char snippet[200]; - char* template = "struct A{int8 c; int8 a; int8 b;} fn main() -> int { local A x; x.b = %d; return x.b; }"; + char* template = "struct Astruct {int8 c; int8 a; int8 b;} fn main() -> int { local Astruct x; x.b = %d; return x.b; }"; sprintf(snippet, template, value); vmcu_system_t* system = prepare_vmcu_system_from_code_snippet(snippet); @@ -35,7 +35,7 @@ void test_gen_tac_structdecl_case_write_struct() { const uint8_t value = 0xc4; char snippet[200]; - char* template = "struct A{int8 a; int8 b;} fn main() -> int { local A x; x.b = %d; return x.b; }"; + char* template = "struct Astruct {int8 a; int8 b;} fn main() -> int { local Astruct x; x.b = %d; return x.b; }"; sprintf(snippet, template, value); vmcu_system_t* system = prepare_vmcu_system_from_code_snippet(snippet); diff --git a/compiler/test/gen_tac/test_gen_tac_variable.c b/compiler/test/gen_tac/test_gen_tac_variable.c index d5e11f438..aa372d630 100644 --- a/compiler/test/gen_tac/test_gen_tac_variable.c +++ b/compiler/test/gen_tac/test_gen_tac_variable.c @@ -34,7 +34,7 @@ void test_gen_tac_variable_case_1_member_access() { for (int8_t value = 0; value < 10; value++) { char snippet[250]; - char* template = "struct A {int8 a; int8 b;}fn main() -> int { local A m; m.b = %d; return m.b; }"; + char* template = "struct Astruct {int8 a; int8 b;}fn main() -> int { local Astruct m; m.b = %d; return m.b; }"; sprintf(snippet, template, value); vmcu_system_t* system = prepare_vmcu_system_from_code_snippet(snippet); diff --git a/compiler/test/typeinference/test-src/infer_type_call_with_struct_member_access.dg b/compiler/test/typeinference/test-src/infer_type_call_with_struct_member_access.dg index 3730bc187..dfee86efe 100644 --- a/compiler/test/typeinference/test-src/infer_type_call_with_struct_member_access.dg +++ b/compiler/test/typeinference/test-src/infer_type_call_with_struct_member_access.dg @@ -1,11 +1,11 @@ -struct A { +struct Astruct { (()->int) x; } fn sub()~>int { - - A b = get(); + + Astruct b = get(); return b.x(); } diff --git a/docs/html/grammar.html b/docs/html/grammar.html index 954136fd1..b533b4e7d 100644 --- a/docs/html/grammar.html +++ b/docs/html/grammar.html @@ -35,10 +35,21 @@

Grammar

AST ::= Namespace+

- Namespace ::= StructDecl* Method* + Namespace ::= EnumDecl* StructDecl* Method*

+

Enum

+ + EnumDecl ::= "enum" uppercase+ '{' EnumMember+ '}' +

+ + EnumMember ::= EnumValue '=' ConstValue ';' +

+ + EnumValue ::= uppercase+ +

+

Struct

StructDecl ::= "struct" StructType '{' StructMember* '}' @@ -146,7 +157,7 @@

Expressions

Deref ::= '*' Term

- Term ::= ConstValue | StringConst | Call | '(' Expr ')' | Variable + Term ::= ConstValue | StringConst | Call | '(' Expr ')' | Variable | EnumValue

Range ::= Expr ".." Expr diff --git a/examples/enum/enum_hex_value/enum_hex_value.dg b/examples/enum/enum_hex_value/enum_hex_value.dg new file mode 100644 index 000000000..1a6f02bd4 --- /dev/null +++ b/examples/enum/enum_hex_value/enum_hex_value.dg @@ -0,0 +1,11 @@ + +enum VALUES { + VALUE_1 = 0x1; + VALUE_6 = 0x6; + VALUE_9 = 0x9; +} + +fn main () -> int { + + return VALUE_9; +} diff --git a/examples/enum/enum_hex_value/enum_hex_value.exitcode b/examples/enum/enum_hex_value/enum_hex_value.exitcode new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/examples/enum/enum_hex_value/enum_hex_value.exitcode @@ -0,0 +1 @@ +9 diff --git a/examples/enum/simple_enum/simple_enum.dg b/examples/enum/simple_enum/simple_enum.dg new file mode 100644 index 000000000..91765ce62 --- /dev/null +++ b/examples/enum/simple_enum/simple_enum.dg @@ -0,0 +1,11 @@ + +enum VALUES { + VALUE_1 = 1; + VALUE_28 = 28; + VALUE_29 = 29; +} + +fn main () -> int { + + return VALUE_28; +} diff --git a/examples/enum/simple_enum/simple_enum.exitcode b/examples/enum/simple_enum/simple_enum.exitcode new file mode 100644 index 000000000..9902f1784 --- /dev/null +++ b/examples/enum/simple_enum/simple_enum.exitcode @@ -0,0 +1 @@ +28 diff --git a/examples/struct/nested/nested.dg b/examples/struct/nested/nested.dg index 1822d2c50..c70eb3aef 100644 --- a/examples/struct/nested/nested.dg +++ b/examples/struct/nested/nested.dg @@ -1,16 +1,16 @@ -struct A { +struct Astruct { uint8 x; - B b; + Bstruct b; } -struct B { +struct Bstruct { uint8 y; } fn main () ~> int { - local A m; + local Astruct m; m.b.y = 3; diff --git a/examples/struct/very_nested/very_nested.dg b/examples/struct/very_nested/very_nested.dg index f25f732e7..5e82c3af1 100644 --- a/examples/struct/very_nested/very_nested.dg +++ b/examples/struct/very_nested/very_nested.dg @@ -1,15 +1,15 @@ -struct A { +struct As { uint8 x; - B b; + Bs b; } -struct B { - C c; +struct Bs { + Cs c; uint8 y; } -struct C { +struct Cs { uint16 z1; uint16 z2; uint16 z3; @@ -18,7 +18,7 @@ struct C { fn main () ~> int { - local A a; + local As a; a.b.y = 33; diff --git a/lexer/src/lexer.c b/lexer/src/lexer.c index 250a3797b..e6cec770c 100644 --- a/lexer/src/lexer.c +++ b/lexer/src/lexer.c @@ -333,6 +333,7 @@ static int handler2(const char* buf, struct TokenList* o, size_t nchars_remain) H2OUT_NOSTR(buf, "in", KEYWORD_IN, o); H2OUT_NOSTR(buf, "break", KEYWORD_BREAK, o); H2OUT_NOSTR(buf, "continue", KEYWORD_CONTINUE, o); + H2OUT_NOSTR(buf, "enum", KEYWORD_ENUM, o); if (strncmp(buf, "0x", 2) == 0) { return handler2_hexconst(buf, o, nchars_remain); @@ -350,7 +351,19 @@ static int handler2(const char* buf, struct TokenList* o, size_t nchars_remain) out_length(o, ID, (char*)buf, index); return index; } else if (isupper(buf[0])) { - out_length(o, TYPEID, (char*)buf, index); + + bool all_upper = true; + for (int i = 0; i < index; i++) { + if (islower(buf[i])) { + all_upper = false; + } + } + + if (all_upper) { + out_length(o, TOKEN_ENUM_VALUE, (char*)buf, index); + } else { + out_length(o, TYPEID, (char*)buf, index); + } return index; } } diff --git a/parser/main/CMakeLists.txt b/parser/main/CMakeLists.txt index 5daad52cc..cc3b94acd 100644 --- a/parser/main/CMakeLists.txt +++ b/parser/main/CMakeLists.txt @@ -35,6 +35,9 @@ add_library(dragon-parser-base astnodes/struct/StructDecl.c astnodes/struct/StructMember.c + astnodes/EnumDecl.c + astnodes/EnumMember.c + astnodes/subr/DeclArg.c astnodes/subr/MethodDecl.c astnodes/subr/Method.c diff --git a/parser/main/astnodes/EnumDecl.c b/parser/main/astnodes/EnumDecl.c new file mode 100644 index 000000000..5e67d0e23 --- /dev/null +++ b/parser/main/astnodes/EnumDecl.c @@ -0,0 +1,114 @@ +#include +#include +#include + +#include "parser/main/util/parse_astnode.h" + +#include "EnumDecl.h" +#include "EnumMember.h" +#include "types/SimpleType.h" +#include "types/StructType.h" + +#include "ast/util/free_ast.h" + +#include "token/list/TokenList.h" +#include "token/TokenKeys.h" +#include "token/token/token.h" + +struct EnumDecl* makeEnumDecl(struct TokenList* tokens) { + + if (list_size(tokens) < 2) { + return NULL; + } + + struct EnumDecl* res = make(EnumDecl); + if (!res) { + goto exit_error_alloc_res; + } + + struct TokenList* copy = list_copy(tokens); + + parse_astnode(copy, &(res->super)); + + struct Token* next = list_head(copy); + + if (next->kind != KEYWORD_ENUM) { + fprintf(stderr, "parsing error, expected 'enum' , but got: %s\n", list_code(copy)); + goto exit_error_tokenlist; + } + list_consume(copy, 1); + + res->members = malloc(sizeof(struct EnumMember*) * 1); + + if (!res->members) { + goto exit_error_tokenlist; + } + + res->count_members = 0; + + //read the enum name + if (list_head(copy)->kind != TOKEN_ENUM_VALUE) { + + fprintf(stderr, "parsing error, expected enum value (all uppercase) , but got: %s\n", list_code(copy)); + goto exit_error_members; + } + res->name = strdup(list_head(copy)->value_ptr); + list_consume(copy, 1); + + next = list_head(copy); + if (next->kind != LCURLY) { + fprintf(stderr, "parsing error, expected '{' , but got: %s\n", list_code(copy)); + goto exit_error; + } + list_consume(copy, 1); + + struct EnumMember* member; + + member = makeEnumMember(copy); + while (member != NULL) { + + res->members[res->count_members] = member; + res->count_members++; + + struct EnumMember** new_members = realloc( + res->members, + sizeof(struct EnumMember*) * (res->count_members + 1)); + + if (!new_members) { + goto exit_error; + } + + res->members = new_members; + + if (list_head(copy)->kind != TOKEN_ENUM_VALUE) { + break; + } + + member = makeEnumMember(copy); + } + + next = list_head(copy); + if (next->kind != RCURLY) { + fprintf(stderr, "parsing error, expected '}' , but got: %s\n", list_code(copy)); + goto exit_error; + } + list_consume(copy, 1); + + list_set(tokens, copy); + freeTokenListShallow(copy); + + return res; + +exit_error: + for (size_t i = 0; i < res->count_members; i++) { + free(res->members[i]->name); + free(res->members[i]); + } +exit_error_members: + free(res->members); +exit_error_tokenlist: + freeTokenListShallow(copy); +exit_error_alloc_res: + free(res); + return NULL; +} diff --git a/parser/main/astnodes/EnumDecl.h b/parser/main/astnodes/EnumDecl.h new file mode 100644 index 000000000..8e952b80e --- /dev/null +++ b/parser/main/astnodes/EnumDecl.h @@ -0,0 +1,6 @@ +#pragma once + +struct TokenList; +struct EnumDecl; + +struct EnumDecl* makeEnumDecl(struct TokenList* tokens); diff --git a/parser/main/astnodes/EnumMember.c b/parser/main/astnodes/EnumMember.c new file mode 100644 index 000000000..d1e2c3193 --- /dev/null +++ b/parser/main/astnodes/EnumMember.c @@ -0,0 +1,65 @@ +#include +#include +#include + +#include "parser/main/util/parse_astnode.h" + +#include "EnumMember.h" +#include "Identifier.h" +#include "types/Type.h" +#include "types/StructType.h" +#include "const/ConstValue.h" + +#include "ast/util/free_ast.h" + +#include "token/list/TokenList.h" +#include "token/TokenKeys.h" +#include "token/token/token.h" + +struct EnumMember* makeEnumMember(struct TokenList* tokens) { + + struct EnumMember* res = make(EnumMember); + struct TokenList* copy = list_copy(tokens); + + parse_astnode(copy, &(res->super)); + + if (list_head(copy)->kind != TOKEN_ENUM_VALUE) { + + fprintf(stderr, "expected enum value (all uppercase)\n"); + freeTokenListShallow(copy); + free(res); + return NULL; + } + asprintf(&(res->name), "%s", list_head(copy)->value_ptr); + list_consume(copy, 1); + + struct Token* next = list_head(copy); + if (next->kind != ASSIGNOP_SIMPLE) { + fprintf(stderr, "expected '='\n"); + freeTokenListShallow(copy); + free(res); + return NULL; + } + list_consume(copy, 1); + + res->value = makeConstValue(copy); + + if (!res->value) { + freeTokenListShallow(copy); + free(res); + return NULL; + } + + next = list_head(copy); + if (next->kind != SEMICOLON) { + freeTokenListShallow(copy); + free(res); + return NULL; + } + list_consume(copy, 1); + + list_set(tokens, copy); + freeTokenListShallow(copy); + + return res; +} diff --git a/parser/main/astnodes/EnumMember.h b/parser/main/astnodes/EnumMember.h new file mode 100644 index 000000000..fb4aea29c --- /dev/null +++ b/parser/main/astnodes/EnumMember.h @@ -0,0 +1,6 @@ +#pragma once + +struct TokenList; +struct EnumMember; + +struct EnumMember* makeEnumMember(struct TokenList* tokens); diff --git a/parser/main/astnodes/Namespace.c b/parser/main/astnodes/Namespace.c index 93f7200e8..d0da39670 100644 --- a/parser/main/astnodes/Namespace.c +++ b/parser/main/astnodes/Namespace.c @@ -5,6 +5,7 @@ #include "Namespace.h" #include "subr/Method.h" #include "struct/StructDecl.h" +#include "EnumDecl.h" #include "ast/util/free_ast.h" @@ -16,6 +17,8 @@ static bool ns_parse_methods(struct Namespace* res, struct TokenList* copy); // @returns false on error static bool ns_parse_structs(struct Namespace* res, struct TokenList* copy); +// @returns false on error +static bool ns_parse_enums(struct Namespace* res, struct TokenList* copy); // @returns false on error bool ns_parse_passthrough_includes(struct Namespace* p_namespace, struct TokenList* p_list); @@ -33,10 +36,12 @@ struct Namespace* makeNamespace(struct TokenList* tokens, char* name) { res->count_methods = 0; res->count_structs = 0; + res->count_enums = 0; const uint16_t INITIAL_CAPACITY = 5; res->capacity_methods = INITIAL_CAPACITY; res->capacity_structs = INITIAL_CAPACITY; + res->capacity_enums = INITIAL_CAPACITY; res->methods = malloc(sizeof(struct Method*) * res->capacity_methods); if (!res->methods) { @@ -46,6 +51,10 @@ struct Namespace* makeNamespace(struct TokenList* tokens, char* name) { if (!res->structs) { goto error_res_methods; } + res->enums = malloc(sizeof(struct EnumDecl*) * res->capacity_enums); + if (!res->enums) { + goto error_res_enums; + } res->src_path = malloc(sizeof(char) * (strlen(name) + 3 + 1)); if (!res->src_path) { goto error_res_structs; @@ -66,6 +75,10 @@ struct Namespace* makeNamespace(struct TokenList* tokens, char* name) { goto error_res_token_path; } + if (!ns_parse_enums(res, copy)) { + goto error_res_parse_enums; + } + if (!ns_parse_structs(res, copy)) { goto error_res_parse_structs; } @@ -78,17 +91,25 @@ struct Namespace* makeNamespace(struct TokenList* tokens, char* name) { freeTokenListShallow(copy); return res; + error_res_parse_structs: for (int i = 0; i < res->count_includes; i++) { free(res->includes[i]); } free(res->includes); +error_res_parse_enums: + for (int i = 0; i < res->count_enums; i++) { + free(res->enums[i]->name); + free(res->enums[i]); + } error_res_token_path: free(res->token_path); error_res_src_path: free(res->src_path); error_res_name: free(res->name); +error_res_enums: + free(res->enums); error_res_structs: free(res->structs); error_res_methods: @@ -99,6 +120,34 @@ struct Namespace* makeNamespace(struct TokenList* tokens, char* name) { return NULL; } +static bool ns_parse_enums(struct Namespace* res, struct TokenList* copy) { + + struct Token* next = list_head(copy); + + while (next->kind == KEYWORD_ENUM) { + + if ((res->count_enums + 1) >= res->capacity_enums) { + res->capacity_enums *= 2; + struct EnumDecl** new_enums = realloc(res->enums, sizeof(struct EnumDecl*) * res->capacity_enums); + + if (!new_enums) { + return false; + } + + res->enums = new_enums; + } + + res->enums[res->count_enums] = makeEnumDecl(copy); + if (!res->enums[res->count_enums]) { + return false; + } + res->count_enums++; + next = list_head(copy); + } + + return true; +} + bool ns_parse_passthrough_includes(struct Namespace* res, struct TokenList* copy) { if (list_size(copy) == 0) { return true; } diff --git a/parser/main/astnodes/expr/Term.c b/parser/main/astnodes/expr/Term.c index 900cd6cbb..795340bc2 100644 --- a/parser/main/astnodes/expr/Term.c +++ b/parser/main/astnodes/expr/Term.c @@ -65,6 +65,11 @@ struct Term* makeTerm(struct TokenList* tokens) { return NULL; } + } else if (tk_kind == TOKEN_ENUM_VALUE) { + res->kind = TERM_KIND_ENUM_VALUE; + res->ptr.enum_value_term = strdup(list_head(copy)->value_ptr); + list_consume(copy, 1); + } else { goto other_term; } diff --git a/tables/CMakeLists.txt b/tables/CMakeLists.txt index f5f04076e..1d1b95d5a 100644 --- a/tables/CMakeLists.txt +++ b/tables/CMakeLists.txt @@ -14,6 +14,9 @@ add_library("sd-tables" STATIC data/data.c + enum/enum_table.c + enum/enum_table.h + symtable/symtable.c ) diff --git a/tables/enum/enum_table.c b/tables/enum/enum_table.c new file mode 100644 index 000000000..e56d71575 --- /dev/null +++ b/tables/enum/enum_table.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "enum_table.h" + +struct EnumTable { + + struct EnumTableEntry** entries; + size_t count_entries; + size_t capacity; +}; + +struct EnumTable* enum_table_ctor() { + + struct EnumTable* res = calloc(1, sizeof(struct EnumTable)); + + if (!res) { + return NULL; + } + + res->count_entries = 0; + res->capacity = 10; + + res->entries = calloc(res->capacity, sizeof(struct EnumTableEntry*)); + + if (!res->entries) { + free(res); + return NULL; + } + + return res; +} + +void enum_table_dtor(struct EnumTable* data) { + + for (size_t i = 0; i < data->count_entries; i++) { + struct EnumTableEntry* entry = data->entries[i]; + + free(entry->name); + free(entry); + } + free(data->entries); + free(data); +} + +static bool enum_table_resize(struct EnumTable* data) { + + assert(data); + + if ((data->count_entries + 1) >= data->capacity) { + + data->capacity *= 2; + struct EnumTableEntry** new_entries = realloc(data->entries, data->capacity * sizeof(struct EnumTableEntry*)); + + if (!new_entries) { + return false; + } + + data->entries = new_entries; + + assert(data->entries); + } + + return true; +} + +bool enum_table_insert(struct EnumTable* et, char* name, uint64_t value) { + + if (!name) { + return false; + } + + if (enum_table_lookup(et, name) >= 0) { + fprintf(stderr, "Error: enum value %s was already registered\n", name); + return false; + } + + const size_t len = strlen(name); + + // enum members are all uppercase + for (size_t i = 0; i < len; i++) { + if (islower(name[i])) { + return false; + } + } + + if (!enum_table_resize(et)) { + return false; + } + + struct EnumTableEntry* entry = malloc(sizeof(struct EnumTableEntry)); + + if (entry == NULL) { + return false; + } + + entry->value = value; + entry->name = strdup(name); + + if (entry->name == NULL) { + free(entry); + return false; + } + + et->entries[et->count_entries++] = entry; + + return true; +} + +int64_t enum_table_lookup(struct EnumTable* et, char* name) { + + assert(et); + assert(name); + + for (size_t i = 0; i < et->count_entries; i++) { + struct EnumTableEntry* entry = et->entries[i]; + + assert(entry->name); + + if (strcmp(entry->name, name) == 0) { + return entry->value; + } + } + + return -1; +} + +int int_value_from_const(struct ConstValue* cv) { + + switch (cv->kind) { + case 1: return (int)cv->ptr.m1_bool_const; + case 2: return (int)cv->ptr.m2_int_const; + case 3: return (int)cv->ptr.m3_char_const; + case 5: return (int)cv->ptr.m5_hex_const; + case 6: return (int)cv->ptr.m5_hex_const; + } + return -1; +} + +static bool enum_table_fill_single(struct EnumTable* enum_table, struct EnumDecl* ed) { + + for (size_t j = 0; j < ed->count_members; j++) { + + struct EnumMember* em = ed->members[j]; + + struct ConstValue* cv = em->value; + + const int value = int_value_from_const(cv); + + if (!enum_table_insert(enum_table, em->name, value)) { + return false; + } + } + + return true; +} + +bool enum_table_fill(struct EnumTable* enum_table, struct Namespace* ns, bool debug) { + + if (debug) { + printf("[debug] filling enum tables\n"); + } + + for (size_t i = 0; i < ns->count_enums; i++) { + + struct EnumDecl* ed = ns->enums[i]; + + if (debug) { + printf("[debug] enum %s\n", ed->name); + } + + if (!enum_table_fill_single(enum_table, ed)) { + return false; + } + } + + if (debug) { + enum_table_print(enum_table); + } + + return true; +} + +void enum_table_print(struct EnumTable* enum_table) { + + printf("Enum Table:\n"); + + for (size_t i = 0; i < enum_table->count_entries; i++) { + struct EnumTableEntry* entry = enum_table->entries[i]; + + printf("%-20s = %10ld\n", entry->name, entry->value); + } + + printf("------------------\n"); +} diff --git a/tables/enum/enum_table.h b/tables/enum/enum_table.h new file mode 100644 index 000000000..58f2a7f78 --- /dev/null +++ b/tables/enum/enum_table.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include "ast/ast_declare.h" +#include "tables/symtable/symtable.h" + +struct EnumTable; + +struct EnumTableEntry { + + // needs to be freed + char* name; + + uint64_t value; +}; + +struct EnumTable* enum_table_ctor(); +void enum_table_dtor(struct EnumTable* data); + +// @brief inserts a new entry into enum value table +// @param str the name to enter +// @param value the value for that enum value name +bool enum_table_insert(struct EnumTable* data, char* str, uint64_t value); + +// @returns -1 if entry is not found +// @returns integer value >= for the enum value name +int64_t enum_table_lookup(struct EnumTable* data, char* name); + +bool enum_table_fill(struct EnumTable* enum_table, struct Namespace* ns, bool debug); + +void enum_table_print(struct EnumTable* enum_table); + +int int_value_from_const(struct ConstValue* cv); diff --git a/tables/symtable/symtable.c b/tables/symtable/symtable.c index 7c1d24227..2f0813c56 100644 --- a/tables/symtable/symtable.c +++ b/tables/symtable/symtable.c @@ -7,6 +7,7 @@ #include "tables/sst/sst.h" #include "tables/stst/stst.h" #include "tables/data/data.h" +#include "tables/enum/enum_table.h" #include "symtable.h" struct ST* st_ctor() { @@ -33,6 +34,7 @@ struct ST* st_ctor() { st->lvst = lvst_ctor(); st->data = data_ctor(); + st->enum_table = enum_table_ctor(); return st; } @@ -43,6 +45,7 @@ void st_free(struct ST* st) { if (st->lvst != NULL) { lvst_free(st->lvst); } if (st->stst != NULL) { stst_free(st->stst); } data_dtor(st->data); + enum_table_dtor(st->enum_table); for (int i = 0; i < st->inferred_types_count; i++) { free_type(st->inferred_types[i]); diff --git a/tables/symtable/symtable.h b/tables/symtable/symtable.h index e728b6961..813434078 100644 --- a/tables/symtable/symtable.h +++ b/tables/symtable/symtable.h @@ -6,6 +6,7 @@ #include "tables/sst/sst.h" #include "tables/stst/stst.h" #include "tables/data/data.h" +#include "tables/enum/enum_table.h" struct ST { //struct SymTable @@ -22,6 +23,8 @@ struct ST { // things which should be in .data segment struct DataTable* data; + struct EnumTable* enum_table; + // ----------------------------- //all the type nodes that were additionally diff --git a/token/TokenKeys.h b/token/TokenKeys.h index 777581efd..a7a6e08ec 100644 --- a/token/TokenKeys.h +++ b/token/TokenKeys.h @@ -1,15 +1,16 @@ #pragma once //Special -#define EMPTY 1 -#define LINE_NO 2 +#define EMPTY 0 +#define LINE_NO 1 //Type Parameter Token -#define TPARAM 3 +#define TPARAM 2 //Identifier -#define ID 4 +#define ID 3 +#define TOKEN_ENUM_VALUE 4 #define TYPEID 5 #define TYPEID_PRIMITIVE_BOOL 7 #define TYPEID_PRIMITIVE_CHAR 8 @@ -96,6 +97,8 @@ // so we prefix with 'KEYWORD_' here #define KEYWORD_IN 62 +#define KEYWORD_ENUM 63 + #define INCLUDE_DECL 70 //operator groups