#include "../../../include/ground.h" #include "../../linenoise/linenoise.h" #include #include #include #include #include #define HEAP_GET(heap, idx) (&(heap)->heap[(idx)]) #define HEAP_SET(heap, idx, val) ((heap)->heap[(idx)] = (val)) #define CONTINUE (struct GroundExecutionResult) { _GER_CONTINUE } #define JUMP(idx) (struct GroundExecutionResult) { .type = _GER_JUMP, .as.line = idx } #define END(res) (struct GroundExecutionResult) { .type = _GER_END, .as.end = res } #define RETURN(val) (struct GroundExecutionResult) { .type = _GER_RETURN, .as.value = val } static void printValue(GroundBytecodeValue* val) { switch (val->type.type) { case GroundType_Int: printf("%" PRId64, val->as.Int); break; case GroundType_Double: printf("%f", val->as.Double); break; case GroundType_Bool: printf(val->as.Bool ? "true" : "false"); break; case GroundType_Char: printf("%c", val->as.Char); break; case GroundType_String: printf("%s", val->as.String.cstr); break; default: printf(""); break; } } struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeInstruction* instruction, GroundBytecodeHeap* heap) { static const void* jumpTable[] = { &&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, &&CALL, &&CALLMETHOD, &&STRUCT, &&ENDSTRUCT, &&INIT, &&GETFIELD, &&SETFIELD, &&USE, &&EXTERN, &&CREATELABEL, &&PAUSE, &&DROP, &&LICENSE, &&ERRORCMD, &&THROW, &&CATCH }; goto *jumpTable[instruction->type]; IF: { GroundBytecodeValue* cond = HEAP_GET(heap, instruction->args.at[0]); if (cond->as.Bool) { return JUMP(instruction->args.at[1]); } return CONTINUE; } JUMP: { return JUMP(instruction->args.at[0]); } END: { return END( HEAP_GET(heap, instruction->args.at[0])->as.Int ); } INPUT: { char* input = linenoise(""); HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(input)))); free(input); return CONTINUE; } PRINT: { for (GroundSize i = 0; i < instruction->args.len; i++) { printValue(HEAP_GET(heap, instruction->args.at[i])); } return CONTINUE; } PRINTLN: { for (GroundSize i = 0; i < instruction->args.len; i++) { printValue(HEAP_GET(heap, instruction->args.at[i])); } printf("\n"); return CONTINUE; } SET: { HEAP_SET(heap, instruction->args.at[0], *HEAP_GET(heap, instruction->args.at[1])); return CONTINUE; } GETTYPE: { Ground.Log.Warning("GETTYPE is deprecated"); return CONTINUE; } EXISTS: { Ground.Log.Warning("EXISTS is deprecated"); return CONTINUE; } SETLIST: { return CONTINUE; } SETLISTAT: { return CONTINUE; } GETLISTAT: { return CONTINUE; } GETLISTSIZE: { return CONTINUE; } LISTAPPEND: { return CONTINUE; } GETSTRSIZE: { return CONTINUE; } GETSTRCHARAT: { return CONTINUE; } ADD: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Int(left->as.Int + right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Int + right->as.Double)); break; } default: { Ground.Log.Error("invalid add operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double + right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double + right->as.Double)); break; } default: { Ground.Log.Error("invalid add operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } case GroundType_String: { switch (right->type.type) { case GroundType_String: { GroundSize total = left->as.String.len + right->as.String.len; char* buf = malloc(total + 1); if (buf == NULL) { Ground.Log.Error("malloc failed (instruction ADD{string, string, dirref}) in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } sprintf(buf, "%s%s", left->as.String.cstr, right->as.String.cstr); *final = Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(buf))); break; } default: { Ground.Log.Error("invalid add operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } } default: { Ground.Log.Error("invalid add operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } return CONTINUE; } SUBTRACT: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Int(left->as.Int - right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Int - right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double - right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double - right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } return CONTINUE; } MULTIPLY: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Int(left->as.Int * right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Int * right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double * right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double * right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } return CONTINUE; } DIVIDE: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Int(left->as.Int / right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Int / right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double / right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Double(left->as.Double / right->as.Double)); break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } break; } default: { Ground.Log.Error("invalid subtract operation in Ground.Instruction.execute()"); Ground.Flags.error = true; break; } } return CONTINUE; } EQUAL: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int == right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int == right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double == right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double == right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } break; } case GroundType_Char: { if (right->type.type == GroundType_Char) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Char == right->as.Char)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); } break; } case GroundType_Bool: { if (right->type.type == GroundType_Bool) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Bool == right->as.Bool)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); } break; } case GroundType_String: { if (right->type.type == GroundType_String) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(strcmp(left->as.String.cstr, right->as.String.cstr) == 0)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); } break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } return CONTINUE; } INEQUAL: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int != right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int != right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double != right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double != right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } case GroundType_Char: { if (right->type.type == GroundType_Char) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Char != right->as.Char)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); } break; } case GroundType_Bool: { if (right->type.type == GroundType_Bool) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Bool != right->as.Bool)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); } break; } case GroundType_String: { if (right->type.type == GroundType_String) { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(strcmp(left->as.String.cstr, right->as.String.cstr) != 0)); } else { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); } break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } return CONTINUE; } NOT: { HEAP_SET(heap, instruction->args.at[1], Ground.New.BytecodeValue(Ground.New.Value.Bool(!HEAP_GET(heap, instruction->args.at[0])->as.Bool))); return CONTINUE; } GREATER: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int > right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int > right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double > right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double > right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } return CONTINUE; } LESSER: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); switch (left->type.type) { case GroundType_Int: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int < right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Int < right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } case GroundType_Double: { switch (right->type.type) { case GroundType_Int: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double < right->as.Int)); break; } case GroundType_Double: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Double < right->as.Double)); break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(true)); break; } } break; } default: { *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } } return CONTINUE; } AND: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Bool && right->as.Bool)); return CONTINUE; } OR: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Bool || right->as.Bool)); return CONTINUE; } XOR: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[2]); GroundBytecodeValue* left = HEAP_GET(heap, instruction->args.at[0]); GroundBytecodeValue* right = HEAP_GET(heap, instruction->args.at[1]); *final = Ground.New.BytecodeValue(Ground.New.Value.Bool(left->as.Bool != right->as.Bool)); return CONTINUE; } NEG: { return CONTINUE; } SHIFT: { return CONTINUE; } STOI: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[1]); GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); char* status = NULL; *final = Ground.New.BytecodeValue(Ground.New.Value.Int(strtoll(in->as.String.cstr, &status, 0))); if (status != in->as.String.cstr) { Ground.Flags.error = true; Ground.Log.Error("Failed converting string to double"); } return CONTINUE; } STOD: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[1]); GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); char* status = NULL; *final = Ground.New.BytecodeValue(Ground.New.Value.Double(strtod(in->as.String.cstr, &status))); if (status != in->as.String.cstr) { Ground.Flags.error = true; Ground.Log.Error("Failed converting string to double"); } return CONTINUE; } ITOC: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[1]); GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); *final = Ground.New.BytecodeValue(Ground.New.Value.Char((char)in->as.Int)); return CONTINUE; } CTOI: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[1]); GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); *final = Ground.New.BytecodeValue(Ground.New.Value.Int((int64_t)in->as.Char)); return CONTINUE; } TOSTRING: { GroundBytecodeValue* final = HEAP_GET(heap, instruction->args.at[1]); GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); char buf[4096]; switch (in->type.type) { case GroundType_Int: { snprintf(buf, sizeof(buf), "%" PRId64, in->as.Int); break; } case GroundType_Double: { snprintf(buf, sizeof(buf), "%f", in->as.Double); break; } case GroundType_Char: { snprintf(buf, sizeof(buf), "%c", in->as.Char); break; } case GroundType_Bool: { snprintf(buf, sizeof(buf), in->as.Bool ? "true" : "false"); break; } case GroundType_String: { *in = Ground.New.BytecodeValue(Ground.New.Value.String(final->as.String)); return CONTINUE; } default: { // TODO: implement tostring for complex types snprintf(buf, sizeof(buf), ""); break; } } *in = Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(buf))); return CONTINUE; } FUN: { // make a copy of the current state and attach it to the function GroundBytecodeFunction* function = &HEAP_GET(heap, instruction->args.at[0])->as.Function; if (function->closure == NULL) {} return CONTINUE; } RETURN: { return RETURN( *HEAP_GET(heap, instruction->args.at[0]) ); } ENDFUN: { // no-op return CONTINUE; } CALL: { // call !function $value... &returnIdx GroundBytecodeFunction* function = &HEAP_GET(heap, instruction->args.at[0])->as.Function; if (function->isNativeFunction) { // TODO implement calling native functions return CONTINUE; } if (function->closure == NULL) { Ground.Log.Error("unexpected NULL closure in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } // amount of captured variables + amount of function args GroundSize heapSize = function->closure->len + function->args.count; GroundBytecodeHeap newHeap = { malloc(sizeof(GroundBytecodeValue) * heapSize), heapSize, heapSize }; if (newHeap.heap == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } // load function arguments into the heap for (GroundSize i = 0; i < function->args.count; i++) { newHeap.heap[i] = Ground.Copy.BytecodeValue(HEAP_GET(heap, i + 1)); // +1 because arg 1 is where fn args start } // function arguments are the first indexes, copy after for (GroundSize i = function->args.count; i < newHeap.len; i++) { newHeap.heap[i] = Ground.Copy.BytecodeValue(&function->closure->heap[i]); } // Now we run the thingy GroundBytecodeValue result = Ground.Bytecode.Program.execute(function->program.ground, &newHeap); // And store the result HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], result); return CONTINUE; } CALLMETHOD: { return CONTINUE; } STRUCT: { // no-op return CONTINUE; } ENDSTRUCT: { // no-op return CONTINUE; } INIT: { return CONTINUE; } GETFIELD: { return CONTINUE; } SETFIELD: { return CONTINUE; } USE: { return CONTINUE; } EXTERN: { return CONTINUE; } CREATELABEL: { // no-op return CONTINUE; } PAUSE: { return CONTINUE; } DROP: { GroundBytecodeValue* in = HEAP_GET(heap, instruction->args.at[0]); Ground.Free.BytecodeValue(in); *in = Ground.New.BytecodeValue(Ground.New.Value.Int(0)); return CONTINUE; } LICENSE: { return CONTINUE; } ERRORCMD: { return CONTINUE; } THROW: { return CONTINUE; } CATCH: { return CONTINUE; } Ground.Log.Error("operation fell through in Ground.Instruction.execute()"); Ground.Flags.error = true; return CONTINUE; }