aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/language.md33
-rw-r--r--examples/records.hdass42
-rwxr-xr-xscripts/test_examples.sh1
-rw-r--r--src/codegen/nasm.c83
-rw-r--r--src/lexer/lexer.c4
-rw-r--r--src/lexer/lexer.h2
-rw-r--r--src/parser/ast.c90
-rw-r--r--src/parser/ast.h54
-rw-r--r--src/parser/parser.c96
-rw-r--r--src/sema/sema.c76
-rw-r--r--tests/codegen_test.c28
-rw-r--r--tests/lexer_test.c8
-rw-r--r--tests/parser_test.c22
-rw-r--r--tests/sema_test.c12
14 files changed, 536 insertions, 15 deletions
diff --git a/docs/language.md b/docs/language.md
index 43b6246..5e75c54 100644
--- a/docs/language.md
+++ b/docs/language.md
@@ -59,6 +59,39 @@ const AREA = 8 * 6 // 48
data message = "type shi.\n" // message -> address, message.len -> 10
```
+## Enums and structs
+
+Both describe compile-time values reached with `Name.member`, which folds to an integer.
+
+`enum` names a set of constants numbered from 0:
+
+```hdass
+enum Status
+{
+ Ok, // 0
+ Warn, // 1
+ Fail // 2
+}
+
+rax = Status.Fail // mov rax, 2
+```
+
+`struct` describes a packed memory layout (no padding). Fields are `name` or `name: size`, where size defaults to `qword`. `Name.field` is the field's byte offset, and `Name.size` is the total size.
+
+```hdass
+struct Point
+{
+ x // qword, offset 0
+ y // qword, offset 8
+ flag: byte // offset 16
+}
+
+rsi += Point.y // add rsi, 8
+rax = Point.size // mov rax, 17
+```
+
+A struct is layout only — it allocates nothing. Pair it with a `stack` buffer and pointer arithmetic (see [examples/records.hdass](../examples/records.hdass)).
+
## Procedures
`proc` groups a body. Parameters name registers — `value` below is `rdi`.
diff --git a/examples/records.hdass b/examples/records.hdass
new file mode 100644
index 0000000..9800839
--- /dev/null
+++ b/examples/records.hdass
@@ -0,0 +1,42 @@
+[entry: main]
+
+// An enum of status codes and a struct describing a memory layout. Stores two
+// fields into a stack instance, reads them back, and exits with their sum.
+enum Status
+{
+ Ok,
+ Warn,
+ Fail
+}
+
+struct Pair
+{
+ a: qword
+ b: qword
+}
+
+const SYS_EXIT = 60
+
+proc main
+{
+ stack pair[16] // Pair.size
+
+ rsi = pair
+ rbx = 40
+ ^rsi = rbx // pair.a (offset 0)
+
+ rsi = pair
+ rsi += Pair.b
+ rcx = Status.Fail // 2
+ ^rsi = rcx // pair.b (offset 8)
+
+ rsi = pair
+ rdi = ^rsi // load a = 40
+ rsi = pair
+ rsi += Pair.b
+ rax = ^rsi // load b = 2
+ rdi += rax // 42
+
+ rax = SYS_EXIT
+ syscall
+}
diff --git a/scripts/test_examples.sh b/scripts/test_examples.sh
index 3e04fde..0928f7c 100755
--- a/scripts/test_examples.sh
+++ b/scripts/test_examples.sh
@@ -84,6 +84,7 @@ check logical "logical register names (r1..r14)" examples/logical.hdass
check mul_div "multiply and non-rax division" examples/mul_div.hdass 8 ""
check load "stores then loads through a pointer" examples/load.hdass 7 ""
check constants "hex literals and constant folding" examples/constants.hdass 42 ""
+check records "enum values and struct field offsets" examples/records.hdass 42 ""
check fibonacci "prints the first ten Fibonacci numbers" examples/fibonacci.hdass 0 "0
1
1
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c
index fef68a8..9c1a178 100644
--- a/src/codegen/nasm.c
+++ b/src/codegen/nasm.c
@@ -209,6 +209,84 @@ static enum StoreSize size_from_int(struct Token token)
}
}
+static bool tokens_equal(struct Token a, struct Token b)
+{
+ return a.length == b.length && memcmp(a.start, b.start, a.length) == 0;
+}
+
+static bool token_matches(struct Token token, const char* text)
+{
+ size_t length = strlen(text);
+ return token.length == length && memcmp(token.start, text, length) == 0;
+}
+
+static struct EnumDecl* find_enum(struct Program* program, struct Token name)
+{
+ for (size_t i = 0; i < program->enum_count; i += 1)
+ if (tokens_equal(program->enums[i].name, name))
+ return &program->enums[i];
+
+ return NULL;
+}
+
+static struct StructDecl* find_struct(struct Program* program, struct Token name)
+{
+ for (size_t i = 0; i < program->struct_count; i += 1)
+ if (tokens_equal(program->structs[i].name, name))
+ return &program->structs[i];
+
+ return NULL;
+}
+
+static uint64_t store_size_bytes(enum StoreSize size)
+{
+ switch (size)
+ {
+ case STORE_SIZE_BYTE: return 1;
+ case STORE_SIZE_WORD: return 2;
+ case STORE_SIZE_DWORD: return 4;
+ default: return 8;
+ }
+}
+
+// an enum member folds to its 0-based index; a struct member folds to its byte
+// offset (or the total size for `.size`)
+static bool emit_named_member(struct Emitter* emitter, struct Token object, struct Token member)
+{
+ struct EnumDecl* enumeration = find_enum(emitter->program, object);
+ if (enumeration != NULL)
+ {
+ for (size_t i = 0; i < enumeration->member_count; i += 1)
+ if (tokens_equal(enumeration->members[i], member))
+ {
+ fprintf(emitter->out, "%zu", i);
+ return true;
+ }
+ }
+
+ struct StructDecl* layout = find_struct(emitter->program, object);
+ if (layout != NULL)
+ {
+ uint64_t offset = 0;
+ for (size_t i = 0; i < layout->field_count; i += 1)
+ {
+ if (tokens_equal(layout->fields[i].name, member))
+ {
+ fprintf(emitter->out, "%llu", (unsigned long long)offset);
+ return true;
+ }
+ offset += store_size_bytes(layout->fields[i].size);
+ }
+ if (token_matches(member, "size"))
+ {
+ fprintf(emitter->out, "%llu", (unsigned long long)offset);
+ return true;
+ }
+ }
+
+ return false;
+}
+
static bool emit_operand(struct Emitter* emitter, struct Expr* expr)
{
switch (expr->kind)
@@ -235,6 +313,11 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr)
return true;
}
+ // enum value or struct offset
+ if (expr->member.object->kind == EXPR_PRIMARY
+ && emit_named_member(emitter, expr->member.object->primary.token, expr->member.member))
+ return true;
+
if (!emit_operand(emitter, expr->member.object))
return false;
fprintf(emitter->out, ".%.*s", (int)expr->member.member.length, expr->member.member.start);
diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c
index 23d0a82..22a671c 100644
--- a/src/lexer/lexer.c
+++ b/src/lexer/lexer.c
@@ -34,6 +34,8 @@ static enum TokenType identifier_type(const char* start, size_t length)
{ "const", 5, TOKEN_CONST },
{ "data", 4, TOKEN_DATA },
{ "proc", 4, TOKEN_PROC },
+ { "enum", 4, TOKEN_ENUM },
+ { "struct", 6, TOKEN_STRUCT },
{ "stack", 5, TOKEN_STACK },
{ "if", 2, TOKEN_IF },
{ "goto", 4, TOKEN_GOTO },
@@ -247,6 +249,8 @@ const char* token_type_name(enum TokenType type)
case TOKEN_CONST: return "const";
case TOKEN_DATA: return "data";
case TOKEN_PROC: return "proc";
+ case TOKEN_ENUM: return "enum";
+ case TOKEN_STRUCT: return "struct";
case TOKEN_STACK: return "stack";
case TOKEN_IF: return "if";
case TOKEN_GOTO: return "goto";
diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h
index afe6e7f..3bc215e 100644
--- a/src/lexer/lexer.h
+++ b/src/lexer/lexer.h
@@ -14,6 +14,8 @@ enum TokenType
TOKEN_CONST,
TOKEN_DATA,
TOKEN_PROC,
+ TOKEN_ENUM,
+ TOKEN_STRUCT,
TOKEN_STACK,
TOKEN_IF,
TOKEN_GOTO,
diff --git a/src/parser/ast.c b/src/parser/ast.c
index 704e9a4..f8bd9cc 100644
--- a/src/parser/ast.c
+++ b/src/parser/ast.c
@@ -70,6 +70,12 @@ struct Program create_program(void)
program.data_decls = NULL;
program.data_count = 0;
program.data_capacity = 0;
+ program.enums = NULL;
+ program.enum_count = 0;
+ program.enum_capacity = 0;
+ program.structs = NULL;
+ program.struct_count = 0;
+ program.struct_capacity = 0;
program.procs = NULL;
program.proc_count = 0;
program.proc_capacity = 0;
@@ -83,6 +89,14 @@ void free_program(struct Program* program)
free(program->consts);
free(program->data_decls);
+ for (size_t i = 0; i < program->enum_count; i += 1)
+ free(program->enums[i].members);
+ free(program->enums);
+
+ for (size_t i = 0; i < program->struct_count; i += 1)
+ free(program->structs[i].fields);
+ free(program->structs);
+
for (size_t i = 0; i < program->proc_count; i += 1)
free_proc(&program->procs[i]);
free(program->procs);
@@ -93,6 +107,12 @@ void free_program(struct Program* program)
program->data_decls = NULL;
program->data_count = 0;
program->data_capacity = 0;
+ program->enums = NULL;
+ program->enum_count = 0;
+ program->enum_capacity = 0;
+ program->structs = NULL;
+ program->struct_count = 0;
+ program->struct_capacity = 0;
program->procs = NULL;
program->proc_count = 0;
program->proc_capacity = 0;
@@ -125,6 +145,76 @@ void add_data(struct Program* program, struct DataDecl decl)
program->data_count += 1;
}
+struct EnumDecl create_enum(void)
+{
+ struct EnumDecl decl;
+ decl.members = NULL;
+ decl.member_count = 0;
+ decl.member_capacity = 0;
+ return decl;
+}
+
+void add_enum_member(struct EnumDecl* decl, struct Token member)
+{
+ if (decl->member_count == decl->member_capacity)
+ {
+ size_t capacity = decl->member_capacity < 8 ? 8 : decl->member_capacity * 2;
+ decl->members = realloc(decl->members, capacity * sizeof(struct Token));
+ decl->member_capacity = capacity;
+ }
+
+ decl->members[decl->member_count] = member;
+ decl->member_count += 1;
+}
+
+void add_enum(struct Program* program, struct EnumDecl decl)
+{
+ if (program->enum_count == program->enum_capacity)
+ {
+ size_t capacity = program->enum_capacity < 8 ? 8 : program->enum_capacity * 2;
+ program->enums = realloc(program->enums, capacity * sizeof(struct EnumDecl));
+ program->enum_capacity = capacity;
+ }
+
+ program->enums[program->enum_count] = decl;
+ program->enum_count += 1;
+}
+
+struct StructDecl create_struct(void)
+{
+ struct StructDecl decl;
+ decl.fields = NULL;
+ decl.field_count = 0;
+ decl.field_capacity = 0;
+ return decl;
+}
+
+void add_struct_field(struct StructDecl* decl, struct StructField field)
+{
+ if (decl->field_count == decl->field_capacity)
+ {
+ size_t capacity = decl->field_capacity < 8 ? 8 : decl->field_capacity * 2;
+ decl->fields = realloc(decl->fields, capacity * sizeof(struct StructField));
+ decl->field_capacity = capacity;
+ }
+
+ decl->fields[decl->field_count] = field;
+ decl->field_count += 1;
+}
+
+void add_struct(struct Program* program, struct StructDecl decl)
+{
+ if (program->struct_count == program->struct_capacity)
+ {
+ size_t capacity = program->struct_capacity < 8 ? 8 : program->struct_capacity * 2;
+ program->structs = realloc(program->structs, capacity * sizeof(struct StructDecl));
+ program->struct_capacity = capacity;
+ }
+
+ program->structs[program->struct_count] = decl;
+ program->struct_count += 1;
+}
+
struct ProcDecl create_proc(void)
{
struct ProcDecl proc;
diff --git a/src/parser/ast.h b/src/parser/ast.h
index 98e8dea..b114fb1 100644
--- a/src/parser/ast.h
+++ b/src/parser/ast.h
@@ -6,6 +6,15 @@
#include "lexer/lexer.h"
+enum StoreSize
+{
+ STORE_SIZE_NONE,
+ STORE_SIZE_BYTE,
+ STORE_SIZE_WORD,
+ STORE_SIZE_DWORD,
+ STORE_SIZE_QWORD,
+};
+
struct ConstDecl
{
struct Token name;
@@ -18,19 +27,32 @@ struct DataDecl
struct Token value;
};
-struct Param
+struct EnumDecl
{
struct Token name;
- struct Token reg;
+ struct Token* members;
+ size_t member_count;
+ size_t member_capacity;
};
-enum StoreSize
+struct StructField
{
- STORE_SIZE_NONE,
- STORE_SIZE_BYTE,
- STORE_SIZE_WORD,
- STORE_SIZE_DWORD,
- STORE_SIZE_QWORD,
+ struct Token name;
+ enum StoreSize size;
+};
+
+struct StructDecl
+{
+ struct Token name;
+ struct StructField* fields;
+ size_t field_count;
+ size_t field_capacity;
+};
+
+struct Param
+{
+ struct Token name;
+ struct Token reg;
};
enum ExprKind
@@ -175,6 +197,14 @@ struct Program
size_t data_count;
size_t data_capacity;
+ struct EnumDecl* enums;
+ size_t enum_count;
+ size_t enum_capacity;
+
+ struct StructDecl* structs;
+ size_t struct_count;
+ size_t struct_capacity;
+
struct ProcDecl* procs;
size_t proc_count;
size_t proc_capacity;
@@ -185,6 +215,14 @@ void free_program(struct Program* program);
void add_const(struct Program* program, struct ConstDecl decl);
void add_data(struct Program* program, struct DataDecl decl);
+struct EnumDecl create_enum(void);
+void add_enum_member(struct EnumDecl* decl, struct Token member);
+void add_enum(struct Program* program, struct EnumDecl decl);
+
+struct StructDecl create_struct(void);
+void add_struct_field(struct StructDecl* decl, struct StructField field);
+void add_struct(struct Program* program, struct StructDecl decl);
+
struct ProcDecl create_proc(void);
void free_proc(struct ProcDecl* proc);
void add_param(struct ProcDecl* proc, struct Param param);
diff --git a/src/parser/parser.c b/src/parser/parser.c
index 87dedbd..682e193 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -53,6 +53,7 @@ static bool consume(struct Parser* parser, enum TokenType type, const char* mess
}
static struct Expr* parse_expression(struct Parser* parser);
+static enum StoreSize parse_store_size(struct Parser* parser);
static bool parse_const(struct Parser* parser, struct Program* program)
{
@@ -92,6 +93,89 @@ static bool parse_data(struct Parser* parser, struct Program* program)
return true;
}
+static bool parse_enum(struct Parser* parser, struct Program* program)
+{
+ struct EnumDecl decl = create_enum();
+
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected enum name after 'enum'"))
+ goto error;
+ decl.name = parser->previous;
+
+ if (!consume(parser, TOKEN_LEFT_BRACE, "expected '{' after enum name"))
+ goto error;
+
+ while (!check(parser, TOKEN_RIGHT_BRACE))
+ {
+ if (check(parser, TOKEN_EOF))
+ {
+ error_at(parser, parser->current, "unterminated enum");
+ goto error;
+ }
+
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected an enum member name"))
+ goto error;
+ add_enum_member(&decl, parser->previous);
+
+ match_token(parser, TOKEN_COMMA);
+ }
+ advance_parser(parser);
+
+ add_enum(program, decl);
+ return true;
+
+error:
+ free(decl.members);
+ return false;
+}
+
+static bool parse_struct(struct Parser* parser, struct Program* program)
+{
+ struct StructDecl decl = create_struct();
+
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected struct name after 'struct'"))
+ goto error;
+ decl.name = parser->previous;
+
+ if (!consume(parser, TOKEN_LEFT_BRACE, "expected '{' after struct name"))
+ goto error;
+
+ while (!check(parser, TOKEN_RIGHT_BRACE))
+ {
+ if (check(parser, TOKEN_EOF))
+ {
+ error_at(parser, parser->current, "unterminated struct");
+ goto error;
+ }
+
+ struct StructField field;
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected a field name"))
+ goto error;
+ field.name = parser->previous;
+
+ field.size = STORE_SIZE_QWORD;
+ if (match_token(parser, TOKEN_COLON))
+ {
+ field.size = parse_store_size(parser);
+ if (field.size == STORE_SIZE_NONE)
+ {
+ error_at(parser, parser->current, "expected a size (byte, word, dword, qword) after ':'");
+ goto error;
+ }
+ }
+
+ add_struct_field(&decl, field);
+ match_token(parser, TOKEN_COMMA);
+ }
+ advance_parser(parser);
+
+ add_struct(program, decl);
+ return true;
+
+error:
+ free(decl.fields);
+ return false;
+}
+
static bool parse_params(struct Parser* parser, struct ProcDecl* proc)
{
if (check(parser, TOKEN_RIGHT_PAREN))
@@ -555,6 +639,18 @@ bool parse_program(struct Lexer* lexer, struct Program* out)
if (!parse_data(&parser, out))
return false;
}
+ else if (check(&parser, TOKEN_ENUM))
+ {
+ advance_parser(&parser);
+ if (!parse_enum(&parser, out))
+ return false;
+ }
+ else if (check(&parser, TOKEN_STRUCT))
+ {
+ advance_parser(&parser);
+ if (!parse_struct(&parser, out))
+ return false;
+ }
else if (check(&parser, TOKEN_PROC))
{
advance_parser(&parser);
diff --git a/src/sema/sema.c b/src/sema/sema.c
index 85907e9..d2b4668 100644
--- a/src/sema/sema.c
+++ b/src/sema/sema.c
@@ -27,7 +27,8 @@ static struct ProcDecl* find_proc(struct Program* program, struct Token name)
static bool check_duplicate_names(struct Source source, struct Program* program)
{
- size_t count = program->const_count + program->data_count + program->proc_count;
+ size_t count = program->const_count + program->data_count
+ + program->enum_count + program->struct_count + program->proc_count;
if (count == 0)
return true;
@@ -43,6 +44,16 @@ static bool check_duplicate_names(struct Source source, struct Program* program)
names[n] = program->data_decls[i].name;
n += 1;
}
+ for (size_t i = 0; i < program->enum_count; i += 1)
+ {
+ names[n] = program->enums[i].name;
+ n += 1;
+ }
+ for (size_t i = 0; i < program->struct_count; i += 1)
+ {
+ names[n] = program->structs[i].name;
+ n += 1;
+ }
for (size_t i = 0; i < program->proc_count; i += 1)
{
names[n] = program->procs[i].name;
@@ -241,6 +252,24 @@ static bool is_data(struct RefCheck* check, struct Token token)
return false;
}
+static struct EnumDecl* find_enum(struct RefCheck* check, struct Token token)
+{
+ for (size_t i = 0; i < check->program->enum_count; i += 1)
+ if (names_equal(check->program->enums[i].name, token))
+ return &check->program->enums[i];
+
+ return NULL;
+}
+
+static struct StructDecl* find_struct(struct RefCheck* check, struct Token token)
+{
+ for (size_t i = 0; i < check->program->struct_count; i += 1)
+ if (names_equal(check->program->structs[i].name, token))
+ return &check->program->structs[i];
+
+ return NULL;
+}
+
static bool is_stack_buffer(struct RefCheck* check, struct Token token)
{
for (size_t i = 0; i < check->proc->body_count; i += 1)
@@ -308,15 +337,48 @@ static void check_expr(struct RefCheck* check, struct Expr* expr)
{
if (object->kind != EXPR_PRIMARY || !is_register(check, object->primary.token))
ref_error(check, member, "size suffix requires a register");
+ break;
}
- else if (object->kind != EXPR_PRIMARY || !is_data(check, object->primary.token))
- {
- check_expr(check, object);
- }
- else if (!token_is(member, "len"))
+
+ if (object->kind == EXPR_PRIMARY)
{
- ref_error(check, member, "unknown member '%.*s'", (int)member.length, member.start);
+ struct EnumDecl* enumeration = find_enum(check, object->primary.token);
+ if (enumeration != NULL)
+ {
+ bool found = false;
+ for (size_t i = 0; i < enumeration->member_count; i += 1)
+ if (names_equal(enumeration->members[i], member))
+ found = true;
+ if (!found)
+ ref_error(check, member, "enum '%.*s' has no member '%.*s'",
+ (int)object->primary.token.length, object->primary.token.start,
+ (int)member.length, member.start);
+ break;
+ }
+
+ struct StructDecl* layout = find_struct(check, object->primary.token);
+ if (layout != NULL)
+ {
+ bool found = token_is(member, "size");
+ for (size_t i = 0; i < layout->field_count; i += 1)
+ if (names_equal(layout->fields[i].name, member))
+ found = true;
+ if (!found)
+ ref_error(check, member, "struct '%.*s' has no field '%.*s'",
+ (int)object->primary.token.length, object->primary.token.start,
+ (int)member.length, member.start);
+ break;
+ }
+
+ if (is_data(check, object->primary.token))
+ {
+ if (!token_is(member, "len"))
+ ref_error(check, member, "unknown member '%.*s'", (int)member.length, member.start);
+ break;
+ }
}
+
+ check_expr(check, object);
break;
}
}
diff --git a/tests/codegen_test.c b/tests/codegen_test.c
index c56ca7a..61e441f 100644
--- a/tests/codegen_test.c
+++ b/tests/codegen_test.c
@@ -399,8 +399,36 @@ static void test_generate_load(struct TestContext* context)
free_program(&program);
}
+static void test_generate_enum_struct(struct TestContext* context)
+{
+ struct Lexer lexer = create_lexer(
+ "enum Color\n{\nRed,\nGreen,\nBlue\n}\n"
+ "struct Point\n{\nx: qword\ny: qword\nflag: byte\n}\n"
+ "proc main\n{\nrax = Color.Blue\nrbx = Point.y\nrcx = Point.flag\nrdx = Point.size\n}\n");
+ struct Program program;
+ check(context, parse_program(&lexer, &program));
+
+ FILE* out = tmpfile();
+ generate_nasm(&program, out);
+ fflush(out);
+ rewind(out);
+
+ char buffer[1024];
+ size_t read = fread(buffer, 1, sizeof(buffer) - 1, out);
+ buffer[read] = '\0';
+ fclose(out);
+
+ check(context, strstr(buffer, "mov rax, 2") != NULL); // Color.Blue -> 2
+ check(context, strstr(buffer, "mov rbx, 8") != NULL); // Point.y -> 8
+ check(context, strstr(buffer, "mov rcx, 16") != NULL); // Point.flag -> 16
+ check(context, strstr(buffer, "mov rdx, 17") != NULL); // Point.size -> 17
+
+ free_program(&program);
+}
+
void run_codegen_tests(struct TestContext* context)
{
+ test_generate_enum_struct(context);
test_generate_load(context);
test_generate_logical_registers(context);
test_generate_logical_disabled(context);
diff --git a/tests/lexer_test.c b/tests/lexer_test.c
index 7260d6c..38644d9 100644
--- a/tests/lexer_test.c
+++ b/tests/lexer_test.c
@@ -62,6 +62,13 @@ static void test_size_keywords(struct TestContext* context)
check(context, scan_token(&lexer).type == TOKEN_QWORD);
}
+static void test_enum_struct_keywords(struct TestContext* context)
+{
+ struct Lexer lexer = create_lexer("enum struct");
+ check(context, scan_token(&lexer).type == TOKEN_ENUM);
+ check(context, scan_token(&lexer).type == TOKEN_STRUCT);
+}
+
static void test_line_counting(struct TestContext* context)
{
struct Lexer lexer = create_lexer("a\nb\nc");
@@ -89,6 +96,7 @@ void run_lexer_tests(struct TestContext* context)
test_literals(context);
test_keywords(context);
test_size_keywords(context);
+ test_enum_struct_keywords(context);
test_line_counting(context);
test_comments(context);
}
diff --git a/tests/parser_test.c b/tests/parser_test.c
index 5a45e9e..71e1433 100644
--- a/tests/parser_test.c
+++ b/tests/parser_test.c
@@ -267,6 +267,27 @@ static void test_parse_register_size_suffix(struct TestContext* context)
free_program(&program);
}
+static void test_parse_enum_struct(struct TestContext* context)
+{
+ struct Lexer lexer = create_lexer(
+ "enum Color\n{\nRed,\nGreen,\nBlue\n}\nstruct Point\n{\nx\ny: byte\n}\n");
+ struct Program program;
+
+ check(context, parse_program(&lexer, &program));
+ check(context, program.enum_count == 1);
+ check(context, text_is(program.enums[0].name, "Color"));
+ check(context, program.enums[0].member_count == 3);
+ check(context, text_is(program.enums[0].members[1], "Green"));
+
+ check(context, program.struct_count == 1);
+ check(context, text_is(program.structs[0].name, "Point"));
+ check(context, program.structs[0].field_count == 2);
+ check(context, program.structs[0].fields[0].size == STORE_SIZE_QWORD);
+ check(context, program.structs[0].fields[1].size == STORE_SIZE_BYTE);
+
+ free_program(&program);
+}
+
static void test_parse_errors(struct TestContext* context)
{
struct Program program;
@@ -300,5 +321,6 @@ void run_parser_tests(struct TestContext* context)
test_parse_directives(context);
test_parse_bad_directive(context);
test_parse_register_size_suffix(context);
+ test_parse_enum_struct(context);
test_parse_errors(context);
}
diff --git a/tests/sema_test.c b/tests/sema_test.c
index 2d6e187..e89f608 100644
--- a/tests/sema_test.c
+++ b/tests/sema_test.c
@@ -97,6 +97,17 @@ static void test_deref_needs_register(struct TestContext* context)
check(context, analyze_source("proc main\n{\nrax = ^rsi\n}\n"));
}
+static void test_enum_struct_members(struct TestContext* context)
+{
+ check(context, analyze_source(
+ "enum Color\n{\nRed,\nGreen\n}\nstruct Point\n{\nx\ny\n}\n"
+ "proc main\n{\nrax = Color.Green\nrbx = Point.y\nrcx = Point.size\n}\n"));
+ check(context, !analyze_source(
+ "enum Color\n{\nRed\n}\nproc main\n{\nrax = Color.Nope\n}\n"));
+ check(context, !analyze_source(
+ "struct Point\n{\nx\n}\nproc main\n{\nrax = Point.z\n}\n"));
+}
+
static void test_references_resolve(struct TestContext* context)
{
// registers, params, consts, data (+ .len), stack buffers, labels, calls
@@ -133,5 +144,6 @@ void run_sema_tests(struct TestContext* context)
test_const_expr_rejects_register(context);
test_const_expr_rejects_data(context);
test_deref_needs_register(context);
+ test_enum_struct_members(context);
test_references_resolve(context);
}