Compare commits

16 Commits

Author SHA1 Message Date
017d0eb273 Merge pull request 'Update unstable with master' (#18) from master into unstable
Reviewed-on: #18
2026-01-23 14:42:36 +11:00
e8c49508a0 Band-aid fix for structs, use more memory :\ 2026-01-23 13:48:18 +11:00
68182d916e Satisfy Leo's thirst for structs and extlibs 2026-01-22 20:58:49 +11:00
3f684dad3f I'm tired of ragebaiting Leo 2026-01-22 20:40:27 +11:00
03aab86413 Fix the fix? 2026-01-22 20:36:19 +11:00
f9352cf59f Start work on floats 2026-01-22 19:09:26 +11:00
3afb59929a Add int, char, and bool to compiler 2026-01-22 14:22:12 +11:00
0c2abbae2f Start working on allowing more types in compiler 2026-01-22 14:10:26 +11:00
955e75addb Merge pull request 'Pull stuff in again' (#17) from master into unstable
Reviewed-on: #17
2026-01-22 14:07:37 +11:00
c382ecd867 Fix the header again 2026-01-22 10:50:48 +11:00
228d2ac762 I forgot the header again lmao 2026-01-21 18:59:24 +11:00
b289448f56 Add struct field access 2026-01-21 18:41:24 +11:00
a3ca979133 Add not to compiler 2026-01-21 16:02:13 +11:00
3e9ce0dfc0 Expose compiler in groundvm.h 2026-01-21 15:54:18 +11:00
063e85d24a Merge pull request 'Add compiler' (#14) from unstable into master
Reviewed-on: #14
2026-01-21 15:53:20 +11:00
07474d4317 Add print and println to compiler 2026-01-21 15:20:00 +11:00
9 changed files with 658 additions and 42 deletions

View File

@@ -82,7 +82,7 @@ build
- [x] Return values (type checked)
- [x] Arguments for functions
- [x] Jumping within functions
- [ ] Custom data structures
- [x] Custom data structures
- [ ] Working with external libraries
## Debugger
@@ -102,7 +102,7 @@ Commands:
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.
At present only the `int`, `char`, and `bool` data types are supported.
Supported instructions so far:
@@ -114,7 +114,10 @@ Supported instructions so far:
* inequal
* greater
* lesser
* not
* jump
* if
* @ (label creation)
* print
* println
* end

View File

@@ -14,7 +14,7 @@
#include <uthash.h>
typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
} GroundInstType;
typedef enum GroundValueType {
@@ -31,6 +31,7 @@ typedef enum ListAccessStatus {
struct GroundValue;
struct GroundFunction;
struct GroundStruct;
struct List;
@@ -58,7 +59,8 @@ typedef struct GroundError {
*/
typedef struct GroundValue {
GroundValueType type;
union {
struct GroundStruct* customType;
struct {
int64_t intVal;
double doubleVal;
char* stringVal;
@@ -170,6 +172,7 @@ GroundProgram groundCreateProgram();
void groundAddInstructionToProgram(GroundProgram* program, GroundInstruction instruction);
GroundValue groundRunProgram(GroundProgram* program);
void groundPrintProgram(GroundProgram* program);
char* groundCompileProgram(GroundProgram* program);
GroundInstruction groundCreateInstruction(GroundInstType type);
void groundAddValueToInstruction(GroundInstruction* inst, GroundValue value);

View File

@@ -4,6 +4,7 @@
#include "include/estr.h"
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
VariableTable createVariableTable() {
VariableTable vt;
@@ -13,7 +14,7 @@ VariableTable createVariableTable() {
return vt;
}
void addVtVariable(VariableTable* vt, const char* name) {
void addVtVariable(VariableTable* vt, const char* name, GroundValueType type) {
// check if it already exists
for (size_t i = 0; i < vt->count; i++) {
if (strcmp(vt->vars[i].name, name) == 0) {
@@ -29,10 +30,20 @@ void addVtVariable(VariableTable* vt, const char* name) {
// add the variable
strcpy(vt->vars[vt->count].name, name);
vt->vars[vt->count].type = type;
vt->vars[vt->count].offset = vt->count * 8;
vt->count++;
}
VariableInfo* getVariable(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];
}
}
return NULL;
}
int getVariablePos(VariableTable* vt, const char* name) {
for (size_t i = 0; i < vt->count; i++) {
if (strcmp(vt->vars[i].name, name) == 0) {
@@ -42,6 +53,318 @@ int getVariablePos(VariableTable* vt, const char* name) {
return -1;
}
DoubleTable createDoubleTable() {
DoubleTable dt;
dt.count = 0;
dt.capacity = 16;
dt.constants = malloc(sizeof(DoubleConstant) * 16);
return dt;
}
char* addDoubleConstant(DoubleTable* dt, Estr* rodata, double value, int* counter) {
for (size_t i = 0; i < dt->count; i++) {
if (dt->constants[i].value == value) {
return dt->constants[i].label;
}
}
if (dt->count >= dt->capacity) {
dt->capacity = dt->capacity == 0 ? 16 : dt->capacity * 2;
dt->constants = realloc(dt->constants, sizeof(DoubleConstant) * dt->capacity);
}
char label[64];
snprintf(label, sizeof(label), "float_const_%d", (*counter)++);
strcpy(dt->constants[dt->count].label, label);
dt->constants[dt->count].value = value;
dt->count++;
char line[128];
snprintf(line, 128, "%s: dq %.17g ; double constant\n", label, value);
APPEND_ESTR((*rodata), line);
return dt->constants[dt->count - 1].label;
}
GroundValueType getInstructionReturnType(GroundInstruction* gi, VariableTable* table) {
switch (gi->type) {
case JUMP:
case PRINT:
case PRINTLN:
case END:
case IF:
case RETURN:
case ENDFUN:
case PUSHARG:
case CALL: // FIXME do this when functions are added
case USE:
case EXTERN:
case CREATELABEL:
case PAUSE:
case DROP:
case ERRORCMD:
case ENDSTRUCT:
case INIT: // FIXME do this when objects are added
case GETFIELD: // FIXME do this when objects are added
case SETFIELD: // FIXME do this when objects are added
case DIVIDE: // FIXME do this when division is added
case SETLISTAT:
case GETLISTAT: // FIXME do this when lists are getting implemented
case LISTAPPEND: {
return NONE;
}
case TOSTRING:
case GETTYPE:
case INPUT: {
return STRING;
}
case STOI:
case GETLISTSIZE:
case GETSTRSIZE: {
return INT;
}
case STOD: {
return DOUBLE;
}
case EQUAL:
case INEQUAL:
case GREATER:
case LESSER:
case NOT:
case EXISTS: {
return BOOL;
}
case GETSTRCHARAT: {
return CHAR;
}
case SETLIST: {
return LIST;
}
case FUN: {
return FUNCTION;
}
case STRUCT: {
return STRUCTVAL;
}
case SET: {
if (gi->args.length == 2) {
if (gi->args.args[1].type == VALUE) {
return gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
return var->type;
} else {
return NONE;
}
} else {
return NONE;
}
}
case ADD: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == STRING) {
if (rightType == STRING) {
return STRING;
} else {
return NONE;
}
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
case SUBTRACT: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
case MULTIPLY: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
}
return INT;
}
char* processValueString(GroundArg arg, VariableTable* vt, GroundValueType* outType, DoubleTable* dt, Estr* rodata, int* doubleCounter) {
if (arg.type == VALREF) {
VariableInfo* var = getVariable(vt, arg.value.refName);
if (var && outType) *outType = var->type;
char* buf = malloc(260);
snprintf(buf, 260, "[%s]", arg.value.refName);
return buf;
}
if (arg.type == VALUE) {
if (outType) *outType = arg.value.value.type;
switch(arg.value.value.type) {
case INT: {
char* buf = malloc(64);
snprintf(buf, 64, "%" PRId64, arg.value.value.data.intVal);
return buf;
break;
}
case DOUBLE: {
char* label = addDoubleConstant(dt, rodata, arg.value.value.data.doubleVal, doubleCounter);
char* buf = malloc(128);
snprintf(buf, 128, "[rel %s]", label);
return buf;
break;
}
case BOOL:
return arg.value.value.data.boolVal ? "1" : "0";
break;
case CHAR: {
char* buf = malloc(8);
snprintf(buf, 8, "%d", (int)arg.value.value.data.charVal);
return buf;
break;
}
default: {
printf("For now, only int, bool, and char are supported data types in the compiler.");
exit(1);
break;
}
}
}
return NULL;
}
/*
char* processValueString(GroundArg arg) {
if (arg.type == VALREF) {
char* buf = malloc(sizeof(char) * 260);
@@ -49,20 +372,95 @@ char* processValueString(GroundArg arg) {
return buf;
}
if (arg.type == VALUE) {
if (arg.value.value.type != INT) {
printf("Only int is supported right now");
exit(1);
}
switch (arg.value.value.type) {
case INT: {
char* buf = malloc(sizeof(char) * 64);
snprintf(buf, sizeof(char) * 260, "%" PRId64, arg.value.value.data.intVal);
return buf;
break;
}
case BOOL: {
return arg.value.value.data.boolVal ? "1" : "0";
break;
}
case CHAR: {
char* buf = malloc(8);
snprintf(buf, 8, "%d", (int) arg.value.value.data.charVal);
return buf;
break;
}
default: {
printf("For now, only int, bool, and char are supported data types in the compiler.");
exit(1);
break;
}
}
}
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("");
Estr rodata = CREATE_ESTR("section .rodata\n");
APPEND_ESTR(rodata, " newline_char: db 10\n");
APPEND_ESTR(rodata, " space_char: db 32\n");
DoubleTable doubleTable = createDoubleTable();
int doubleConstCounter = 0;
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();
@@ -71,13 +469,13 @@ char* compileGroundProgram(GroundProgram* program) {
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);
addVtVariable(&varTable, gi.args.args[j].value.refName, getInstructionReturnType(&gi, &varTable));
}
}
}
// Create data section
for (size_t i = 0; i < program->size; i++) {
for (size_t i = 0; i < varTable.count; i++) {
if (strcmp(varTable.vars[i].name, "") == 0) {
continue;
}
@@ -113,7 +511,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; set\n")
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
@@ -159,7 +557,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; if\n");
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0]));
APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " test rax, rax\n");
APPEND_ESTR(start, " jnz ");
@@ -168,6 +566,33 @@ char* compileGroundProgram(GroundProgram* program) {
break;
}
case NOT: {
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 != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef", &gi, i);
}
char* varName = gi.args.args[1].value.refName;
APPEND_ESTR(start, " ; not\n");
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " xor rax, 1\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
APPEND_ESTR(start, "], rax\n");
break;
}
case ADD: {
if (gi.args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args for add instruction", &gi, i);
@@ -185,17 +610,55 @@ char* compileGroundProgram(GroundProgram* program) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i);
}
GroundValueType leftType, rightType;
char* left = processValueString(gi.args.args[0], &varTable, &leftType, &doubleTable, &rodata, &doubleConstCounter);
char* right = processValueString(gi.args.args[1], &varTable, &rightType, &doubleTable, &rodata, &doubleConstCounter);
char* varName = gi.args.args[2].value.refName;
bool isDouble = (leftType == DOUBLE || rightType == DOUBLE);
if (isDouble) {
APPEND_ESTR(start, " ; add (double)\n");
if (leftType == INT) {
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, left);
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cvtsi2sd xmm0, rax ; Convert int to double\n");
} else {
APPEND_ESTR(start, " movsd xmm0, ");
APPEND_ESTR(start, left);
APPEND_ESTR(start, "\n");
}
if (rightType == INT) {
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, right);
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cvtsi2sd xmm1, rax ; Convert int to double\n");
APPEND_ESTR(start, " addsd xmm0, xmm1\n");
} else {
APPEND_ESTR(start, " addsd xmm0, ");
APPEND_ESTR(start, right);
APPEND_ESTR(start, "\n");
}
APPEND_ESTR(start, " movsd [");
APPEND_ESTR(start, varName);
APPEND_ESTR(start, "], xmm0\n");
} else {
APPEND_ESTR(start, " ; add\n");
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0]));
APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " add rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
APPEND_ESTR(start, "], rax\n");
}
break;
}
case SUBTRACT: {
@@ -218,10 +681,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " sub rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
@@ -248,10 +711,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " imul rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
@@ -278,10 +741,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " sete al\n");
APPEND_ESTR(start, " movzx rax, al\n");
@@ -310,10 +773,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setne al\n");
APPEND_ESTR(start, " movzx rax, al\n");
@@ -342,10 +805,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setg al\n");
APPEND_ESTR(start, " movzx rax, al\n");
@@ -374,10 +837,10 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1]));
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setl al\n");
APPEND_ESTR(start, " movzx rax, al\n");
@@ -386,6 +849,42 @@ char* compileGroundProgram(GroundProgram* program) {
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], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
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);
@@ -396,11 +895,15 @@ char* compileGroundProgram(GroundProgram* program) {
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, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
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);
@@ -411,7 +914,9 @@ char* compileGroundProgram(GroundProgram* program) {
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, data.str)
APPEND_ESTR(complete, helpers.str);
APPEND_ESTR(complete, data.str);
APPEND_ESTR(complete, rodata.str);
return complete.str;
}

View File

@@ -7,6 +7,7 @@ char* compileGroundProgram(GroundProgram* program);
typedef struct VariableInfo {
char name[256];
int offset;
GroundValueType type;
} VariableInfo;
typedef struct VariableTable {
@@ -15,4 +16,13 @@ typedef struct VariableTable {
size_t capacity;
} VariableTable;
typedef struct DoubleConstant {
double value;
char label[64];
} DoubleConstant;
typedef struct DoubleTable {
DoubleConstant* constants;
size_t count;
size_t capacity;
} DoubleTable;

View File

@@ -1,5 +1,6 @@
#include "parser.h"
#include "interpreter.h"
#include "compiler.h"
#include "types.h"
#include <stdlib.h>
@@ -123,3 +124,7 @@ void groundAddValueToScope(GroundScope* gs, const char* name, GroundValue value)
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field) {
addFieldToStruct(gstruct, name, field);
}
char* groundCompileProgram(GroundProgram* program) {
return compileGroundProgram(program);
}

View File

@@ -2028,6 +2028,77 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
break;
}
/*
* OBJECTS
* Allows manipulation of Ground objects.
*/
// getfield $obj &field &result
case GETFIELD: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Object for arg 1", in, currentInstruction);
}
if (in->args.args[0].value.value.type != CUSTOM) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Object for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundObjectField* field = findField(*in->args.args[0].value.value.data.customVal, in->args.args[1].value.refName);
if (field == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[2].value.refName, field->value);
break;
}
// setfield &obj &field $value
case SETFIELD: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an Object for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 3", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Could not find that object", in, currentInstruction);
}
if (var->value.type != CUSTOM) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to an Object for arg 1", in, currentInstruction);
}
GroundObjectField* field = findField(*var->value.data.customVal, in->args.args[1].value.refName);
if (field == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Struct does not contain that field", in, currentInstruction);
}
if (field->value.type != in->args.args[2].value.value.type) {
runtimeError(ARG_TYPE_MISMATCH, "Field type and provided type do not match", in, currentInstruction);
}
field->value = copyGroundValue(&in->args.args[2].value.value);
break;
}
case DROP: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);

View File

@@ -168,6 +168,8 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "struct") == 0) return STRUCT;
if (strcmp(inst, "endstruct") == 0) return ENDSTRUCT;
if (strcmp(inst, "init") == 0) return INIT;
if (strcmp(inst, "getfield") == 0) return GETFIELD;
if (strcmp(inst, "setfield") == 0) return SETFIELD;
if (strcmp(inst, "use") == 0) return USE;
if (strcmp(inst, "extern") == 0) return EXTERN;
if (strcmp(inst, "drop") == 0) return DROP;

View File

@@ -9,7 +9,7 @@
#include "include/uthash.h"
typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
} GroundInstType;
typedef enum GroundValueType {
@@ -63,7 +63,7 @@ typedef struct GroundError {
typedef struct GroundValue {
GroundValueType type;
struct GroundStruct* customType;
union {
struct {
int64_t intVal;
double doubleVal;
char* stringVal;

17
tests/struct.grnd Normal file
View File

@@ -0,0 +1,17 @@
struct -point
init &x -int
init &y -int
endstruct
init &mypoint -point
setfield &mypoint &x 53
setfield &mypoint &y 32
getfield $mypoint &x &value
println $value
getfield $mypoint &y &value
println $value