aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
7 files changed, 390 insertions, 15 deletions
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;
}
}