diff --git a/README.md b/README.md index 7cd9ef5..9240c35 100644 --- a/README.md +++ b/README.md @@ -98,3 +98,25 @@ Commands: * eval (code): Runs Ground code in the current scope * help: Shows a help message +## Compiler + +CGround now includes an experimental Ground -> x86_64 Linux ASM compiler. You can try it by adding the `-c` flag when running Ground. This will print the generated ASM to the console. + +At present only the `int` data type is supported. + +Supported instructions so far: + +* set +* add +* subtract +* multiply +* equal +* inequal +* greater +* lesser +* jump +* if +* @ (label creation) +* print +* println +* end diff --git a/src/compiler.c b/src/compiler.c new file mode 100644 index 0000000..716aed9 --- /dev/null +++ b/src/compiler.c @@ -0,0 +1,514 @@ +#include "compiler.h" +#include "interpreter.h" +#include "types.h" +#include "include/estr.h" +#include +#include + +VariableTable createVariableTable() { + VariableTable vt; + vt.capacity = 16; + vt.count = 0; + vt.vars = malloc(sizeof(VariableInfo) * vt.capacity); + return vt; +} + +void addVtVariable(VariableTable* vt, const char* name) { + // check if it already exists + for (size_t i = 0; i < vt->count; i++) { + if (strcmp(vt->vars[i].name, name) == 0) { + return; + } + } + + // increase capacity + if (vt->count >= vt->capacity) { + vt->capacity *= 2; + vt->vars = realloc(vt->vars, sizeof(VariableInfo) * vt->capacity); + } + + // add the variable + strcpy(vt->vars[vt->count].name, name); + vt->vars[vt->count].offset = vt->count * 8; + vt->count++; +} + +int getVariablePos(VariableTable* vt, const char* name) { + for (size_t i = 0; i < vt->count; i++) { + if (strcmp(vt->vars[i].name, name) == 0) { + return vt->vars[i].offset; + } + } + return -1; +} + +char* processValueString(GroundArg arg) { + if (arg.type == VALREF) { + char* buf = malloc(sizeof(char) * 260); + snprintf(buf, sizeof(char) * 260, "[%s]", arg.value.refName); + return buf; + } + if (arg.type == VALUE) { + if (arg.value.value.type != INT) { + printf("Only int is supported right now\n"); + exit(1); + } + char* buf = malloc(sizeof(char) * 64); + snprintf(buf, sizeof(char) * 260, "%" PRId64, arg.value.value.data.intVal); + return buf; + } + return NULL; +} + +char* compileGroundProgram(GroundProgram* program) { + Estr start = CREATE_ESTR("global _start\nsection .text\n_start:\n"); + Estr data = CREATE_ESTR("section .bss\n"); + Estr helpers = CREATE_ESTR(""); + + APPEND_ESTR(helpers, "\n; Helper: Print integer in rax\n"); + APPEND_ESTR(helpers, "print_int:\n"); + APPEND_ESTR(helpers, " push rbp\n"); + APPEND_ESTR(helpers, " mov rbp, rsp\n"); + APPEND_ESTR(helpers, " sub rsp, 32 ; Allocate buffer on stack\n"); + APPEND_ESTR(helpers, " mov rdi, rsp ; RDI = buffer pointer\n"); + APPEND_ESTR(helpers, " add rdi, 31 ; Point to end of buffer\n"); + APPEND_ESTR(helpers, " mov byte [rdi], 0 ; Null terminator\n"); + APPEND_ESTR(helpers, " dec rdi\n"); + APPEND_ESTR(helpers, " mov rbx, 10 ; Divisor\n"); + APPEND_ESTR(helpers, " test rax, rax\n"); + APPEND_ESTR(helpers, " jns .positive\n"); + APPEND_ESTR(helpers, " neg rax ; Make positive\n"); + APPEND_ESTR(helpers, " push rax\n"); + APPEND_ESTR(helpers, " mov byte [rdi], '-'\n"); + APPEND_ESTR(helpers, " dec rdi\n"); + APPEND_ESTR(helpers, " pop rax\n"); + APPEND_ESTR(helpers, ".positive:\n"); + APPEND_ESTR(helpers, " xor rcx, rcx ; Digit counter\n"); + APPEND_ESTR(helpers, ".convert_loop:\n"); + APPEND_ESTR(helpers, " xor rdx, rdx\n"); + APPEND_ESTR(helpers, " div rbx ; rax = rax/10, rdx = remainder\n"); + APPEND_ESTR(helpers, " add dl, '0' ; Convert to ASCII\n"); + APPEND_ESTR(helpers, " mov [rdi], dl\n"); + APPEND_ESTR(helpers, " dec rdi\n"); + APPEND_ESTR(helpers, " inc rcx\n"); + APPEND_ESTR(helpers, " test rax, rax\n"); + APPEND_ESTR(helpers, " jnz .convert_loop\n"); + APPEND_ESTR(helpers, " inc rdi ; Point back to first digit\n"); + APPEND_ESTR(helpers, " ; Now print the string\n"); + APPEND_ESTR(helpers, " mov rax, 1 ; sys_write\n"); + APPEND_ESTR(helpers, " mov rsi, rdi ; Buffer\n"); + APPEND_ESTR(helpers, " mov rdx, rcx ; Length\n"); + APPEND_ESTR(helpers, " mov rdi, 1 ; stdout\n"); + APPEND_ESTR(helpers, " syscall\n"); + APPEND_ESTR(helpers, " mov rsp, rbp\n"); + APPEND_ESTR(helpers, " pop rbp\n"); + APPEND_ESTR(helpers, " ret\n"); + + APPEND_ESTR(helpers, "\n; Helper: Print string at address in rax, length in rbx\n"); + APPEND_ESTR(helpers, "print_string:\n"); + APPEND_ESTR(helpers, " push rax\n"); + APPEND_ESTR(helpers, " push rbx\n"); + APPEND_ESTR(helpers, " mov rdi, 1 ; stdout\n"); + APPEND_ESTR(helpers, " mov rsi, rax ; String address\n"); + APPEND_ESTR(helpers, " mov rdx, rbx ; Length\n"); + APPEND_ESTR(helpers, " mov rax, 1 ; sys_write\n"); + APPEND_ESTR(helpers, " syscall\n"); + APPEND_ESTR(helpers, " pop rbx\n"); + APPEND_ESTR(helpers, " pop rax\n"); + APPEND_ESTR(helpers, " ret\n"); + + VariableTable varTable = createVariableTable(); + + // Allocate a spot for all direct references + for (size_t i = 0; i < program->size; i++) { + GroundInstruction gi = program->instructions[i]; + for (size_t j = 0; j < gi.args.length; j++) { + if (gi.args.args[j].type == DIRREF) { + addVtVariable(&varTable, gi.args.args[j].value.refName); + } + } + } + + // Create data section + for (size_t i = 0; i < varTable.count; i++) { + if (strcmp(varTable.vars[i].name, "") == 0) { + continue; + } + APPEND_ESTR(data, " "); + APPEND_ESTR(data, varTable.vars[i].name); + APPEND_ESTR(data, " resq 1\n"); + } + + // Generate assembly + for (size_t i = 0; i < program->size; i++) { + GroundInstruction gi = program->instructions[i]; + switch (gi.type) { + case CREATELABEL: { + APPEND_ESTR(start, gi.args.args[0].value.refName); + APPEND_ESTR(start, ":\n"); + break; + } + case SET: { + if (gi.args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", &gi, i); + } + if (gi.args.length > 2) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args", &gi, i); + } + if (gi.args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", &gi, i); + } + + char* varName= gi.args.args[0].value.refName; + + APPEND_ESTR(start, " ; set\n") + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + + break; + } + case JUMP: { + if (gi.args.length < 1) { + runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &gi, i); + } + if (gi.args.length > 1) { + runtimeError(TOO_MANY_ARGS, "Expecting 1 arg", &gi, i); + } + if (gi.args.args[0].type != LINEREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef", &gi, i); + } + + char* labelName = gi.args.args[0].value.refName; + + APPEND_ESTR(start, " ; jump\n"); + APPEND_ESTR(start, " jmp "); + APPEND_ESTR(start, labelName); + APPEND_ESTR(start, "\n"); + + break; + } + case IF: { + if (gi.args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", &gi, i); + } + if (gi.args.length > 2) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int (for now) for arg 1", &gi, i); + } + if (gi.args.args[1].type != LINEREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef", &gi, i); + } + + char* labelName = gi.args.args[1].value.refName; + + APPEND_ESTR(start, " ; if\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " test rax, rax\n"); + APPEND_ESTR(start, " jnz "); + APPEND_ESTR(start, labelName); + APPEND_ESTR(start, "\n"); + + break; + } + case ADD: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for add instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for add instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; add\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " add rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case SUBTRACT: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for subtract instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for subtract instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; subtract\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " sub rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case MULTIPLY: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for multiply instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for multiply instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; multiply\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " imul rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case EQUAL: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for equal instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for equal instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; equal\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " cmp rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " sete al\n"); + APPEND_ESTR(start, " movzx rax, al\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case INEQUAL: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for inequal instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for inequal instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; inequal\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " cmp rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " setne al\n"); + APPEND_ESTR(start, " movzx rax, al\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case GREATER: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for greater instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for greater instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; greater\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " cmp rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " setg al\n"); + APPEND_ESTR(start, " movzx rax, al\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case LESSER: { + if (gi.args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args for lesser instruction", &gi, i); + } + if (gi.args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args for lesser instruction", &gi, i); + } + if (gi.args.args[0].type != VALUE && gi.args.args[0].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", &gi, i); + } + if (gi.args.args[1].type != VALUE && gi.args.args[1].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 2", &gi, i); + } + if (gi.args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); + } + + char* varName= gi.args.args[2].value.refName; + APPEND_ESTR(start, " ; lesser\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " cmp rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[1])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " setl al\n"); + APPEND_ESTR(start, " movzx rax, al\n"); + APPEND_ESTR(start, " mov ["); + APPEND_ESTR(start, varName); + APPEND_ESTR(start, "], rax\n"); + break; + } + case PRINT: + case PRINTLN: { + if (gi.args.length < 1) { + runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &gi, i); + } + + for (size_t j = 0; j < gi.args.length; j++) { + if (gi.args.args[j].type != VALUE && gi.args.args[j].type != VALREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", &gi, i); + } + if (j > 0) { + // Print space between arguments + APPEND_ESTR(start, " ; print space\n"); + APPEND_ESTR(start, " mov rax, 1\n"); + APPEND_ESTR(start, " mov rdi, 1\n"); + APPEND_ESTR(start, " lea rsi, [rel space_char]\n"); + APPEND_ESTR(start, " mov rdx, 1\n"); + APPEND_ESTR(start, " syscall\n"); + } + APPEND_ESTR(start, " ; print int\n"); + APPEND_ESTR(start, " mov rax, "); + APPEND_ESTR(start, processValueString(gi.args.args[j])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " call print_int\n"); + } + + if (gi.type == PRINTLN) { + APPEND_ESTR(start, " ; print newline\n"); + APPEND_ESTR(start, " mov rax, 1\n"); + APPEND_ESTR(start, " mov rdi, 1\n"); + APPEND_ESTR(start, " lea rsi, [rel newline_char]\n"); + APPEND_ESTR(start, " mov rdx, 1\n"); + APPEND_ESTR(start, " syscall\n"); + } + break; + } + case END: { + if (gi.args.length < 1) { + runtimeError(TOO_FEW_ARGS, "Expecting 1 arg for end instruction", &gi, i); + } + if (gi.args.length > 1) { + runtimeError(TOO_MANY_ARGS, "Expecting 1 arg for end instruction", &gi, i); + } + APPEND_ESTR(start, " ; end\n"); + APPEND_ESTR(start, " mov rax, 60\n"); + APPEND_ESTR(start, " mov rdi, "); + APPEND_ESTR(start, processValueString(gi.args.args[0])); + APPEND_ESTR(start, "\n"); + APPEND_ESTR(start, " syscall\n"); + break; + } + case DROP: { + // Drop does nothing in ground->asm as we use the stack + break; + } + default: { + printf("no\n"); + exit(1); + } + } + } + + Estr complete = CREATE_ESTR(""); + APPEND_ESTR(complete, start.str); + APPEND_ESTR(complete, " ; End of program\n mov rax, 60\n mov rdi, 0\n syscall\n"); + APPEND_ESTR(complete, helpers.str); + APPEND_ESTR(complete, "\nsection .rodata\n"); + APPEND_ESTR(complete, "newline_char: db 10\n"); + APPEND_ESTR(complete, "space_char: db 32\n"); + APPEND_ESTR(complete, data.str) + + return complete.str; +} diff --git a/src/compiler.h b/src/compiler.h new file mode 100644 index 0000000..4e5e37f --- /dev/null +++ b/src/compiler.h @@ -0,0 +1,18 @@ +#include "types.h" +#include "interpreter.h" + +char* compileGroundProgram(GroundProgram* program); +[[noreturn]] void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine); + +typedef struct VariableInfo { + char name[256]; + int offset; +} VariableInfo; + +typedef struct VariableTable { + VariableInfo* vars; + size_t count; + size_t capacity; +} VariableTable; + + diff --git a/src/include/estr.h b/src/include/estr.h new file mode 100644 index 0000000..df5d172 --- /dev/null +++ b/src/include/estr.h @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#ifndef ESTR_H +#define ESTR_H + +/* + + estr.h - Easy string manipulation + This library has macros to allow easier manipulation of strings. No longer shall + you have to malloc and realloc away to keep adding to your strings. + + Usage: + + Estr myString = CREATE_ESTR("my awesome string"); + APPEND_ESTR(myString, " is so cool"); + printf("%s\n", myString.str); + +*/ + +#define CREATE_ESTR(instr) \ + (Estr) { \ + .str = instr,\ + .size = strlen(instr),\ + .shouldBeFreed = 0, \ + .destroyed = 0 \ + } + +#define APPEND_ESTR(estr, instr) { \ + estr.size = estr.size + strlen(instr); \ + char* tmp_ptr = malloc(estr.size + 1); \ + if (tmp_ptr == NULL) printf("WARNING: Could not realloc estr " #estr "\n"); \ + else { \ + snprintf(tmp_ptr, estr.size + 1, "%s%s", estr.str, instr); \ + if (estr.shouldBeFreed > 0) free(estr.str); \ + estr.shouldBeFreed = 1; \ + estr.str = tmp_ptr; \ + } \ +} + +#define DESTROY_ESTR(estr) if (estr.shouldBeFreed > 0 && estr.destroyed < 1) free(estr.str); + +typedef struct Estr { + char* str; + size_t size; + int8_t shouldBeFreed; + int8_t destroyed; +} Estr; + +#endif // ESTR_H diff --git a/src/interface.c b/src/interface.c index 3fc61a6..2f41515 100644 --- a/src/interface.c +++ b/src/interface.c @@ -1,4 +1,3 @@ -#include "lexer.h" #include "parser.h" #include "interpreter.h" #include "types.h" @@ -75,9 +74,9 @@ GroundValue groundCreateValue(GroundValueType type, ...) { case STRUCTVAL: { GroundValue gv; gv.type = STRUCTVAL; - gv.data.structVal = va_arg(args, GroundStruct*); + gv.data.structVal = malloc(sizeof(GroundStruct)); + *gv.data.structVal = va_arg(args, GroundStruct); return gv; - break; } case NONE: { return createNoneGroundValue(); @@ -88,11 +87,6 @@ GroundValue groundCreateValue(GroundValueType type, ...) { // FIXME break; } - /* - default: { - return createNoneGroundValue(); - } - */ } return createNoneGroundValue(); @@ -114,3 +108,18 @@ void groundPrintProgram(GroundProgram* program) { GroundProgram groundParseFile(const char* code) { return parseFile(code); } + +GroundStruct groundCreateStruct() { + GroundStruct gs; + gs.size = 0; + gs.fields = malloc(sizeof(GroundStructField)); + return gs; +} + +void groundAddValueToScope(GroundScope* gs, const char* name, GroundValue value) { + addVariable(gs->variables, name, value); +} + +void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field) { + addFieldToStruct(gstruct, name, field); +} diff --git a/src/interpreter.c b/src/interpreter.c index 07f73f3..0dbb3ca 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -352,7 +352,7 @@ GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffs GroundValue gv; - switch (stringToValueType(in->instructions[i].args.args[0].value.refName)) { + switch (stringToValueType(in->instructions[i].args.args[1].value.refName)) { case INT: { gv = createIntGroundValue(0); break; @@ -399,6 +399,7 @@ GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffs gv.type = CUSTOM; gv.data.customVal = malloc(sizeof(GroundObject)); *gv.data.customVal = createObject(*gstruct); + gv.customType = gstruct; break; } case NONE: { @@ -1782,7 +1783,8 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (in->args.args[i + 1].type != VALUE) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); } - if (in->args.args[i + 1].value.value.type != function->args[i].type) { + //if (in->args.args[i + 1].value.value.type != function->args[i].type) { + if (!checkFnTypes(&in->args.args[i + 1].value.value, &function->args[i])) { runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction); } appendToList(&argsList, in->args.args[i + 1].value.value); @@ -1810,7 +1812,8 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (in->args.args[i + 1].type != VALUE) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); } - if (in->args.args[i + 1].value.value.type != function->args[i].type) { + //if (in->args.args[i + 1].value.value.type != function->args[i].type) { + if (!checkFnTypes(&in->args.args[i + 1].value.value, &function->args[i])) { runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction); } addVariable(newScope.variables, function->args[i].name, in->args.args[i + 1].value.value); @@ -2008,6 +2011,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop gv.type = CUSTOM; gv.data.customVal = malloc(sizeof(GroundObject)); *gv.data.customVal = createObject(*gstruct); + gv.customType = gstruct; break; } case NONE: { diff --git a/src/interpreter.h b/src/interpreter.h index cde1fff..ae78460 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -42,6 +42,7 @@ GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset); GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); +void addVariable(GroundVariable **head, const char *id, GroundValue data); diff --git a/src/main.c b/src/main.c index 85ebc42..2bdd63e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "parser.h" #include "interpreter.h" +#include "compiler.h" #include char* getFileContents(const char* filename) { @@ -39,6 +40,7 @@ char* getFileContents(const char* filename) { } int main(int argc, char** argv) { + /* if (argc < 2) { printf("Usage: ground [file]\n"); exit(1); @@ -46,5 +48,49 @@ int main(int argc, char** argv) { char* file = getFileContents(argv[1]); GroundProgram program = parseFile(file); free(file); + char* compiled = compileGroundProgram(&program); + printf("%s\n", compiled); interpretGroundProgram(&program, NULL); + */ + + if (argc < 2) { + printf("Usage: %s [-c] [--compile] [-h] [--help]\n", argv[0]); + exit(1); + } + + bool compile = false; + char* fileName = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp("--compile", argv[i]) == 0 || strcmp("-c", argv[i]) == 0) { + compile = true; + } + else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) { + printf("GroundVM help\n"); + printf("Usage: %s [-c] [--compile] [-h] [--help]\n", argv[0]); + printf("Options:\n"); + printf(" -c or --compile: Outputs Linux x86_64 assembly instead of interpreting (WIP)\n"); + printf(" -h or --help: Shows this help message\n"); + exit(0); + } else { + fileName = argv[i]; + } + } + + if (fileName == NULL) { + printf("Usage: %s [-c] [--compile] [-h] [--help]\n", argv[0]); + printf("Error: No file name provided\n"); + exit(1); + } + + char* file = getFileContents(fileName); + GroundProgram program = parseFile(file); + free(file); + + if (compile) { + char* compiled = compileGroundProgram(&program); + printf("%s\n", compiled); + } else { + interpretGroundProgram(&program, NULL); + } } diff --git a/src/types.c b/src/types.c index 334a466..bbf5c36 100644 --- a/src/types.c +++ b/src/types.c @@ -585,3 +585,43 @@ GroundError createGroundError(char* what, char* type, GroundInstruction* where, return ge; } + +bool checkFnTypes(GroundValue* left, GroundFunctionArgs* right) { + if (left->type != right->type) { + return false; + } + if (left->type == CUSTOM) { + if (left->customType->size != right->customType->size) { + return false; + } + for (size_t i = 0; i < left->customType->size; i++) { + if (strcmp(left->customType->fields[i].id, right->customType->fields[i].id) != 0) { + return false; + } + if (!checkTypes(&left->customType->fields[i].value, &right->customType->fields[i].value)) { + return false; + } + } + } + return true; +} + +bool checkTypes(GroundValue* left, GroundValue* right) { + if (left->type != right->type) { + return false; + } + if (left->type == CUSTOM) { + if (left->customType->size != right->customType->size) { + return false; + } + for (size_t i = 0; i < left->customType->size; i++) { + if (strcmp(left->customType->fields[i].id, right->customType->fields[i].id) != 0) { + return false; + } + if (!checkTypes(&left->customType->fields[i].value, &right->customType->fields[i].value)) { + return false; + } + } + } + return true; +} diff --git a/src/types.h b/src/types.h index 65690dc..5085cd5 100644 --- a/src/types.h +++ b/src/types.h @@ -62,6 +62,7 @@ typedef struct GroundError { */ typedef struct GroundValue { GroundValueType type; + struct GroundStruct* customType; union { int64_t intVal; double doubleVal; @@ -125,6 +126,7 @@ typedef struct GroundProgram { */ typedef struct GroundFunctionArgs { GroundValueType type; + struct GroundStruct* customType; char* name; } GroundFunctionArgs; @@ -276,6 +278,11 @@ void freeGroundObject(GroundObject* object); // Creates a GroundError. GroundError createGroundError(char* what, char* type, GroundInstruction* where, size_t* line); +// Compares types of a value and function args. +bool checkFnTypes(GroundValue* left, GroundFunctionArgs* arg); + +// Compares types of two values. +bool checkTypes(GroundValue* left, GroundValue* right); #endif