diff --git a/include/groundvm.h b/include/groundvm.h index 816de02..ebe7c1e 100644 --- a/include/groundvm.h +++ b/include/groundvm.h @@ -16,7 +16,7 @@ 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, AND, OR, XOR, NEG, SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, 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, AND, OR, XOR, NEG, SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, CALLMETHOD, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD } GroundInstType; typedef enum GroundValueType { diff --git a/src/compiler.c b/src/compiler.c index b2b0e20..d6fc02d 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -1,9 +1,386 @@ #include "compiler.h" #include "types.h" +#include "include/uthash.h" #include +// Helper macros to reduce repetition +#define CHECK_ARGC(expected) \ + if (instruction->args.length != (expected)) { \ + fprintf(stderr, "Error at instruction %zu (%s): expected %d argument(s), got %zu\n", \ + i, #expected, (expected), instruction->args.length); \ + return false; \ + } + +#define CHECK_ARGC_MIN(min) \ + if (instruction->args.length < (min)) { \ + fprintf(stderr, "Error at instruction %zu: expected at least %d argument(s), got %zu\n", \ + i, (min), instruction->args.length); \ + return false; \ + } + +#define CHECK_ARG_TYPE(idx, expected_type) \ + if (instruction->args.args[(idx)].type != (expected_type)) { \ + fprintf(stderr, "Error at instruction %zu: argument %d should be " #expected_type ", got %d\n", \ + i, (idx), instruction->args.args[(idx)].type); \ + return false; \ + } + +bool checkForErrors(GroundProgram* program) { + for (size_t i = 0; i < program->size; i++) { + GroundInstruction* instruction = &program->instructions[i]; + switch (instruction->type) { + + // if $condition %label + case IF: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, LINEREF); + break; + + // jump %label + case JUMP: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, LINEREF); + break; + + // end $status + case END: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // input &variable + case INPUT: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, DIRREF); + break; + + // print $value + case PRINT: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // println $value + case PRINTLN: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // set &variable $value + case SET: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, VALREF); + break; + + // gettype $value &variable + case GETTYPE: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // exists &variable &output + case EXISTS: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // setlist &varname $val1 $val2 ... (at least 1 arg, rest are valrefs) + case SETLIST: + CHECK_ARGC_MIN(1); + CHECK_ARG_TYPE(0, DIRREF); + for (size_t j = 1; j < instruction->args.length; j++) { + if (instruction->args.args[j].type != VALREF) { + fprintf(stderr, "Error at instruction %zu (SETLIST): argument %zu should be VALREF, got %d\n", + i, j, instruction->args.args[j].type); + return false; + } + } + break; + + // setlistat &listname $index $value + case SETLISTAT: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, VALREF); + break; + + // getlistat &list $index &variable + case GETLISTAT: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // getlistsize &list &variable + case GETLISTSIZE: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // listappend $value &list + case LISTAPPEND: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // getstrsize $string &variable + case GETSTRSIZE: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // getstrcharat $string $index &variable + case GETSTRCHARAT: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // add $value $value &variable + case ADD: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // subtract $value $value &variable + case SUBTRACT: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // multiply $value $value &variable + case MULTIPLY: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // divide $value $value &variable + case DIVIDE: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // equal $value $value &variable + case EQUAL: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // inequal $value $value &variable + case INEQUAL: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // not $value &variable + case NOT: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // greater $value $value &variable + case GREATER: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // lesser $value $value &variable + case LESSER: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, VALREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // stoi $value &variable + case STOI: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // stod $value &variable + case STOD: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // itoc, ctoi $value &variable + case ITOC: + case CTOI: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // tostring $value &variable + case TOSTRING: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + break; + + // fun !functionName -returnType [-argType &arg ...] + // Minimum 2 args: the function ref and return type + case FUN: + CHECK_ARGC_MIN(2); + CHECK_ARG_TYPE(0, FNREF); + CHECK_ARG_TYPE(1, TYPEREF); + // Remaining args must be alternating TYPEREF, DIRREF pairs + if ((instruction->args.length - 2) % 2 != 0) { + fprintf(stderr, "Error at instruction %zu (FUN): argument pairs after return type must be -type &name pairs\n", i); + return false; + } + for (size_t j = 2; j < instruction->args.length; j += 2) { + CHECK_ARG_TYPE(j, TYPEREF); + CHECK_ARG_TYPE(j + 1, DIRREF); + } + break; + + // return $value + case RETURN: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // endfun - no args + case ENDFUN: + CHECK_ARGC(0); + break; + + // call !function [$arg1 $arg2 ...] &returnVal + // Minimum 2 args: function ref and return variable + case CALL: + CHECK_ARGC_MIN(2); + CHECK_ARG_TYPE(0, FNREF); + // Middle args are valrefs, last arg is dirref + for (size_t j = 1; j < instruction->args.length - 1; j++) { + if (instruction->args.args[j].type != VALREF) { + fprintf(stderr, "Error at instruction %zu (CALL): argument %zu should be VALREF, got %d\n", + i, j, instruction->args.args[j].type); + return false; + } + } + CHECK_ARG_TYPE(instruction->args.length - 1, DIRREF); + break; + + // call &object !methodName [$arg1 $arg2 ...] &returnVal + // Minimum 3 args: object ref, method name, return variable + case CALLMETHOD: + CHECK_ARGC_MIN(3); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, FNREF); + // Middle args are valrefs, last arg is dirref + for (size_t j = 2; j < instruction->args.length - 1; j++) { + if (instruction->args.args[j].type != VALREF) { + fprintf(stderr, "Error at instruction %zu (CALL): argument %zu should be VALREF, got %d\n", + i, j, instruction->args.args[j].type); + return false; + } + } + CHECK_ARG_TYPE(instruction->args.length - 1, DIRREF); + break; + + // struct -structName + case STRUCT: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, TYPEREF); + break; + + // endstruct - no args + case ENDSTRUCT: + CHECK_ARGC(0); + break; + + // init &var -type + case INIT: + CHECK_ARGC(2); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, TYPEREF); + break; + + // getfield $object &fieldName &outputVar + case GETFIELD: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, VALREF); + CHECK_ARG_TYPE(1, DIRREF); + CHECK_ARG_TYPE(2, DIRREF); + break; + + // setfield &object &fieldName $value + case SETFIELD: + CHECK_ARGC(3); + CHECK_ARG_TYPE(0, DIRREF); + CHECK_ARG_TYPE(1, DIRREF); + CHECK_ARG_TYPE(2, VALREF); + break; + + // use $libraryName + case USE: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // extern $libraryName + case EXTERN: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, VALREF); + break; + + // createlabel @label + case CREATELABEL: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, LABEL); + break; + + // pause, licence - no args + case PAUSE: + case LICENSE: + break; + // drop &variable + case DROP: + CHECK_ARGC(1); + CHECK_ARG_TYPE(0, DIRREF); + break; + } + } + return true; +} + void compileGroundProgram(GroundProgram* program, char* name) { + + if (!checkForErrors(program)) { + printf("Error: Invalid program\n"); + return; + } + Tram_Program tramProgram = Tram_Program_Create(); Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable("main")}); @@ -27,6 +404,153 @@ void compileGroundProgram(GroundProgram* program, char* name) { } -void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program) { +typedef struct GroundCompilerVariable { + GroundValueType type; + char id[MAX_ID_LEN]; + UT_hash_handle hh; +} GroundCompilerVariable; +typedef struct GroundState { + GroundCompilerVariable* variables; + GroundLabel* labels; +} GroundState; + +GroundState createGroundState() { + GroundState state = { + .variables = NULL, + .labels = NULL + }; + return state; +} + +GroundCompilerVariable* cFindVariable(GroundState* state, const char* id) { + GroundCompilerVariable* var; + HASH_FIND_STR(state->variables, id, var); + return var; +} + +GroundCompilerVariable* cDeleteVariable(GroundState* state, const char* id) { + GroundCompilerVariable* var; + HASH_FIND_STR(state->variables, id, var); + HASH_DEL(state->variables, var); +} + +GroundCompilerVariable* cAddVariable(GroundState* state, const char* id, GroundValueType type) { + GroundCompilerVariable* var = malloc(sizeof(GroundCompilerVariable)); + snprintf(var->id, MAX_ID_LEN, "%s", id); + var->type = type; + HASH_ADD_STR(state->variables, id, var); + return var; +} + +void compileGroundInstruction(GroundInstruction* instruction, Tram_Program* program) { + switch (instruction->type) { + case IF: + break; + case JUMP: + break; + case END: + break; + case INPUT: + break; + case PRINT: + break; + case PRINTLN: + break; + case SET: + break; + case GETTYPE: + break; + case EXISTS: + break; + case SETLIST: + break; + case SETLISTAT: + break; + case GETLISTAT: + break; + case GETLISTSIZE: + break; + case LISTAPPEND: + break; + case GETSTRSIZE: + break; + case GETSTRCHARAT: + break; + case ADD: + break; + case SUBTRACT: + break; + case MULTIPLY: + break; + case DIVIDE: + break; + case EQUAL: + break; + case INEQUAL: + break; + case NOT: + break; + case GREATER: + break; + case LESSER: + break; + case AND: + break; + case OR: + break; + case XOR: + break; + case NEG: + break; + case SHIFT: + break; + case STOI: + break; + case STOD: + break; + case ITOC: + break; + case CTOI: + break; + case TOSTRING: + break; + case FUN: + break; + case RETURN: + break; + case ENDFUN: + break; + case PUSHARG: + break; + case CALL: + break; + case STRUCT: + break; + case ENDSTRUCT: + break; + case INIT: + break; + case GETFIELD: + break; + case SETFIELD: + break; + case USE: + break; + case EXTERN: + break; + case CREATELABEL: { + Tram_ParameterList parameters = Tram_ParameterList_Create(1, (Tram_Parameter[]){Tram_Parameter_Variable("main")}); + Tram_Program_AddInstruction(program, Tram_Instruction_Create(Tram_InstructionType_CreateLabel, parameters)); + break; + } + case PAUSE: + break; + case DROP: + break; + case LICENSE: + break; + case ERRORCMD: + break; + } } diff --git a/src/types.h b/src/types.h index ebbed3f..152c86a 100644 --- a/src/types.h +++ b/src/types.h @@ -29,7 +29,7 @@ void wasm_print(const char* str); #endif 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, AND, OR, XOR, NEG, SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, 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, AND, OR, XOR, NEG, SHIFT, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, CALLMETHOD, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, LICENSE, ERRORCMD } GroundInstType; typedef enum GroundValueType {