#include "../../../include/ground.h" #ifndef _WIN32 #include "../../linenoise/linenoise.h" #endif #include #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 ffi_type* ffiTypeFromGroundType(GroundType* type) { switch (type->type) { case GroundType_Int: return &ffi_type_sint64; case GroundType_Double: return &ffi_type_double; case GroundType_Char: return &ffi_type_schar; case GroundType_Bool: return &ffi_type_uint8; case GroundType_String: return &ffi_type_pointer; default: return &ffi_type_pointer; // FIXME implement stuff for everything else } } 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: { #ifdef _WIN32 // TODO implement line input on Windows HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String("")))); return CONTINUE; #else 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; #endif } PRINT: { for (GroundSize i = 0; i < instruction->args.len; i++) { printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i]))); } return CONTINUE; } PRINTLN: { for (GroundSize i = 0; i < instruction->args.len; i++) { printf("%s ", Ground.Stringify.BytecodeValue(HEAP_GET(heap, instruction->args.at[i]))); } printf("\n"); return CONTINUE; } SET: { if (instruction->args.at[0] != instruction->args.at[1]) { GroundBytecodeValue copy = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[1])); Ground.Free.BytecodeValue(&heap->heap[instruction->args.at[0]]); HEAP_SET(heap, instruction->args.at[0], copy); } 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))); free(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* 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: { 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) { function->closure = malloc(sizeof(GroundBytecodeHeap)); if (function->closure == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } } else { for (GroundSize i = 0; i < function->closure->len; i++) { Ground.Free.BytecodeValue(&function->closure->heap[i]); } free(function->closure->heap); } function->closure->capacity = heap->capacity; function->closure->len = heap->len; function->closure->heap = malloc(sizeof(GroundBytecodeValue) * heap->capacity); if (function->closure->heap == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } for (GroundSize i = 0; i < heap->len; i++) { function->closure->heap[i] = Ground.Copy.BytecodeValue(&heap->heap[i]); } 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) { // Ensure we have the right amount and types of arguments first if (function->args.count != instruction->args.len - 2) { Ground.Log.Error("incorrect amount of arguments for native function in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } ffi_type* argTypes[function->args.count]; void* args[function->args.count]; for (GroundSize i = 0; i < function->args.count; i++) { if (function->args.at[i].as.type.type != HEAP_GET(heap, instruction->args.at[i+1])->type.type) { printf("%d != %d\n", function->args.at[i].as.type.type, HEAP_GET(heap, instruction->args.at[i+1])->type.type); Ground.Log.Error("incorrect type of argument for native function in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } argTypes[i] = ffiTypeFromGroundType(&function->args.at[i].as.type); switch (HEAP_GET(heap, instruction->args.at[i+1])->type.type) { case GroundType_Int: { args[i] = &HEAP_GET(heap, instruction->args.at[i+1])->as.Int; break; } case GroundType_Double: { args[i] = &HEAP_GET(heap, instruction->args.at[i+1])->as.Double; break; } case GroundType_Char: { args[i] = &HEAP_GET(heap, instruction->args.at[i+1])->as.Char; break; } case GroundType_Bool: { args[i] = &HEAP_GET(heap, instruction->args.at[i+1])->as.Bool; break; } case GroundType_String: { args[i] = &HEAP_GET(heap, instruction->args.at[i+1])->as.String.cstr; break; } default: { // FIXME implement non-basic types args[i] = HEAP_GET(heap, instruction->args.at[i+1]); break; } } } ffi_cif cif; ffi_status status = ffi_prep_cif( &cif, FFI_DEFAULT_ABI, function->args.count, ffiTypeFromGroundType(function->returnType), argTypes ); if (status != FFI_OK) { Ground.Log.Error("ffi CIF preperation failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } union { GroundInt Int; GroundDouble Double; GroundChar Char; GroundBool Bool; char* String; // TODO more stuff } result; ffi_call(&cif, function->program.native, &result, args); // Put return value into the scope switch (function->returnType->type) { case GroundType_Int: { HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], Ground.New.BytecodeValue(Ground.New.Value.Int(result.Int))); break; } case GroundType_Char: { HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], Ground.New.BytecodeValue(Ground.New.Value.Char(result.Char))); break; } case GroundType_Double: { HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], Ground.New.BytecodeValue(Ground.New.Value.Double(result.Double))); break; } case GroundType_Bool: { HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], Ground.New.BytecodeValue(Ground.New.Value.Bool(result.Bool))); break; } case GroundType_String: { HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(result.String)))); break; } default: break; // TODO more types } 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, instruction->args.at[i + 1])); } // function arguments are the first indexes, copy closure after for (GroundSize i = function->args.count; i < newHeap.len; i++) { newHeap.heap[i] = Ground.Copy.BytecodeValue(&function->closure->heap[i - function->args.count]); } // Now we run the thingy GroundBytecodeValue result = Ground.Bytecode.Program.execute(function->program.ground, &newHeap); // Deep copy the result before freeing the function's heap GroundBytecodeValue resultCopy = Ground.Copy.BytecodeValue(&result); // Clean up the new heap for (GroundSize i = 0; i < newHeap.len; i++) { Ground.Free.BytecodeValue(&newHeap.heap[i]); } free(newHeap.heap); // And store the result HEAP_SET(heap, instruction->args.at[instruction->args.len - 1], resultCopy); return CONTINUE; } CALLMETHOD: { return CONTINUE; } STRUCT: { GroundBytecodeStruct* gbs = &HEAP_GET(heap, instruction->args.at[0])->as.Struct; // struct &structName &field OP $value &field OP $value &field OP $value... // where: // &field is the field to initialise // OP is a numeric identifier: // 0 -> none // 1 -> init // 2 -> set // 3 -> attach closure to function // $value is either a type reference (OP=0), a value reference (OP=1), or none (OP=2) for (GroundSize i = 1; i < instruction->args.len; i++) { GroundSize offset = instruction->args.at[i]; i++; GroundSize op = instruction->args.at[i]; switch (op) { case 0: break; case 1: { i++; GroundBytecodeValue* type = HEAP_GET(heap, instruction->args.at[i]); switch (type->type.type) { case GroundType_Struct: { // TODO nested structs break; } case GroundType_CoreType: { switch (type->as.CoreType) { case GroundType_Int: { gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Int(0)); break; } case GroundType_Double: { gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Double(0.0)); break; } case GroundType_String: { gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(""))); break; } case GroundType_Char: { gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Char(0)); break; } case GroundType_Bool: { gbs->values[offset] = Ground.New.BytecodeValue(Ground.New.Value.Bool(false)); break; } default: break; // TODO implement all the other stuff } } } break; } case 2: { i++; gbs->values[offset] = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[i])); break; } case 3: { GroundBytecodeFunction* function = &gbs->values[offset].as.Function; if (function->closure == NULL) { function->closure = malloc(sizeof(GroundBytecodeHeap)); if (function->closure == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } } else { for (GroundSize i = 0; i < function->closure->len; i++) { Ground.Free.BytecodeValue(&function->closure->heap[i]); } free(function->closure->heap); } function->closure->capacity = heap->capacity; function->closure->len = heap->len; function->closure->heap = malloc(sizeof(GroundBytecodeValue) * heap->capacity); if (function->closure->heap == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } for (GroundSize i = 0; i < heap->len; i++) { function->closure->heap[i] = Ground.Copy.BytecodeValue(&heap->heap[i]); } break; } } } return CONTINUE; } ENDSTRUCT: { // no-op return CONTINUE; } INIT: { GroundBytecodeValue* type = HEAP_GET(heap, instruction->args.at[1]); switch (type->type.type) { case GroundType_Struct: { // copy the contents of the struct into the object GroundBytecodeObject object = { .size = type->as.Struct.size, .capacity = type->as.Struct.size, .values = malloc(sizeof(GroundBytecodeValue) * type->as.Struct.size) }; for (GroundSize i = 0; i < type->as.Struct.size; i++) { object.values[i] = Ground.Copy.BytecodeValue(&type->as.Struct.values[i]); } HEAP_SET(heap, instruction->args.at[0], ((GroundBytecodeValue) {.as.Object = object, .type = {GroundType_Object}})); break; } case GroundType_CoreType: { switch (type->as.CoreType) { case GroundType_Int: { HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Int(0))); break; } case GroundType_Double: { HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Double(0.0))); break; } case GroundType_String: { HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String("")))); break; } case GroundType_Char: { HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Char(0))); break; } case GroundType_Bool: { HEAP_SET(heap, instruction->args.at[0], Ground.New.BytecodeValue(Ground.New.Value.Bool(false))); break; } default: break; // TODO implement all the other stuff } } default: break; // should not be reached } return CONTINUE; } GETFIELD: { // args: [parent_offset, field0_offset, ..., fieldF_offset, output_offset] GroundBytecodeValue* current = HEAP_GET(heap, instruction->args.at[0]); for (GroundSize i = 1; i < instruction->args.len - 1; i++) { current = ¤t->as.Object.values[instruction->args.at[i]]; } GroundBytecodeValue* output = HEAP_GET(heap, instruction->args.at[instruction->args.len - 1]); Ground.Free.BytecodeValue(output); *output = Ground.Copy.BytecodeValue(current); return CONTINUE; } SETFIELD: { return CONTINUE; } USE: { return CONTINUE; } EXTERN: { // extern "libfile.so" "symbolName" !fnName -returnType -argType... void* handle = Ground.FFI.openSharedObject(HEAP_GET(heap, instruction->args.at[0])->as.String.cstr); if (Ground.Flags.error) { return CONTINUE; } void* function = Ground.FFI.getFunction(handle, HEAP_GET(heap, instruction->args.at[1])->as.String.cstr); if (Ground.Flags.error) { return CONTINUE; } GroundSize argsSize = instruction->args.len - 4; // subtract 4 for libfile, symbol name, fn name, return type GroundBytecodeFunction bf = { .isNativeFunction = true, .closure = NULL, .program.native = function, .args = { .at = malloc(sizeof(GroundFunctionArg) * argsSize), .capacity = argsSize, .count = argsSize }, .returnType = malloc(sizeof(GroundType)) }; if (bf.args.at == NULL || bf.returnType == NULL) { Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute"); Ground.Flags.error = true; return CONTINUE; } enum GroundTypeType returnTypeType = HEAP_GET(heap, instruction->args.at[3])->type.type; switch (returnTypeType) { case GroundType_Struct: { bf.returnType->type = GroundType_Object; // TODO implement structs break; } case GroundType_CoreType: { bf.returnType->type = HEAP_GET(heap, instruction->args.at[3])->as.CoreType; break; } default: { // should have been caught elsewhere, not really our problem break; } } for (size_t i = 0; i < argsSize; i++) { GroundBytecodeValue* argTypeVal = HEAP_GET(heap, instruction->args.at[i + 4]); switch (argTypeVal->type.type) { case GroundType_Struct: { bf.args.at[i].as.type.type = GroundType_Object; // TODO implement structs break; } case GroundType_CoreType: { bf.args.at[i].as.type.type = argTypeVal->as.CoreType; break; } default: { // should have been caught elsewhere, not really our problem break; } } } GroundBytecodeValue val = { .type = {GroundType_Function}, .as.Function = bf }; HEAP_SET(heap, instruction->args.at[2], val); 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; }