From 9733795e0e90c2b2b68caed1e7d10d0490783918 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sat, 6 Aug 2022 21:11:52 -0700 Subject: [PATCH 001/104] update readme to include FreeBSD Signed-off-by: Brian Downs --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 97956303..7592b9d7 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,32 @@ $ ./build/Dictu Refer to [Dictu Docker](https://github.com/dictu-lang/Dictu/blob/develop/Docker/README.md) +### FreeBSD Installation + +For a full installation, make sure `curl` and `linenoise` are installed. They can be installed from the commands below: + +```bash +$ pkg install -y curl linenoise-ng +``` + +The following variables need to be set/available to run `cmake` successfully. + +For Bourne compatible shells... + +```bash +export CPATH=/usr/local/include +export LIBRARY_PATH=/usr/local/lib +export LD_LIBRARY_PATH=/usr/local/lib +``` + +```bash +$ git clone -b master https://github.com/dictu-lang/Dictu.git +$ cd Dictu +$ cmake -DCMAKE_BUILD_TYPE=Release -B ./build +$ cmake --build ./build +$ ./dictu +``` + ## Extensions Dictu has a Visual Studio Code extension [here](https://marketplace.visualstudio.com/items?itemName=Dictu.dictuvsc) with the implementation located From 81818212e1bb50479b025320265643f65ab4c2e0 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 22 Aug 2022 23:49:12 +0100 Subject: [PATCH 002/104] Add new Object module and createFrom method --- src/optionals/object.c | 44 +++++++++++++++++++++++++++++++++++++++ src/optionals/object.h | 11 ++++++++++ src/optionals/optionals.c | 1 + src/optionals/optionals.h | 1 + 4 files changed, 57 insertions(+) create mode 100644 src/optionals/object.c create mode 100644 src/optionals/object.h diff --git a/src/optionals/object.c b/src/optionals/object.c new file mode 100644 index 00000000..d808ae5d --- /dev/null +++ b/src/optionals/object.c @@ -0,0 +1,44 @@ +#include "object.h" + +static Value objectCreateFrom(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "from() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0])) { + runtimeError(vm, "from() argument must be a string"); + return EMPTY_VAL; + } + + ObjString *classString = AS_STRING(args[0]); + + Value klass; + CallFrame *frame = &vm->frames[vm->frameCount - 1]; + + if (tableGet(&frame->closure->function->module->values, classString, &klass) && IS_CLASS(klass)) { + ObjInstance *instance = newInstance(vm, AS_CLASS(klass)); + return OBJ_VAL(instance); + } + + return NIL_VAL; +} + +Value createObjectModule(DictuVM *vm) { + ObjString *name = copyString(vm, "Object", 6); + push(vm, OBJ_VAL(name)); + ObjModule *module = newModule(vm, name); + push(vm, OBJ_VAL(module)); + + srand(time(NULL)); + + /** + * Define Object methods + */ + defineNative(vm, &module->values, "createFrom", objectCreateFrom); + + pop(vm); + pop(vm); + + return OBJ_VAL(module); +} \ No newline at end of file diff --git a/src/optionals/object.h b/src/optionals/object.h new file mode 100644 index 00000000..c2c64b7f --- /dev/null +++ b/src/optionals/object.h @@ -0,0 +1,11 @@ +#ifndef dictu_object_module_h +#define dictu_object_module_h + +#include + +#include "optionals.h" +#include "../vm/vm.h" + +Value createObjectModule(DictuVM *vm); + +#endif //dictu_object_module_h diff --git a/src/optionals/optionals.c b/src/optionals/optionals.c index b5e2f195..c6580d3d 100644 --- a/src/optionals/optionals.c +++ b/src/optionals/optionals.c @@ -16,6 +16,7 @@ BuiltinModules modules[] = { {"System", &createSystemModule, false}, {"UnitTest", &createUnitTestModule, true}, {"Inspect", &createInspectModule, false}, + {"Object", &createObjectModule, false}, #ifndef DISABLE_HTTP {"HTTP", &createHTTPModule, true}, #endif diff --git a/src/optionals/optionals.h b/src/optionals/optionals.h index 5572ccb9..38ebeb6d 100644 --- a/src/optionals/optionals.h +++ b/src/optionals/optionals.h @@ -18,6 +18,7 @@ #include "sqlite.h" #include "process.h" #include "inspect.h" +#include "object.h" #include "unittest/unittest.h" typedef Value (*BuiltinModule)(DictuVM *vm); From fed64023abc970f27581efdd1607f699fda993b3 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 24 Aug 2022 19:21:53 +0100 Subject: [PATCH 003/104] Wrap object in result type and handle case where class can't be found --- src/optionals/object.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/optionals/object.c b/src/optionals/object.c index d808ae5d..8477e364 100644 --- a/src/optionals/object.c +++ b/src/optionals/object.c @@ -18,10 +18,19 @@ static Value objectCreateFrom(DictuVM *vm, int argCount, Value *args) { if (tableGet(&frame->closure->function->module->values, classString, &klass) && IS_CLASS(klass)) { ObjInstance *instance = newInstance(vm, AS_CLASS(klass)); - return OBJ_VAL(instance); + return newResultSuccess(vm, OBJ_VAL(instance)); } - return NIL_VAL; + char *error = ALLOCATE(vm, char, classString->length + 26); + memcpy(error, classString->chars, classString->length); + memcpy(error + classString->length, " class could not be found", 25); + error[classString->length + 25] = '\0'; + + Value result = newResultError(vm, error); + + FREE_ARRAY(vm, char, error, classString->length + 26); + + return result; } Value createObjectModule(DictuVM *vm) { From 3ebf8121fb741ab77b48406d39f7059a429ab1db Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 24 Aug 2022 22:53:22 +0100 Subject: [PATCH 004/104] Add getClassRef and write createFrom in Dictu --- docs/docs/standard-lib/object.md | 49 ++++++++++++++++++++ scripts/generate.du | 3 +- src/optionals/object.c | 53 --------------------- src/optionals/object/object-source.h | 9 ++++ src/optionals/object/object.c | 69 ++++++++++++++++++++++++++++ src/optionals/object/object.du | 8 ++++ src/optionals/{ => object}/object.h | 4 +- src/optionals/optionals.c | 2 +- src/optionals/optionals.h | 2 +- tests/object/createFrom.du | 21 +++++++++ tests/object/import.du | 6 +++ 11 files changed, 168 insertions(+), 58 deletions(-) create mode 100644 docs/docs/standard-lib/object.md delete mode 100644 src/optionals/object.c create mode 100644 src/optionals/object/object-source.h create mode 100644 src/optionals/object/object.c create mode 100644 src/optionals/object/object.du rename src/optionals/{ => object}/object.h (75%) create mode 100644 tests/object/createFrom.du create mode 100644 tests/object/import.du diff --git a/docs/docs/standard-lib/object.md b/docs/docs/standard-lib/object.md new file mode 100644 index 00000000..704e6cb5 --- /dev/null +++ b/docs/docs/standard-lib/object.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Object +nav_order: 17 +parent: Standard Library +--- + +# Object +{: .no_toc } + +## Table of contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +--- + +## Object + +To make use of the Object module an import is required. + +```cs +import Object; +``` + +### Object.getClassRef(string) + +This method will attempt to get a class reference from the class name provided as a string. + +Returns a Result and unwraps to an Object upon success. + +```cs +class Test {} + +Object.getClassRef("Test"); // +``` + +### Object.createFrom(string) + +This method will attempt to create a new object from the class name provided as a string. + +Returns a Result and unwraps to an Object upon success. + +```cs +class Test {} + +Object.createFrom("Test"); // +``` diff --git a/scripts/generate.du b/scripts/generate.du index 2f12b70d..941efe65 100644 --- a/scripts/generate.du +++ b/scripts/generate.du @@ -31,7 +31,8 @@ var files = [ 'src/vm/datatypes/result/result', 'src/optionals/unittest/unittest', 'src/optionals/env/env', - 'src/optionals/http/http' + 'src/optionals/http/http', + 'src/optionals/object/object' ]; for (var i = 0; i < files.len(); i += 1) { diff --git a/src/optionals/object.c b/src/optionals/object.c deleted file mode 100644 index 8477e364..00000000 --- a/src/optionals/object.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "object.h" - -static Value objectCreateFrom(DictuVM *vm, int argCount, Value *args) { - if (argCount != 1) { - runtimeError(vm, "from() takes 1 argument (%d given)", argCount); - return EMPTY_VAL; - } - - if (!IS_STRING(args[0])) { - runtimeError(vm, "from() argument must be a string"); - return EMPTY_VAL; - } - - ObjString *classString = AS_STRING(args[0]); - - Value klass; - CallFrame *frame = &vm->frames[vm->frameCount - 1]; - - if (tableGet(&frame->closure->function->module->values, classString, &klass) && IS_CLASS(klass)) { - ObjInstance *instance = newInstance(vm, AS_CLASS(klass)); - return newResultSuccess(vm, OBJ_VAL(instance)); - } - - char *error = ALLOCATE(vm, char, classString->length + 26); - memcpy(error, classString->chars, classString->length); - memcpy(error + classString->length, " class could not be found", 25); - error[classString->length + 25] = '\0'; - - Value result = newResultError(vm, error); - - FREE_ARRAY(vm, char, error, classString->length + 26); - - return result; -} - -Value createObjectModule(DictuVM *vm) { - ObjString *name = copyString(vm, "Object", 6); - push(vm, OBJ_VAL(name)); - ObjModule *module = newModule(vm, name); - push(vm, OBJ_VAL(module)); - - srand(time(NULL)); - - /** - * Define Object methods - */ - defineNative(vm, &module->values, "createFrom", objectCreateFrom); - - pop(vm); - pop(vm); - - return OBJ_VAL(module); -} \ No newline at end of file diff --git a/src/optionals/object/object-source.h b/src/optionals/object/object-source.h new file mode 100644 index 00000000..f90a2bdb --- /dev/null +++ b/src/optionals/object/object-source.h @@ -0,0 +1,9 @@ +#define DICTU_OBJECT_SOURCE "def createFrom(className, ...arguments) {\n" \ +" import Object;\n" \ +"\n" \ +" return Object.__getClassRef(className).matchWrap(\n" \ +" def (klass) => klass(),\n" \ +" def (error) => error\n" \ +" );\n" \ +"}\n" \ + diff --git a/src/optionals/object/object.c b/src/optionals/object/object.c new file mode 100644 index 00000000..0b2bb61f --- /dev/null +++ b/src/optionals/object/object.c @@ -0,0 +1,69 @@ +#include "object.h" + +#include "object-source.h" + +static Value objectGetClassRefImpl(DictuVM *vm, int argCount, Value *args, bool internal) { + if (argCount != 1) { + runtimeError(vm, "getClassRef() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0])) { + runtimeError(vm, "getClassRef() argument must be a string"); + return EMPTY_VAL; + } + + ObjString *classString = AS_STRING(args[0]); + + Value klass; + CallFrame *frame; + if (internal) { + // -2 as we want to go to the callee site, not the Object.du code + frame = &vm->frames[vm->frameCount - 2]; + } else { + frame = &vm->frames[vm->frameCount - 1]; + } + + if (tableGet(&frame->closure->function->module->values, classString, &klass) && IS_CLASS(klass)) { + return newResultSuccess(vm, klass); + } + + char *error = ALLOCATE(vm, char, classString->length + 26); + memcpy(error, classString->chars, classString->length); + memcpy(error + classString->length, " class could not be found", 25); + error[classString->length + 25] = '\0'; + + Value result = newResultError(vm, error); + + FREE_ARRAY(vm, char, error, classString->length + 26); + + return result; +} + +static Value objectGetClassRef(DictuVM *vm, int argCount, Value *args) { + return objectGetClassRefImpl(vm, argCount, args, false); +} + +static Value objectGetClassRefInternal(DictuVM *vm, int argCount, Value *args) { + return objectGetClassRefImpl(vm, argCount, args, true); +} + +Value createObjectModule(DictuVM *vm) { + ObjClosure *closure = compileModuleToClosure(vm, "Object", DICTU_OBJECT_SOURCE); + + if (closure == NULL) { + return EMPTY_VAL; + } + + push(vm, OBJ_VAL(closure)); + + /** + * Define Object methods + */ + defineNative(vm, &closure->function->module->values, "__getClassRef", objectGetClassRefInternal); + defineNative(vm, &closure->function->module->values, "getClassRef", objectGetClassRef); + + pop(vm); + + return OBJ_VAL(closure); +} \ No newline at end of file diff --git a/src/optionals/object/object.du b/src/optionals/object/object.du new file mode 100644 index 00000000..387c90d8 --- /dev/null +++ b/src/optionals/object/object.du @@ -0,0 +1,8 @@ +import Object; + +def createFrom(className, ...arguments) { + return Object.__getClassRef(className).matchWrap( + def (klass) => klass(), + def (error) => error + ); +} \ No newline at end of file diff --git a/src/optionals/object.h b/src/optionals/object/object.h similarity index 75% rename from src/optionals/object.h rename to src/optionals/object/object.h index c2c64b7f..c8384cf8 100644 --- a/src/optionals/object.h +++ b/src/optionals/object/object.h @@ -3,8 +3,8 @@ #include -#include "optionals.h" -#include "../vm/vm.h" +#include "../optionals.h" +#include "../../vm/vm.h" Value createObjectModule(DictuVM *vm); diff --git a/src/optionals/optionals.c b/src/optionals/optionals.c index c6580d3d..d18cf319 100644 --- a/src/optionals/optionals.c +++ b/src/optionals/optionals.c @@ -16,7 +16,7 @@ BuiltinModules modules[] = { {"System", &createSystemModule, false}, {"UnitTest", &createUnitTestModule, true}, {"Inspect", &createInspectModule, false}, - {"Object", &createObjectModule, false}, + {"Object", &createObjectModule, true}, #ifndef DISABLE_HTTP {"HTTP", &createHTTPModule, true}, #endif diff --git a/src/optionals/optionals.h b/src/optionals/optionals.h index 38ebeb6d..28c825ed 100644 --- a/src/optionals/optionals.h +++ b/src/optionals/optionals.h @@ -18,7 +18,7 @@ #include "sqlite.h" #include "process.h" #include "inspect.h" -#include "object.h" +#include "object/object.h" #include "unittest/unittest.h" typedef Value (*BuiltinModule)(DictuVM *vm); diff --git a/tests/object/createFrom.du b/tests/object/createFrom.du new file mode 100644 index 00000000..7e81761c --- /dev/null +++ b/tests/object/createFrom.du @@ -0,0 +1,21 @@ +/** + * createFrom.du + * + * Testing the Object.createFrom method + */ +from UnitTest import UnitTest; + +import Object; + +class Test {} + +class TestObjectCreateFrom < UnitTest { + testObjectCreateFrom() { + this.assertSuccess(Object.createFrom('Test')); + this.assertError(Object.createFrom('Unknown')); + + this.assertType(Object.createFrom('Test'), 'Test'); + } +} + +TestObjectCreateFrom().run(); \ No newline at end of file diff --git a/tests/object/import.du b/tests/object/import.du new file mode 100644 index 00000000..706e10de --- /dev/null +++ b/tests/object/import.du @@ -0,0 +1,6 @@ +/** + * import.du + * + * General import file for all the Object module tests + */ + From 2782e19e5dcfc7d567649cfa6f1fe37d2e994822 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 17:55:37 +0100 Subject: [PATCH 005/104] Get unpacking working for functions --- src/vm/compiler.c | 38 ++++++++++++++++++++++++++++++-------- src/vm/debug.c | 2 ++ src/vm/opcodes.h | 2 ++ src/vm/vm.c | 24 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index a6e1fbc8..a0c9fe6b 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -433,10 +433,15 @@ static void defineVariable(Compiler *compiler, uint8_t global, bool constant) { } } -static int argumentList(Compiler *compiler) { +static int argumentList(Compiler *compiler, bool *unpack) { int argCount = 0; + if (!check(compiler, TOKEN_RIGHT_PAREN)) { do { + if (match(compiler, TOKEN_STAR)) { + *unpack = true; + } + expression(compiler); argCount++; @@ -641,9 +646,17 @@ static void ternary(Compiler *compiler, Token previousToken, bool canAssign) { static void call(Compiler *compiler, Token previousToken, bool canAssign) { UNUSED(previousToken); UNUSED(canAssign); + bool unpack = false; - int argCount = argumentList(compiler); - emitBytes(compiler, OP_CALL, argCount); + int argCount = argumentList(compiler, &unpack); + + if (unpack) { + emitConstant(compiler, NUMBER_VAL(argCount)); + emitByte(compiler, OP_UNPACK); + emitByte(compiler, OP_CALL_UNPACK); + } else { + emitBytes(compiler, OP_CALL, argCount); + } } static bool privatePropertyExists(Token name, Compiler *compiler) { @@ -662,7 +675,9 @@ static void dot(Compiler *compiler, Token previousToken, bool canAssign) { Token identifier = compiler->parser->previous; if (match(compiler, TOKEN_LEFT_PAREN)) { - int argCount = argumentList(compiler); + bool unpack = false; + + int argCount = argumentList(compiler, &unpack); if (compiler->class != NULL && (previousToken.type == TOKEN_THIS || identifiersEqual(&previousToken, &compiler->class->name))) { emitBytes(compiler, OP_INVOKE_INTERNAL, argCount); } else { @@ -1300,11 +1315,17 @@ static void super_(Compiler *compiler, bool canAssign) { namedVariable(compiler, syntheticToken("this"), false); if (match(compiler, TOKEN_LEFT_PAREN)) { - int argCount = argumentList(compiler); + bool unpack = false; - pushSuperclass(compiler); - emitBytes(compiler, OP_SUPER, argCount); - emitByte(compiler, name); + int argCount = argumentList(compiler, &unpack); + + if (unpack) { + // TODO: + } else { + pushSuperclass(compiler); + emitBytes(compiler, OP_SUPER, argCount); + emitByte(compiler, name); + } } else { pushSuperclass(compiler); emitBytes(compiler, OP_GET_SUPER, name); @@ -1989,6 +2010,7 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_NEW_DICT: case OP_CLOSE_FILE: case OP_MULTI_CASE: + case OP_UNPACK: return 1; case OP_DEFINE_OPTIONAL: diff --git a/src/vm/debug.c b/src/vm/debug.c index 8906119c..0cf881ac 100644 --- a/src/vm/debug.c +++ b/src/vm/debug.c @@ -296,6 +296,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return constantInstruction("OP_CLOSE_FILE", chunk, offset); case OP_BREAK: return simpleInstruction("OP_BREAK", offset); + case OP_UNPACK: + return simpleInstruction("OP_UNPACK", offset); default: printf("Unknown opcode %d\n", instruction); return offset + 1; diff --git a/src/vm/opcodes.h b/src/vm/opcodes.h index c392fb24..717a19e8 100644 --- a/src/vm/opcodes.h +++ b/src/vm/opcodes.h @@ -47,6 +47,7 @@ OPCODE(JUMP_IF_FALSE) OPCODE(JUMP_IF_NIL) OPCODE(LOOP) OPCODE(CALL) +OPCODE(CALL_UNPACK) OPCODE(INVOKE) OPCODE(INVOKE_INTERNAL) OPCODE(SUPER) @@ -70,6 +71,7 @@ OPCODE(IMPORT_END) OPCODE(USE) OPCODE(OPEN_FILE) OPCODE(BREAK) +OPCODE(UNPACK) OPCODE(CLOSE_FILE) OPCODE(BITWISE_AND) OPCODE(BITWISE_XOR) diff --git a/src/vm/vm.c b/src/vm/vm.c index c192b86a..a424cdbf 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1529,6 +1529,30 @@ static DictuInterpretResult run(DictuVM *vm) { DISPATCH(); } + CASE_CODE(UNPACK): { + int argCount = AS_NUMBER(pop(vm)); + ObjList *list = AS_LIST(pop(vm)); + + for (int i = 0; i < list->values.count; ++i) { + push(vm, list->values.values[i]); + } + + push(vm, NUMBER_VAL(argCount + (list->values.count - 1))); + + DISPATCH(); + } + + CASE_CODE(CALL_UNPACK): { + int argCount = AS_NUMBER(pop(vm)); + frame->ip = ip; + if (!callValue(vm, peek(vm, argCount), argCount)) { + return INTERPRET_RUNTIME_ERROR; + } + frame = &vm->frames[vm->frameCount - 1]; + ip = frame->ip; + DISPATCH(); + } + CASE_CODE(BREAK): { DISPATCH(); } From e3cdf961f28dce39c74d1b0246114dead5b06316 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 18:32:52 +0100 Subject: [PATCH 006/104] Get unpacking working across the board --- src/vm/compiler.c | 33 ++++++++---------- src/vm/debug.c | 14 ++++++-- src/vm/vm.c | 88 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index a0c9fe6b..49114fd1 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -650,13 +650,8 @@ static void call(Compiler *compiler, Token previousToken, bool canAssign) { int argCount = argumentList(compiler, &unpack); - if (unpack) { - emitConstant(compiler, NUMBER_VAL(argCount)); - emitByte(compiler, OP_UNPACK); - emitByte(compiler, OP_CALL_UNPACK); - } else { - emitBytes(compiler, OP_CALL, argCount); - } + emitBytes(compiler, OP_CALL, argCount); + emitByte(compiler, unpack); } static bool privatePropertyExists(Token name, Compiler *compiler) { @@ -683,7 +678,10 @@ static void dot(Compiler *compiler, Token previousToken, bool canAssign) { } else { emitBytes(compiler, OP_INVOKE, argCount); } - emitByte(compiler, name); + + // printf("unpack - %d", unpack); + + emitBytes(compiler, name, unpack); return; } @@ -1316,16 +1314,11 @@ static void super_(Compiler *compiler, bool canAssign) { if (match(compiler, TOKEN_LEFT_PAREN)) { bool unpack = false; - int argCount = argumentList(compiler, &unpack); - if (unpack) { - // TODO: - } else { - pushSuperclass(compiler); - emitBytes(compiler, OP_SUPER, argCount); - emitByte(compiler, name); - } + pushSuperclass(compiler); + emitBytes(compiler, OP_SUPER, argCount); + emitBytes(compiler, name, unpack); } else { pushSuperclass(compiler); emitBytes(compiler, OP_GET_SUPER, name); @@ -2003,7 +1996,6 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_SET_INIT_PROPERTIES: case OP_SET_PRIVATE_INIT_PROPERTIES: case OP_GET_SUPER: - case OP_CALL: case OP_METHOD: case OP_IMPORT: case OP_NEW_LIST: @@ -2019,14 +2011,17 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_JUMP_IF_NIL: case OP_JUMP_IF_FALSE: case OP_LOOP: - case OP_INVOKE: - case OP_INVOKE_INTERNAL: case OP_SUPER: case OP_CLASS: case OP_SUBCLASS: case OP_IMPORT_BUILTIN: + case OP_CALL: return 2; + case OP_INVOKE: + case OP_INVOKE_INTERNAL: + return 3; + case OP_IMPORT_BUILTIN_VARIABLE: { int argCount = code[ip + 2]; diff --git a/src/vm/debug.c b/src/vm/debug.c index 0cf881ac..27037ce3 100644 --- a/src/vm/debug.c +++ b/src/vm/debug.c @@ -22,14 +22,22 @@ static int constantInstruction(const char *name, Chunk *chunk, return offset + 2; } +static int callInstruction(const char *name, Chunk *chunk, int offset) { + uint8_t argCount = chunk->code[offset + 1]; + uint8_t unpack = chunk->code[offset + 2]; + printf("%-16s (%d args) Unpack - %d '", name, argCount, unpack); + return offset + 3; +} + static int invokeInstruction(const char* name, Chunk* chunk, int offset) { uint8_t argCount = chunk->code[offset + 1]; uint8_t constant = chunk->code[offset + 2]; - printf("%-16s (%d args) %4d '", name, argCount, constant); + uint8_t unpack = chunk->code[offset + 3]; + printf("%-16s (%d args) %4d unpack - %d '", name, argCount, constant, unpack); printValue(chunk->constants.values[constant]); printf("'\n"); - return offset + 3; + return offset + 4; } static int importFromInstruction(const char *name, Chunk *chunk, @@ -242,7 +250,7 @@ int disassembleInstruction(Chunk *chunk, int offset) { case OP_NEW_DICT: return byteInstruction("OP_NEW_DICT", chunk, offset); case OP_CALL: - return byteInstruction("OP_CALL", chunk, offset); + return callInstruction("OP_CALL", chunk, offset); case OP_INVOKE_INTERNAL: return invokeInstruction("OP_INVOKE_INTERNAL", chunk, offset); case OP_INVOKE: diff --git a/src/vm/vm.c b/src/vm/vm.c index a424cdbf..a7bcff76 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -264,8 +264,18 @@ static bool call(DictuVM *vm, ObjClosure *closure, int argCount) { return true; } -static bool callValue(DictuVM *vm, Value callee, int argCount) { +static bool callValue(DictuVM *vm, Value callee, int argCount, bool unpack) { if (IS_OBJ(callee)) { + if (unpack) { + ObjList *list = AS_LIST(pop(vm)); + + for (int i = 0; i < list->values.count; ++i) { + push(vm, list->values.values[i]); + } + + argCount += (list->values.count - 1); + } + switch (OBJ_TYPE(callee)) { case OBJ_BOUND_METHOD: { ObjBoundMethod *bound = AS_BOUND_METHOD(callee); @@ -301,6 +311,9 @@ static bool callValue(DictuVM *vm, Value callee, int argCount) { case OBJ_CLOSURE: { vm->stackTop[-argCount - 1] = callee; + + + return call(vm, AS_CLOSURE(callee), argCount); } @@ -343,7 +356,18 @@ static bool callNativeMethod(DictuVM *vm, Value method, int argCount) { } static bool invokeFromClass(DictuVM *vm, ObjClass *klass, ObjString *name, - int argCount) { + int argCount, bool unpack) { + if (unpack) { + ObjList *list = AS_LIST(pop(vm)); + + for (int i = 0; i < list->values.count; ++i) { + push(vm, list->values.values[i]); + } + + argCount += (list->values.count - 1); + unpack = false; + } + // Look for the method. Value method; if (!tableGet(&klass->publicMethods, name, &method)) { @@ -359,9 +383,20 @@ static bool invokeFromClass(DictuVM *vm, ObjClass *klass, ObjString *name, return call(vm, AS_CLOSURE(method), argCount); } -static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount) { +static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount, bool unpack) { Value receiver = peek(vm, argCount); + if (unpack) { + ObjList *list = AS_LIST(pop(vm)); + + for (int i = 0; i < list->values.count; ++i) { + push(vm, list->values.values[i]); + } + + argCount += (list->values.count - 1); + unpack = false; + } + if (IS_INSTANCE(receiver)) { ObjInstance *instance = AS_INSTANCE(receiver); @@ -383,7 +418,7 @@ static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount) { // Look for a field which may shadow a method. if (tableGet(&instance->publicFields, name, &value)) { vm->stackTop[-argCount - 1] = value; - return callValue(vm, value, argCount); + return callValue(vm, value, argCount, unpack); } } else if (IS_CLASS(receiver)) { ObjClass *instance = AS_CLASS(receiver); @@ -399,7 +434,7 @@ static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount) { return false; } - return callValue(vm, method, argCount); + return callValue(vm, method, argCount, unpack); } if (tableGet(&instance->publicMethods, name, &method)) { @@ -413,7 +448,7 @@ static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount) { return false; } - return callValue(vm, method, argCount); + return callValue(vm, method, argCount, unpack); } if (tableGet(&vm->classMethods, name, &method)) { @@ -425,9 +460,20 @@ static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount) { return false; } -static bool invoke(DictuVM *vm, ObjString *name, int argCount) { +static bool invoke(DictuVM *vm, ObjString *name, int argCount, bool unpack) { Value receiver = peek(vm, argCount); + if (unpack) { + ObjList *list = AS_LIST(pop(vm)); + + for (int i = 0; i < list->values.count; ++i) { + push(vm, list->values.values[i]); + } + + argCount += (list->values.count - 1); + unpack = false; + } + if (!IS_OBJ(receiver)) { if (IS_NUMBER(receiver)) { Value value; @@ -464,7 +510,7 @@ static bool invoke(DictuVM *vm, ObjString *name, int argCount) { runtimeError(vm, "Undefined property '%s'.", name->chars); return false; } - return callValue(vm, value, argCount); + return callValue(vm, value, argCount, unpack); } case OBJ_CLASS: { @@ -481,7 +527,7 @@ static bool invoke(DictuVM *vm, ObjString *name, int argCount) { return false; } - return callValue(vm, method, argCount); + return callValue(vm, method, argCount, unpack); } if (tableGet(&vm->classMethods, name, &method)) { @@ -509,7 +555,7 @@ static bool invoke(DictuVM *vm, ObjString *name, int argCount) { // Look for a field which may shadow a method. if (tableGet(&instance->publicFields, name, &value)) { vm->stackTop[-argCount - 1] = value; - return callValue(vm, value, argCount); + return callValue(vm, value, argCount, unpack); } if (tableGet(&instance->klass->privateMethods, name, &value)) { @@ -628,7 +674,7 @@ static bool invoke(DictuVM *vm, ObjString *name, int argCount) { Value value; if (tableGet(&enumObj->values, name, &value)) { - return callValue(vm, value, argCount); + return callValue(vm, value, argCount, false); } runtimeError(vm, "'%s' enum has no property '%s'.", enumObj->name->chars, name->chars); @@ -1545,7 +1591,7 @@ static DictuInterpretResult run(DictuVM *vm) { CASE_CODE(CALL_UNPACK): { int argCount = AS_NUMBER(pop(vm)); frame->ip = ip; - if (!callValue(vm, peek(vm, argCount), argCount)) { + if (!callValue(vm, peek(vm, argCount), argCount, false)) { return INTERPRET_RUNTIME_ERROR; } frame = &vm->frames[vm->frameCount - 1]; @@ -2024,8 +2070,10 @@ static DictuInterpretResult run(DictuVM *vm) { CASE_CODE(CALL): { int argCount = READ_BYTE(); + bool unpack = READ_BYTE(); + frame->ip = ip; - if (!callValue(vm, peek(vm, argCount), argCount)) { + if (!callValue(vm, peek(vm, argCount), argCount, unpack)) { return INTERPRET_RUNTIME_ERROR; } frame = &vm->frames[vm->frameCount - 1]; @@ -2036,8 +2084,10 @@ static DictuInterpretResult run(DictuVM *vm) { CASE_CODE(INVOKE): { int argCount = READ_BYTE(); ObjString *method = READ_STRING(); + bool unpack = READ_BYTE(); + frame->ip = ip; - if (!invoke(vm, method, argCount)) { + if (!invoke(vm, method, argCount, unpack)) { return INTERPRET_RUNTIME_ERROR; } frame = &vm->frames[vm->frameCount - 1]; @@ -2048,8 +2098,10 @@ static DictuInterpretResult run(DictuVM *vm) { CASE_CODE(INVOKE_INTERNAL): { int argCount = READ_BYTE(); ObjString *method = READ_STRING(); + bool unpack = READ_BYTE(); + frame->ip = ip; - if (!invokeInternal(vm, method, argCount)) { + if (!invokeInternal(vm, method, argCount, unpack)) { return INTERPRET_RUNTIME_ERROR; } frame = &vm->frames[vm->frameCount - 1]; @@ -2060,9 +2112,11 @@ static DictuInterpretResult run(DictuVM *vm) { CASE_CODE(SUPER): { int argCount = READ_BYTE(); ObjString *method = READ_STRING(); + bool unpack = READ_BYTE(); + frame->ip = ip; ObjClass *superclass = AS_CLASS(pop(vm)); - if (!invokeFromClass(vm, superclass, method, argCount)) { + if (!invokeFromClass(vm, superclass, method, argCount, unpack)) { return INTERPRET_RUNTIME_ERROR; } frame = &vm->frames[vm->frameCount - 1]; @@ -2271,7 +2325,7 @@ DictuInterpretResult dictuInterpret(DictuVM *vm, char *moduleName, char *source) ObjClosure *closure = newClosure(vm, function); pop(vm); push(vm, OBJ_VAL(closure)); - callValue(vm, OBJ_VAL(closure), 0); + callValue(vm, OBJ_VAL(closure), 0, false); DictuInterpretResult result = run(vm); return result; From 5ce9f10182e817a444a53ec1b3af56496ff29e51 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 18:33:26 +0100 Subject: [PATCH 007/104] Remove unpack opcode --- src/vm/compiler.c | 1 - src/vm/debug.c | 2 -- src/vm/opcodes.h | 1 - src/vm/vm.c | 13 ------------- 4 files changed, 17 deletions(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 49114fd1..726e6d62 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -2002,7 +2002,6 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_NEW_DICT: case OP_CLOSE_FILE: case OP_MULTI_CASE: - case OP_UNPACK: return 1; case OP_DEFINE_OPTIONAL: diff --git a/src/vm/debug.c b/src/vm/debug.c index 27037ce3..4b72b197 100644 --- a/src/vm/debug.c +++ b/src/vm/debug.c @@ -304,8 +304,6 @@ int disassembleInstruction(Chunk *chunk, int offset) { return constantInstruction("OP_CLOSE_FILE", chunk, offset); case OP_BREAK: return simpleInstruction("OP_BREAK", offset); - case OP_UNPACK: - return simpleInstruction("OP_UNPACK", offset); default: printf("Unknown opcode %d\n", instruction); return offset + 1; diff --git a/src/vm/opcodes.h b/src/vm/opcodes.h index 717a19e8..8cd167ad 100644 --- a/src/vm/opcodes.h +++ b/src/vm/opcodes.h @@ -71,7 +71,6 @@ OPCODE(IMPORT_END) OPCODE(USE) OPCODE(OPEN_FILE) OPCODE(BREAK) -OPCODE(UNPACK) OPCODE(CLOSE_FILE) OPCODE(BITWISE_AND) OPCODE(BITWISE_XOR) diff --git a/src/vm/vm.c b/src/vm/vm.c index a7bcff76..aeecea8d 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1575,19 +1575,6 @@ static DictuInterpretResult run(DictuVM *vm) { DISPATCH(); } - CASE_CODE(UNPACK): { - int argCount = AS_NUMBER(pop(vm)); - ObjList *list = AS_LIST(pop(vm)); - - for (int i = 0; i < list->values.count; ++i) { - push(vm, list->values.values[i]); - } - - push(vm, NUMBER_VAL(argCount + (list->values.count - 1))); - - DISPATCH(); - } - CASE_CODE(CALL_UNPACK): { int argCount = AS_NUMBER(pop(vm)); frame->ip = ip; From 08dff063cbe0fabd169726a38adde7a638546d55 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 18:42:43 +0100 Subject: [PATCH 008/104] Change unpack operator to ... to be consistent with variadic --- src/vm/compiler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 726e6d62..f0be193f 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -438,7 +438,7 @@ static int argumentList(Compiler *compiler, bool *unpack) { if (!check(compiler, TOKEN_RIGHT_PAREN)) { do { - if (match(compiler, TOKEN_STAR)) { + if (match(compiler, TOKEN_DOT_DOT_DOT)) { *unpack = true; } From 0197f7782d625742a07982d2bbc5afac318b8774 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 23:01:59 +0100 Subject: [PATCH 009/104] Add checks for arg unpacking and write some docs --- docs/docs/built-ins.md | 4 +-- docs/docs/functions.md | 17 ++++++++++++ src/vm/compiler.c | 4 +++ src/vm/vm.c | 60 +++++++++++++++--------------------------- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/docs/docs/built-ins.md b/docs/docs/built-ins.md index ca934094..8f84a4bc 100644 --- a/docs/docs/built-ins.md +++ b/docs/docs/built-ins.md @@ -25,7 +25,7 @@ The path name of the compilation unit. Global functions which are built into Dictu. -### print(...values...) +### print(...values) Prints a given list of values to stdout. @@ -35,7 +35,7 @@ print("test"); // "test" print(10, "test", nil, true); // 10, "test", nil, true ``` -### printError(...values...) +### printError(...values) Prints a given list of values to stderr. diff --git a/docs/docs/functions.md b/docs/docs/functions.md index e534ebbf..c7e58009 100644 --- a/docs/docs/functions.md +++ b/docs/docs/functions.md @@ -189,4 +189,21 @@ class BadClass { def badFunction(a=5, ...x) { // ... } +``` + +### Argument Unpacking + +Sometimes you may have a list of values and you wish to pass them all to a function. Rather than having +to loop over a list within the function and pull out singular values, you can unpack this list at the call site. + +Note: This will work on built-ins, class constructors and methods along with functions. + +``` +const myList = [1, 2, 3]; + +def printMyList(a, b, c) { + print(a, b, c); +} + +printMyList(...myList); // 1 2 3 ``` \ No newline at end of file diff --git a/src/vm/compiler.c b/src/vm/compiler.c index f0be193f..825afc71 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -438,6 +438,10 @@ static int argumentList(Compiler *compiler, bool *unpack) { if (!check(compiler, TOKEN_RIGHT_PAREN)) { do { + if (*unpack) { + errorAtCurrent(compiler->parser, "Value unpacking must be the last argument."); + } + if (match(compiler, TOKEN_DOT_DOT_DOT)) { *unpack = true; } diff --git a/src/vm/vm.c b/src/vm/vm.c index aeecea8d..ddd60c91 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -33,6 +33,23 @@ static void resetStack(DictuVM *vm) { vm->compiler = NULL; } +#define HANDLE_UNPACK \ + if (unpack) { \ + if (!IS_LIST(peek(vm, 0))) { \ + runtimeError(vm, "Attempted to unpack a value that is not a list"); \ + return false; \ + } \ + \ + ObjList *list = AS_LIST(pop(vm)); \ + \ + for (int i = 0; i < list->values.count; ++i) { \ + push(vm, list->values.values[i]); \ + } \ + \ + argCount += (list->values.count - 1); \ + unpack = false; \ + } + void runtimeError(DictuVM *vm, const char *format, ...) { for (int i = vm->frameCount - 1; i >= 0; i--) { CallFrame *frame = &vm->frames[i]; @@ -266,15 +283,7 @@ static bool call(DictuVM *vm, ObjClosure *closure, int argCount) { static bool callValue(DictuVM *vm, Value callee, int argCount, bool unpack) { if (IS_OBJ(callee)) { - if (unpack) { - ObjList *list = AS_LIST(pop(vm)); - - for (int i = 0; i < list->values.count; ++i) { - push(vm, list->values.values[i]); - } - - argCount += (list->values.count - 1); - } + HANDLE_UNPACK switch (OBJ_TYPE(callee)) { case OBJ_BOUND_METHOD: { @@ -357,16 +366,7 @@ static bool callNativeMethod(DictuVM *vm, Value method, int argCount) { static bool invokeFromClass(DictuVM *vm, ObjClass *klass, ObjString *name, int argCount, bool unpack) { - if (unpack) { - ObjList *list = AS_LIST(pop(vm)); - - for (int i = 0; i < list->values.count; ++i) { - push(vm, list->values.values[i]); - } - - argCount += (list->values.count - 1); - unpack = false; - } + HANDLE_UNPACK // Look for the method. Value method; @@ -386,16 +386,7 @@ static bool invokeFromClass(DictuVM *vm, ObjClass *klass, ObjString *name, static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount, bool unpack) { Value receiver = peek(vm, argCount); - if (unpack) { - ObjList *list = AS_LIST(pop(vm)); - - for (int i = 0; i < list->values.count; ++i) { - push(vm, list->values.values[i]); - } - - argCount += (list->values.count - 1); - unpack = false; - } + HANDLE_UNPACK if (IS_INSTANCE(receiver)) { ObjInstance *instance = AS_INSTANCE(receiver); @@ -463,16 +454,7 @@ static bool invokeInternal(DictuVM *vm, ObjString *name, int argCount, bool unpa static bool invoke(DictuVM *vm, ObjString *name, int argCount, bool unpack) { Value receiver = peek(vm, argCount); - if (unpack) { - ObjList *list = AS_LIST(pop(vm)); - - for (int i = 0; i < list->values.count; ++i) { - push(vm, list->values.values[i]); - } - - argCount += (list->values.count - 1); - unpack = false; - } + HANDLE_UNPACK if (!IS_OBJ(receiver)) { if (IS_NUMBER(receiver)) { From 5c5f47dd7dc97c951285bded507c06f7381f0942 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 23:09:08 +0100 Subject: [PATCH 010/104] Add unpack tests --- tests/operators/unpack.du | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/operators/unpack.du diff --git a/tests/operators/unpack.du b/tests/operators/unpack.du new file mode 100644 index 00000000..9cd5552a --- /dev/null +++ b/tests/operators/unpack.du @@ -0,0 +1,81 @@ +/** + * unpack.du + * + * Testing unpack operator + */ +from UnitTest import UnitTest; + +import Math; + +class UnpackOperatorTest < UnitTest { + testUnpackOperatorOnBuiltIn() { + this.assertEquals(Math.sum(...[1, 2, 3]), 6); + } + + testUnpackOperatorOnFunction() { + const f = def (a, b, c) => a + b + c; + + this.assertEquals(f(...[1, 2, 3]), 6); + } + + testUnpackOperatorOnFunctionWithPositional() { + const f = def (a, b, c) => a + b + c; + + this.assertEquals(f(1, 2, ...[3]), 6); + this.assertEquals(f(1, ...[2, 3]), 6); + } + + testUnpackOperatorOnFunctionWithOptional() { + const f = def (a, b=10, c=20) => a + b + c; + + this.assertEquals(f(...[1]), 31); + this.assertEquals(f(...[1, 2]), 23); + this.assertEquals(f(...[1, 2, 3]), 6); + } + + testUnpackOperatorOnVariadicFunction() { + const f = def (...a) => a; + + this.assertEquals(f(...[1, 2, 3]), [1, 2, 3]); + } + + testUnpackOperatorOnClass() { + class Test { + init(var a, var b) {} + } + + const obj = Test(...[1, 2]); + + this.assertEquals(obj.a, 1); + this.assertEquals(obj.b, 2); + } + + testUnpackOperatorOnClassWithPositional() { + class Test { + init(var a, var b) {} + } + + const obj = Test(1, ...[2]); + + this.assertEquals(obj.a, 1); + this.assertEquals(obj.b, 2); + } + + testUnpackOperatorOnClassWithOptional() { + class Test { + init(var a=10, var b=20) {} + } + + const obj = Test(...[1, 2]); + + this.assertEquals(obj.a, 1); + this.assertEquals(obj.b, 2); + + const obj1 = Test(...[1]); + + this.assertEquals(obj1.a, 1); + this.assertEquals(obj1.b, 20); + } +} + +UnpackOperatorTest().run(); \ No newline at end of file From b5fed5030cdc148bb5b530b502f8145aa5139a62 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 23:11:58 +0100 Subject: [PATCH 011/104] Remove printf call --- src/vm/compiler.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 825afc71..f8ff27cb 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -683,8 +683,6 @@ static void dot(Compiler *compiler, Token previousToken, bool canAssign) { emitBytes(compiler, OP_INVOKE, argCount); } - // printf("unpack - %d", unpack); - emitBytes(compiler, name, unpack); return; } From ba72e05fc8c7db9f909f276bf77f906f7c9401e8 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 25 Aug 2022 23:13:07 +0100 Subject: [PATCH 012/104] Correct byte count for OP_SUPER --- src/vm/compiler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index f8ff27cb..9fbefe97 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -2012,7 +2012,6 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_JUMP_IF_NIL: case OP_JUMP_IF_FALSE: case OP_LOOP: - case OP_SUPER: case OP_CLASS: case OP_SUBCLASS: case OP_IMPORT_BUILTIN: @@ -2021,6 +2020,7 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_INVOKE: case OP_INVOKE_INTERNAL: + case OP_SUPER: return 3; case OP_IMPORT_BUILTIN_VARIABLE: { From e2ae7bab859719fb7d13976d7d925341529b8259 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 2 Sep 2022 20:39:57 +0100 Subject: [PATCH 013/104] Only pop repl in top level --- src/vm/compiler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 9fbefe97..f530891e 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -1935,7 +1935,7 @@ static void expressionStatement(Compiler *compiler) { expression(compiler); consume(compiler, TOKEN_SEMICOLON, "Expect ';' after expression."); - if (compiler->parser->vm->repl && t != TOKEN_EQUAL) { + if (compiler->parser->vm->repl && t != TOKEN_EQUAL && compiler->type == TYPE_TOP_LEVEL) { emitByte(compiler, OP_POP_REPL); } else { emitByte(compiler, OP_POP); From 8160ebdac57d17945e619c048a863ceeb1170cc3 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 16 Sep 2022 21:03:56 +0100 Subject: [PATCH 014/104] Remove unused opcode --- src/vm/opcodes.h | 1 - src/vm/vm.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/src/vm/opcodes.h b/src/vm/opcodes.h index 8cd167ad..c392fb24 100644 --- a/src/vm/opcodes.h +++ b/src/vm/opcodes.h @@ -47,7 +47,6 @@ OPCODE(JUMP_IF_FALSE) OPCODE(JUMP_IF_NIL) OPCODE(LOOP) OPCODE(CALL) -OPCODE(CALL_UNPACK) OPCODE(INVOKE) OPCODE(INVOKE_INTERNAL) OPCODE(SUPER) diff --git a/src/vm/vm.c b/src/vm/vm.c index ddd60c91..ef2e27f8 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1557,17 +1557,6 @@ static DictuInterpretResult run(DictuVM *vm) { DISPATCH(); } - CASE_CODE(CALL_UNPACK): { - int argCount = AS_NUMBER(pop(vm)); - frame->ip = ip; - if (!callValue(vm, peek(vm, argCount), argCount, false)) { - return INTERPRET_RUNTIME_ERROR; - } - frame = &vm->frames[vm->frameCount - 1]; - ip = frame->ip; - DISPATCH(); - } - CASE_CODE(BREAK): { DISPATCH(); } From 4f4bf147352d65ad3e4c3b7c0dbaabc54d86a538 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 16 Sep 2022 21:16:43 +0100 Subject: [PATCH 015/104] Use the correct unpack operator --- src/optionals/object/object-source.h | 6 +++--- src/optionals/object/object.du | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/optionals/object/object-source.h b/src/optionals/object/object-source.h index f90a2bdb..48c9fa33 100644 --- a/src/optionals/object/object-source.h +++ b/src/optionals/object/object-source.h @@ -1,8 +1,8 @@ -#define DICTU_OBJECT_SOURCE "def createFrom(className, ...arguments) {\n" \ -" import Object;\n" \ +#define DICTU_OBJECT_SOURCE "import Object;\n" \ "\n" \ +"def createFrom(className, ...arguments) {\n" \ " return Object.__getClassRef(className).matchWrap(\n" \ -" def (klass) => klass(),\n" \ +" def (klass) => klass(...arguments),\n" \ " def (error) => error\n" \ " );\n" \ "}\n" \ diff --git a/src/optionals/object/object.du b/src/optionals/object/object.du index 387c90d8..e8471041 100644 --- a/src/optionals/object/object.du +++ b/src/optionals/object/object.du @@ -2,7 +2,7 @@ import Object; def createFrom(className, ...arguments) { return Object.__getClassRef(className).matchWrap( - def (klass) => klass(), + def (klass) => klass(...arguments), def (error) => error ); } \ No newline at end of file From a718da89e9588018fb886cf79fc1efc14d34ba5c Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 20:31:24 -0700 Subject: [PATCH 016/104] add repeat method to string type Signed-off-by: Brian Downs --- docs/docs/strings.md | 9 +++++++++ src/vm/datatypes/strings.c | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/docs/docs/strings.md b/docs/docs/strings.md index b2176af0..d46f28b3 100644 --- a/docs/docs/strings.md +++ b/docs/docs/strings.md @@ -257,4 +257,13 @@ Returns a title cased version of string with first letter of each word capitaliz "dictu language".title(); // Dictu Language "this documentation".title(); // This Documentation "once upon a time".title(); // Once Upon A Time +``` + +### string.repeat(number) + +Returns a new string with the original string repeated the given number of times. + +```cs +"ba"+"na".repeat(2); // banana +"la".repeat(2) + " land"; // lala land ``` \ No newline at end of file diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index e234239a..3e83eef7 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -530,6 +530,30 @@ static Value titleString(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(takeString(vm, temp, string->length)); } +static Value repeatString(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "repeat() takes one argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[1])) { + runtimeError(vm, "repeat() count argument must be a number"); + return EMPTY_VAL; + } + + ObjString *string = AS_STRING(args[0]); + int count = AS_NUMBER(args[1]); + char *temp = ALLOCATE(vm, char, (string->length * count) + count); + + memcpy(temp, string->chars, string->length + 1); + strcpy(temp, string->chars); + while (--count > 0) { + strcat(temp, string->chars); + } + + return OBJ_VAL(takeString(vm, temp, strlen(temp))); +} + void declareStringMethods(DictuVM *vm) { defineNative(vm, &vm->stringMethods, "len", lenString); defineNative(vm, &vm->stringMethods, "toNumber", toNumberString); @@ -548,4 +572,6 @@ void declareStringMethods(DictuVM *vm) { defineNative(vm, &vm->stringMethods, "count", countString); defineNative(vm, &vm->stringMethods, "toBool", boolNative); // Defined in util defineNative(vm, &vm->stringMethods, "title", titleString); + defineNative(vm, &vm->stringMethods, "repeat", repeatString); + } From c73fed96ea3e074dac9908525191535587ff6348 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 20:31:38 -0700 Subject: [PATCH 017/104] add repeat method to string type Signed-off-by: Brian Downs --- tests/strings/repeat.du | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/strings/repeat.du diff --git a/tests/strings/repeat.du b/tests/strings/repeat.du new file mode 100644 index 00000000..4d66c66d --- /dev/null +++ b/tests/strings/repeat.du @@ -0,0 +1,17 @@ +/** + * repeat.du + * + * Testing the str.repeat() method + * + * .repeat() returns a new string with the original string repeated the given number of times + */ +from UnitTest import UnitTest; + +class TestStringRepeat < UnitTest { + testStringRepeat() { + this.assertEquals("ba" + "na".repeat(2), "banana"); + this.assertEquals("la".repeat(2) + " land", "lala land"); + } +} + +TestStringRepeat().run(); \ No newline at end of file From e8b676a8073d73e201383cc46747f0c26d0095f8 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 20:33:52 -0700 Subject: [PATCH 018/104] remove unnecessary memcpy Signed-off-by: Brian Downs --- src/vm/datatypes/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index 3e83eef7..62555b86 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -545,7 +545,7 @@ static Value repeatString(DictuVM *vm, int argCount, Value *args) { int count = AS_NUMBER(args[1]); char *temp = ALLOCATE(vm, char, (string->length * count) + count); - memcpy(temp, string->chars, string->length + 1); + //memcpy(temp, string->chars, string->length + 1); strcpy(temp, string->chars); while (--count > 0) { strcat(temp, string->chars); From cef760cb2fe6c538052187e139dd8b0015009083 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 20:36:39 -0700 Subject: [PATCH 019/104] add new line to test file Signed-off-by: Brian Downs --- tests/strings/repeat.du | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/strings/repeat.du b/tests/strings/repeat.du index 4d66c66d..21668a26 100644 --- a/tests/strings/repeat.du +++ b/tests/strings/repeat.du @@ -14,4 +14,4 @@ class TestStringRepeat < UnitTest { } } -TestStringRepeat().run(); \ No newline at end of file +TestStringRepeat().run(); From 7f2924e50a90eaa382bb8bff2a9a31df105a550f Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 21:03:13 -0700 Subject: [PATCH 020/104] update test, code quality check issue Signed-off-by: Brian Downs --- src/vm/datatypes/strings.c | 5 +++-- tests/strings/repeat.du | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index 62555b86..5ac497db 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -543,14 +543,15 @@ static Value repeatString(DictuVM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int count = AS_NUMBER(args[1]); - char *temp = ALLOCATE(vm, char, (string->length * count) + count); + char *temp = ALLOCATE(vm, char, (string->length * count) + 1); - //memcpy(temp, string->chars, string->length + 1); strcpy(temp, string->chars); while (--count > 0) { strcat(temp, string->chars); } + temp[strlen(temp)] = '\0'; + return OBJ_VAL(takeString(vm, temp, strlen(temp))); } diff --git a/tests/strings/repeat.du b/tests/strings/repeat.du index 21668a26..60b6f846 100644 --- a/tests/strings/repeat.du +++ b/tests/strings/repeat.du @@ -10,7 +10,7 @@ from UnitTest import UnitTest; class TestStringRepeat < UnitTest { testStringRepeat() { this.assertEquals("ba" + "na".repeat(2), "banana"); - this.assertEquals("la".repeat(2) + " land", "lala land"); + this.assertEquals("ha".repeat(6), "hahahahahaha"); } } From ab7cf326adf0956b0c128fb47d385ced032df59e Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 28 Sep 2022 21:07:52 -0700 Subject: [PATCH 021/104] revert to original Signed-off-by: Brian Downs --- src/vm/datatypes/strings.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index 5ac497db..d2675d2b 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -550,8 +550,6 @@ static Value repeatString(DictuVM *vm, int argCount, Value *args) { strcat(temp, string->chars); } - temp[strlen(temp)] = '\0'; - return OBJ_VAL(takeString(vm, temp, strlen(temp))); } From 859b5664e033565bc798b1b956b8785b578cff3e Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 06:37:17 -0700 Subject: [PATCH 022/104] add repeat tests and update temp string len use Signed-off-by: Brian Downs --- src/vm/datatypes/strings.c | 6 ++++-- tests/strings/import.du | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index d2675d2b..dfd44810 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -543,14 +543,16 @@ static Value repeatString(DictuVM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int count = AS_NUMBER(args[1]); - char *temp = ALLOCATE(vm, char, (string->length * count) + 1); + + int tempLen = (string->length * count) + 1; + char *temp = ALLOCATE(vm, char, tempLen); strcpy(temp, string->chars); while (--count > 0) { strcat(temp, string->chars); } - return OBJ_VAL(takeString(vm, temp, strlen(temp))); + return OBJ_VAL(takeString(vm, temp, tempLen)); } void declareStringMethods(DictuVM *vm) { diff --git a/tests/strings/import.du b/tests/strings/import.du index e35c4cea..d622ef82 100644 --- a/tests/strings/import.du +++ b/tests/strings/import.du @@ -23,3 +23,4 @@ import "rawStrings.du"; import "toNumber.du"; import "toBool.du"; import "escapeCodes.du"; +import "repeat.du"; From d3f32260352eaf93fafd2a323e2b16174fbef7b3 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 06:39:05 -0700 Subject: [PATCH 023/104] adjust temp string length return Signed-off-by: Brian Downs --- src/vm/datatypes/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index dfd44810..296084e0 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -552,7 +552,7 @@ static Value repeatString(DictuVM *vm, int argCount, Value *args) { strcat(temp, string->chars); } - return OBJ_VAL(takeString(vm, temp, tempLen)); + return OBJ_VAL(takeString(vm, temp, tempLen - 1)); } void declareStringMethods(DictuVM *vm) { From fb5642a5c4af8ce633d079a28637c5eb23333124 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 08:15:16 -0700 Subject: [PATCH 024/104] Add uname function to System module. Added documentation and tests for new function. Signed-off-by: Brian Downs --- docs/docs/standard-lib/system.md | 8 ++++ src/optionals/system.c | 63 ++++++++++++++++++++++++++++++++ tests/system/import.du | 1 + tests/system/uname.du | 21 +++++++++++ 4 files changed, 93 insertions(+) create mode 100644 tests/system/uname.du diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index d80b7314..9bef7690 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -246,3 +246,11 @@ Note: This is not available on Windows systems. ```cs System.chown("/path/to/file", 0, 0); ``` + +### System.uname() + +Returns the name and version of the system along with operating system and hardware information. + +```cs +System.uname(); +``` \ No newline at end of file diff --git a/src/optionals/system.c b/src/optionals/system.c index db6622be..f7d98f25 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -332,6 +332,68 @@ static Value chmodNative(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } +static Value unameNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 0) { + runtimeError(vm, "uname() doesn't take any arguments (%d given)).", argCount); + return EMPTY_VAL; + } + + struct utsname u; + if (uname(&u) == -1) { + runtimeError(vm, ""); + return EMPTY_VAL; + } + + ObjDict *unameDict = newDict(vm); + push(vm, OBJ_VAL(unameDict)); + + ObjString *sysname = copyString(vm, "sysname", 7); + push(vm, OBJ_VAL(sysname)); + ObjString *sysnameVal = copyString(vm, u.sysname, strlen(u.sysname)); + push(vm, OBJ_VAL(sysnameVal)); + dictSet(vm, unameDict, OBJ_VAL(sysname), OBJ_VAL(sysnameVal)); + pop(vm); + pop(vm); + + ObjString *nodename = copyString(vm, "nodename", 8); + push(vm, OBJ_VAL(nodename)); + ObjString *nodenameVal = copyString(vm, u.nodename, strlen(u.nodename)); + push(vm, OBJ_VAL(nodenameVal)); + dictSet(vm, unameDict, OBJ_VAL(nodename), OBJ_VAL(nodenameVal)); + pop(vm); + pop(vm); + + ObjString *machine = copyString(vm, "machine", 7); + push(vm, OBJ_VAL(machine)); + ObjString *machineVal = copyString(vm, u.machine, strlen(u.machine)); + push(vm, OBJ_VAL(machineVal)); + dictSet(vm, unameDict, OBJ_VAL(machine), OBJ_VAL(machineVal)); + pop(vm); + pop(vm); + + ObjString *release = copyString(vm, "release", 7); + push(vm, OBJ_VAL(release)); + ObjString *releaseVal = copyString(vm, u.release, strlen(u.release)); + push(vm, OBJ_VAL(releaseVal)); + dictSet(vm, unameDict, OBJ_VAL(release), OBJ_VAL(releaseVal)); + pop(vm); + pop(vm); + + ObjString *version = copyString(vm, "version", 7); + push(vm, OBJ_VAL(version)); + ObjString *versionVal = copyString(vm, u.version, strlen(u.version)); + push(vm, OBJ_VAL(versionVal)); + dictSet(vm, unameDict, OBJ_VAL(version), OBJ_VAL(versionVal)); + pop(vm); + pop(vm); + + pop(vm); + + return OBJ_VAL(unameDict); +} + void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); @@ -429,6 +491,7 @@ Value createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "sleep", sleepNative); defineNative(vm, &module->values, "exit", exitNative); defineNative(vm, &module->values, "chmod", chmodNative); + defineNative(vm, &module->values, "uname", unameNative); /** * Define System properties diff --git a/tests/system/import.du b/tests/system/import.du index b36c1a40..357f87e0 100644 --- a/tests/system/import.du +++ b/tests/system/import.du @@ -17,3 +17,4 @@ import "mkdir.du"; import "constants.du"; import "chmod.du"; import "chown.du"; +import "uname.du"; diff --git a/tests/system/uname.du b/tests/system/uname.du new file mode 100644 index 00000000..8e5f88a7 --- /dev/null +++ b/tests/system/uname.du @@ -0,0 +1,21 @@ +/** + * sleep.du + * + * Testing the System.uname() function + * + * uname() returns the name and version of the system along with operating + * system and hardware information + */ +from UnitTest import UnitTest; + +import System; + +class TestSystemUname < UnitTest { + testSystemUname() { + const uname = System.uname(); + this.assertNotNil(uname); + this.assertType(uname, "dict"); + } +} + +TestSystemUname().run(); From b55048de53caa9366d888dd7ea4f1eb7ea697a1b Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 08:16:25 -0700 Subject: [PATCH 025/104] update name of test Signed-off-by: Brian Downs --- tests/system/uname.du | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/uname.du b/tests/system/uname.du index 8e5f88a7..e3e73fa0 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -1,5 +1,5 @@ /** - * sleep.du + * uname.du * * Testing the System.uname() function * From 5994722e5c6fbb6d47cc5ddded7687f5d2fef3c6 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 10:29:53 -0700 Subject: [PATCH 026/104] add error string Signed-off-by: Brian Downs --- src/optionals/system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index f7d98f25..e0e486db 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -342,7 +342,7 @@ static Value unameNative(DictuVM *vm, int argCount, Value *args) { struct utsname u; if (uname(&u) == -1) { - runtimeError(vm, ""); + runtimeError(vm, "uname() failed to retrieve information"); return EMPTY_VAL; } From 2b3296c27ccd225c315f36fd48d65cf1824162c7 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 17:25:52 -0700 Subject: [PATCH 027/104] prevent uname from running on windows Signed-off-by: Brian Downs --- src/optionals/system.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/optionals/system.c b/src/optionals/system.c index e0e486db..bbcb2ca8 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -332,6 +332,7 @@ static Value chmodNative(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } +#ifndef _WIN32 static Value unameNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); @@ -393,6 +394,7 @@ static Value unameNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(unameDict); } +#endif void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); From bceb11d61fdf5d46a5984d72b9259bddb32f00cf Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 17:38:04 -0700 Subject: [PATCH 028/104] prevent uname from running on windows Signed-off-by: Brian Downs --- src/optionals/system.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index bbcb2ca8..78abe476 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -332,7 +332,7 @@ static Value chmodNative(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } -#ifndef _WIN32 + static Value unameNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); @@ -394,7 +394,6 @@ static Value unameNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(unameDict); } -#endif void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); @@ -493,8 +492,9 @@ Value createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "sleep", sleepNative); defineNative(vm, &module->values, "exit", exitNative); defineNative(vm, &module->values, "chmod", chmodNative); +#ifndef _WIN32 defineNative(vm, &module->values, "uname", unameNative); - +#endif /** * Define System properties */ From 2144a3d4e16242fab806f7db760a8e55fe4b002d Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 17:54:19 -0700 Subject: [PATCH 029/104] prevent uname from running on windows Signed-off-by: Brian Downs --- src/optionals/system.c | 130 ++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 66 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index 78abe476..64f702b3 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -100,6 +100,68 @@ static Value chownNative(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, EMPTY_VAL); } + +static Value unameNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 0) { + runtimeError(vm, "uname() doesn't take any arguments (%d given)).", argCount); + return EMPTY_VAL; + } + + struct utsname u; + if (uname(&u) == -1) { + runtimeError(vm, "uname() failed to retrieve information"); + return EMPTY_VAL; + } + + ObjDict *unameDict = newDict(vm); + push(vm, OBJ_VAL(unameDict)); + + ObjString *sysname = copyString(vm, "sysname", 7); + push(vm, OBJ_VAL(sysname)); + ObjString *sysnameVal = copyString(vm, u.sysname, strlen(u.sysname)); + push(vm, OBJ_VAL(sysnameVal)); + dictSet(vm, unameDict, OBJ_VAL(sysname), OBJ_VAL(sysnameVal)); + pop(vm); + pop(vm); + + ObjString *nodename = copyString(vm, "nodename", 8); + push(vm, OBJ_VAL(nodename)); + ObjString *nodenameVal = copyString(vm, u.nodename, strlen(u.nodename)); + push(vm, OBJ_VAL(nodenameVal)); + dictSet(vm, unameDict, OBJ_VAL(nodename), OBJ_VAL(nodenameVal)); + pop(vm); + pop(vm); + + ObjString *machine = copyString(vm, "machine", 7); + push(vm, OBJ_VAL(machine)); + ObjString *machineVal = copyString(vm, u.machine, strlen(u.machine)); + push(vm, OBJ_VAL(machineVal)); + dictSet(vm, unameDict, OBJ_VAL(machine), OBJ_VAL(machineVal)); + pop(vm); + pop(vm); + + ObjString *release = copyString(vm, "release", 7); + push(vm, OBJ_VAL(release)); + ObjString *releaseVal = copyString(vm, u.release, strlen(u.release)); + push(vm, OBJ_VAL(releaseVal)); + dictSet(vm, unameDict, OBJ_VAL(release), OBJ_VAL(releaseVal)); + pop(vm); + pop(vm); + + ObjString *version = copyString(vm, "version", 7); + push(vm, OBJ_VAL(version)); + ObjString *versionVal = copyString(vm, u.version, strlen(u.version)); + push(vm, OBJ_VAL(versionVal)); + dictSet(vm, unameDict, OBJ_VAL(version), OBJ_VAL(versionVal)); + pop(vm); + pop(vm); + + pop(vm); + + return OBJ_VAL(unameDict); +} #endif static Value rmdirNative(DictuVM *vm, int argCount, Value *args) { @@ -332,69 +394,6 @@ static Value chmodNative(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } - -static Value unameNative(DictuVM *vm, int argCount, Value *args) { - UNUSED(args); - - if (argCount != 0) { - runtimeError(vm, "uname() doesn't take any arguments (%d given)).", argCount); - return EMPTY_VAL; - } - - struct utsname u; - if (uname(&u) == -1) { - runtimeError(vm, "uname() failed to retrieve information"); - return EMPTY_VAL; - } - - ObjDict *unameDict = newDict(vm); - push(vm, OBJ_VAL(unameDict)); - - ObjString *sysname = copyString(vm, "sysname", 7); - push(vm, OBJ_VAL(sysname)); - ObjString *sysnameVal = copyString(vm, u.sysname, strlen(u.sysname)); - push(vm, OBJ_VAL(sysnameVal)); - dictSet(vm, unameDict, OBJ_VAL(sysname), OBJ_VAL(sysnameVal)); - pop(vm); - pop(vm); - - ObjString *nodename = copyString(vm, "nodename", 8); - push(vm, OBJ_VAL(nodename)); - ObjString *nodenameVal = copyString(vm, u.nodename, strlen(u.nodename)); - push(vm, OBJ_VAL(nodenameVal)); - dictSet(vm, unameDict, OBJ_VAL(nodename), OBJ_VAL(nodenameVal)); - pop(vm); - pop(vm); - - ObjString *machine = copyString(vm, "machine", 7); - push(vm, OBJ_VAL(machine)); - ObjString *machineVal = copyString(vm, u.machine, strlen(u.machine)); - push(vm, OBJ_VAL(machineVal)); - dictSet(vm, unameDict, OBJ_VAL(machine), OBJ_VAL(machineVal)); - pop(vm); - pop(vm); - - ObjString *release = copyString(vm, "release", 7); - push(vm, OBJ_VAL(release)); - ObjString *releaseVal = copyString(vm, u.release, strlen(u.release)); - push(vm, OBJ_VAL(releaseVal)); - dictSet(vm, unameDict, OBJ_VAL(release), OBJ_VAL(releaseVal)); - pop(vm); - pop(vm); - - ObjString *version = copyString(vm, "version", 7); - push(vm, OBJ_VAL(version)); - ObjString *versionVal = copyString(vm, u.version, strlen(u.version)); - push(vm, OBJ_VAL(versionVal)); - dictSet(vm, unameDict, OBJ_VAL(version), OBJ_VAL(versionVal)); - pop(vm); - pop(vm); - - pop(vm); - - return OBJ_VAL(unameDict); -} - void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); @@ -477,6 +476,7 @@ Value createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "getppid", getppidNative); defineNative(vm, &module->values, "getpid", getpidNative); defineNative(vm, &module->values, "chown", chownNative); + defineNative(vm, &module->values, "uname", unameNative); #endif defineNative(vm, &module->values, "rmdir", rmdirNative); defineNative(vm, &module->values, "mkdir", mkdirNative); @@ -492,9 +492,7 @@ Value createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "sleep", sleepNative); defineNative(vm, &module->values, "exit", exitNative); defineNative(vm, &module->values, "chmod", chmodNative); -#ifndef _WIN32 - defineNative(vm, &module->values, "uname", unameNative); -#endif + /** * Define System properties */ From 7f2a19c8ef4880a98a4ca437b5c118d11b4090b2 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 19:02:32 -0700 Subject: [PATCH 030/104] prevent uname from running on windows Signed-off-by: Brian Downs --- tests/system/uname.du | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/system/uname.du b/tests/system/uname.du index e3e73fa0..be783576 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -12,6 +12,9 @@ import System; class TestSystemUname < UnitTest { testSystemUname() { + if (System.Platform == "windows") { + return; + } const uname = System.uname(); this.assertNotNil(uname); this.assertType(uname, "dict"); From 2451b2e5a7c9fdd8754e37ed2ceb2e6b669fa1cd Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 19:04:58 -0700 Subject: [PATCH 031/104] prevent uname from running on windows Signed-off-by: Brian Downs --- tests/system/uname.du | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/system/uname.du b/tests/system/uname.du index be783576..025c17ba 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -12,12 +12,11 @@ import System; class TestSystemUname < UnitTest { testSystemUname() { - if (System.Platform == "windows") { - return; + if (System.Platform != "windows") { + const uname = System.uname(); + this.assertNotNil(uname); + this.assertType(uname, "dict"); } - const uname = System.uname(); - this.assertNotNil(uname); - this.assertType(uname, "dict"); } } From 960c013e971b3fdc581a4b0b4d628f1bff37f602 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 29 Sep 2022 22:48:18 -0700 Subject: [PATCH 032/104] update docs and test Signed-off-by: Brian Downs --- docs/docs/standard-lib/system.md | 2 ++ tests/system/uname.du | 12 ------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index 9bef7690..ccbf7163 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -251,6 +251,8 @@ System.chown("/path/to/file", 0, 0); Returns the name and version of the system along with operating system and hardware information. +Note: This is not available on Windows systems. + ```cs System.uname(); ``` \ No newline at end of file diff --git a/tests/system/uname.du b/tests/system/uname.du index 025c17ba..648e5754 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -9,15 +9,3 @@ from UnitTest import UnitTest; import System; - -class TestSystemUname < UnitTest { - testSystemUname() { - if (System.Platform != "windows") { - const uname = System.uname(); - this.assertNotNil(uname); - this.assertType(uname, "dict"); - } - } -} - -TestSystemUname().run(); From 1fb80549f18c0ccfdca76f99bc7bdcc8c06ec6a3 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Fri, 30 Sep 2022 08:37:24 -0700 Subject: [PATCH 033/104] update docs and test Signed-off-by: Brian Downs --- examples/system.du | 3 +++ tests/system/uname.du | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 examples/system.du diff --git a/examples/system.du b/examples/system.du new file mode 100644 index 00000000..2d5ecc13 --- /dev/null +++ b/examples/system.du @@ -0,0 +1,3 @@ +import System; + +print(System.uname()); diff --git a/tests/system/uname.du b/tests/system/uname.du index 648e5754..79ef6c9a 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -9,3 +9,15 @@ from UnitTest import UnitTest; import System; + +class TestSystemUname < UnitTest { + testSystemUname() { + if (System.platform != "windows") { + const uname = System.uname(); + this.assertNotNil(uname); + this.assertType(uname, "dict"); + } + } +} + +TestSystemUname().run(); From 277c77321f1620eaf8f6761013c6e16d2cf9ff0b Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 2 Oct 2022 19:34:53 +0100 Subject: [PATCH 034/104] Update uname.du --- tests/system/uname.du | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/uname.du b/tests/system/uname.du index 79ef6c9a..c3acf310 100644 --- a/tests/system/uname.du +++ b/tests/system/uname.du @@ -14,7 +14,7 @@ class TestSystemUname < UnitTest { testSystemUname() { if (System.platform != "windows") { const uname = System.uname(); - this.assertNotNil(uname); + this.assertNotNil(uname); this.assertType(uname, "dict"); } } From e47cd8df38b37647020e979aae445afa080cd70e Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 14:38:01 -0700 Subject: [PATCH 035/104] fix merge conflicts Signed-off-by: Brian Downs --- docs/docs/standard-lib/system.md | 10 ++++++++++ examples/mkdirTemp.du | 8 ++++++++ src/optionals/system.c | 31 +++++++++++++++++++++++++++++++ tests/system/import.du | 4 ++++ tests/system/mkdirTemp.du | 29 +++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 examples/mkdirTemp.du create mode 100644 tests/system/mkdirTemp.du diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index ccbf7163..ebf03589 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -255,4 +255,14 @@ Note: This is not available on Windows systems. ```cs System.uname(); +``` + +### System.mkdirTemp(string) + +Makes a temporary directory. If an empty string is given, the temporary directory's name will be a random string created in the current working directory. If a string is passed in, the temporary directory will be created with that name in the current working directory. + +Note: This is not available on Windows systems. + +```cs +System.mkdirTemp(""); ``` \ No newline at end of file diff --git a/examples/mkdirTemp.du b/examples/mkdirTemp.du new file mode 100644 index 00000000..e7582552 --- /dev/null +++ b/examples/mkdirTemp.du @@ -0,0 +1,8 @@ +import Env; +import System; + +const tmpDir = Env.get("TMPDIR") + "XXXXXX"; + +var res = System.mkdirTemp(tmpDir); + +print(res); diff --git a/src/optionals/system.c b/src/optionals/system.c index 64f702b3..b9c5ff5f 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -162,6 +162,36 @@ static Value unameNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(unameDict); } + +static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { + if (argCount == 0) { + runtimeError(vm, "mkdirTemp() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0])) { + runtimeError(vm, "mkdirTemp() first argument must be a string"); + return EMPTY_VAL; + } + + char *template = AS_CSTRING(args[0]); + char *tmpl = {0}; + + if (template[0] != '\0') { + tmpl = malloc(strlen(template)+1); + strcpy(tmpl, template); + } else { + tmpl = malloc(PATH_MAX); + strcpy(tmpl, "XXXXXX"); + } + + char *tmpDir = mkdtemp(tmpl); + if (tmpDir) { + return OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir))); + } + + return NIL_VAL; +} #endif static Value rmdirNative(DictuVM *vm, int argCount, Value *args) { @@ -477,6 +507,7 @@ Value createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "getpid", getpidNative); defineNative(vm, &module->values, "chown", chownNative); defineNative(vm, &module->values, "uname", unameNative); + defineNative(vm, &module->values, "mkdirTemp", mkdirTempNative); #endif defineNative(vm, &module->values, "rmdir", rmdirNative); defineNative(vm, &module->values, "mkdir", mkdirNative); diff --git a/tests/system/import.du b/tests/system/import.du index 357f87e0..47ac165e 100644 --- a/tests/system/import.du +++ b/tests/system/import.du @@ -17,4 +17,8 @@ import "mkdir.du"; import "constants.du"; import "chmod.du"; import "chown.du"; +<<<<<<< HEAD import "uname.du"; +======= +import "mkdirTemp.du"; +>>>>>>> b220b78 (add mkdirTemp to System module) diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du new file mode 100644 index 00000000..770ff6d3 --- /dev/null +++ b/tests/system/mkdirTemp.du @@ -0,0 +1,29 @@ +/** + * mkdirTemp.du + * + * Testing the System.mkdirTemp function + * + * mkdirTemp makes a temporary directory. + */ +from UnitTest import UnitTest; + +import Path; +import System; + +class TestSystemMkdirTest < UnitTest { + testSystemMkdirTest() { + if (System.platform != "windows") { + const tempDirName = "test_mkdir_temp"; + + var res = System.mkdirTemp(tempDirName); + if (res != nil) { + this.assertEquals(res, tempDirName); + this.assertTruthy(System.rmdir(tempDirName).success()); + } else { + this.assertEquals(res, nil); + } + } + } +} + +TestSystemMkdirTest().run(); From d5edce6668e59325188080fdb447566108a3d945 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 15:05:07 -0700 Subject: [PATCH 036/104] refactor to use result Signed-off-by: Brian Downs --- src/optionals/system.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index b9c5ff5f..2d5aaaf3 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -178,10 +178,10 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { char *tmpl = {0}; if (template[0] != '\0') { - tmpl = malloc(strlen(template)+1); + tmpl = ALLOCATE(vm, char, strlen(template)); strcpy(tmpl, template); } else { - tmpl = malloc(PATH_MAX); + tmpl = ALLOCATE(vm, char, PATH_MAX); strcpy(tmpl, "XXXXXX"); } @@ -190,7 +190,7 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir))); } - return NIL_VAL; + return newResultSuccess(vm, NIL_VAL); } #endif From 249f1b88be0b5ccd86fbcad77e896effdd96c662 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 15:07:25 -0700 Subject: [PATCH 037/104] fix merge conflict Signed-off-by: Brian Downs --- tests/system/import.du | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/system/import.du b/tests/system/import.du index 47ac165e..e3e04d74 100644 --- a/tests/system/import.du +++ b/tests/system/import.du @@ -17,8 +17,5 @@ import "mkdir.du"; import "constants.du"; import "chmod.du"; import "chown.du"; -<<<<<<< HEAD import "uname.du"; -======= import "mkdirTemp.du"; ->>>>>>> b220b78 (add mkdirTemp to System module) From 8f302cc57360aef84ad1c2f024daf25081f791ce Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 15:18:45 -0700 Subject: [PATCH 038/104] refactor test Signed-off-by: Brian Downs --- src/optionals/system.c | 8 ++++---- tests/system/mkdirTemp.du | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index 2d5aaaf3..4fab9ef5 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -186,11 +186,11 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { } char *tmpDir = mkdtemp(tmpl); - if (tmpDir) { - return OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir))); + if (!tmpDir) { + ERROR_RESULT; } - - return newResultSuccess(vm, NIL_VAL); + + return newResultSuccess(vm, OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir)))); } #endif diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index 770ff6d3..420cc0b4 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -15,13 +15,21 @@ class TestSystemMkdirTest < UnitTest { if (System.platform != "windows") { const tempDirName = "test_mkdir_temp"; - var res = System.mkdirTemp(tempDirName); - if (res != nil) { - this.assertEquals(res, tempDirName); - this.assertTruthy(System.rmdir(tempDirName).success()); - } else { - this.assertEquals(res, nil); - } + var res = System.mkdirTemp(tempDirName).match( + def (result) => { + this.assertEquals(result, tempDirName); + this.assertTruthy(System.rmdir(tempDirName).success()); + }, + def (error) => { + this.assertEquals(error, "File exists"); + } + ); + // if (res != nil) { + // this.assertEquals(res, tempDirName); + // this.assertTruthy(System.rmdir(tempDirName).success()); + // } else { + // this.assertEquals(res, nil); + // } } } } From ea6f0df8eda0a7715dab34b9e40f717436603cfd Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 16:50:20 -0700 Subject: [PATCH 039/104] update test Signed-off-by: Brian Downs --- tests/system/mkdirTemp.du | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index 420cc0b4..43fd46cd 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -24,12 +24,6 @@ class TestSystemMkdirTest < UnitTest { this.assertEquals(error, "File exists"); } ); - // if (res != nil) { - // this.assertEquals(res, tempDirName); - // this.assertTruthy(System.rmdir(tempDirName).success()); - // } else { - // this.assertEquals(res, nil); - // } } } } From 0eb111f75b4ce6df66fbe02ffc9a6df17db8f020 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 18:54:10 -0700 Subject: [PATCH 040/104] update test Signed-off-by: Brian Downs --- tests/system/mkdirTemp.du | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index 43fd46cd..f3f1d8b7 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -21,7 +21,7 @@ class TestSystemMkdirTest < UnitTest { this.assertTruthy(System.rmdir(tempDirName).success()); }, def (error) => { - this.assertEquals(error, "File exists"); + this.assertEquals(error, "Invalid argument"); } ); } From a5723055e061b504f2dd43102d8ef7c2c105138d Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 19:01:46 -0700 Subject: [PATCH 041/104] fix memory Signed-off-by: Brian Downs --- src/optionals/system.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/optionals/system.c b/src/optionals/system.c index 4fab9ef5..08fc3581 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -187,10 +187,16 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { char *tmpDir = mkdtemp(tmpl); if (!tmpDir) { + goto CLEANUP; ERROR_RESULT; } + + goto CLEANUP; return newResultSuccess(vm, OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir)))); + +CLEANUP: + FREE(vm, char, tmpl); } #endif From 31804641e4dd0b39a9afe5f315abf3826eff7285 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 19:14:22 -0700 Subject: [PATCH 042/104] fix memory Signed-off-by: Brian Downs --- src/optionals/system.c | 6 ------ tests/system/mkdirTemp.du | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index 08fc3581..4fab9ef5 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -187,16 +187,10 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { char *tmpDir = mkdtemp(tmpl); if (!tmpDir) { - goto CLEANUP; ERROR_RESULT; } - - goto CLEANUP; return newResultSuccess(vm, OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir)))); - -CLEANUP: - FREE(vm, char, tmpl); } #endif diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index f3f1d8b7..b9276a07 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -14,6 +14,7 @@ class TestSystemMkdirTest < UnitTest { testSystemMkdirTest() { if (System.platform != "windows") { const tempDirName = "test_mkdir_temp"; + this.assertTruthy(System.rmdir(tempDirName).success()); var res = System.mkdirTemp(tempDirName).match( def (result) => { From d54174b638da74fc98b2c39d3ce7f5b0131bcf42 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 19:17:04 -0700 Subject: [PATCH 043/104] fix memory Signed-off-by: Brian Downs --- tests/system/mkdirTemp.du | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index b9276a07..bf6d027b 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -14,7 +14,10 @@ class TestSystemMkdirTest < UnitTest { testSystemMkdirTest() { if (System.platform != "windows") { const tempDirName = "test_mkdir_temp"; - this.assertTruthy(System.rmdir(tempDirName).success()); + + if (Path.exists(tempDirName)) { + this.assertTruthy(System.rmdir(tempDirName).success()); + } var res = System.mkdirTemp(tempDirName).match( def (result) => { From 884ff343ff7bbc4d6c9d11c4495338b71edb7a49 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 2 Oct 2022 20:14:57 -0700 Subject: [PATCH 044/104] fix memory Signed-off-by: Brian Downs --- src/optionals/system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index 4fab9ef5..5454fb5c 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -186,11 +186,11 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { } char *tmpDir = mkdtemp(tmpl); - if (!tmpDir) { + if (!tmpDir) { ERROR_RESULT; } - return newResultSuccess(vm, OBJ_VAL(copyString(vm, tmpDir, strlen(tmpDir)))); + return newResultSuccess(vm, OBJ_VAL(takeString(vm, tmpDir, strlen(tmpDir)))); } #endif From c8d0d6f3cf5576e101e4eab1ba6fad1f288dff54 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 5 Oct 2022 22:00:33 -0700 Subject: [PATCH 045/104] start of fixes pr comments Signed-off-by: Brian Downs --- src/optionals/system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/optionals/system.c b/src/optionals/system.c index 5454fb5c..fdbcbf2a 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -178,10 +178,10 @@ static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { char *tmpl = {0}; if (template[0] != '\0') { - tmpl = ALLOCATE(vm, char, strlen(template)); + tmpl = ALLOCATE(vm, char, strlen(template)+1); strcpy(tmpl, template); } else { - tmpl = ALLOCATE(vm, char, PATH_MAX); + tmpl = ALLOCATE(vm, char, 7); strcpy(tmpl, "XXXXXX"); } From baf5175a5b2caf5c77d08d9c4a5db441eb345c22 Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Thu, 6 Oct 2022 15:19:49 +0100 Subject: [PATCH 046/104] Handle freeing memory if returning result type --- docs/docs/standard-lib/system.md | 9 +++++++-- src/optionals/system.c | 31 ++++++++++++++++++++----------- tests/system/mkdirTemp.du | 19 ++++++++++++------- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index ebf03589..3e4c4ba2 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -257,12 +257,17 @@ Note: This is not available on Windows systems. System.uname(); ``` -### System.mkdirTemp(string) +### System.mkdirTemp(string: directory_template -> optional) Makes a temporary directory. If an empty string is given, the temporary directory's name will be a random string created in the current working directory. If a string is passed in, the temporary directory will be created with that name in the current working directory. +The directory template passed in **must** end with "XXXXXX". + +Returns a Result type and on success will unwrap to a the created directory name. + Note: This is not available on Windows systems. ```cs -System.mkdirTemp(""); +System.mkdirTemp().unwrap(); // "VOO16s" +System.mkdirTemp("test_XXXXXX").unwrap(); // "test_0bL2qS" ``` \ No newline at end of file diff --git a/src/optionals/system.c b/src/optionals/system.c index fdbcbf2a..1cc9df9f 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -164,33 +164,42 @@ static Value unameNative(DictuVM *vm, int argCount, Value *args) { } static Value mkdirTempNative(DictuVM *vm, int argCount, Value *args) { - if (argCount == 0) { - runtimeError(vm, "mkdirTemp() takes 1 argument (%d given)", argCount); + if (argCount > 1) { + runtimeError(vm, "mkdirTemp() takes 0 or 1 argument(s) (%d given)", argCount); return EMPTY_VAL; } - if (!IS_STRING(args[0])) { - runtimeError(vm, "mkdirTemp() first argument must be a string"); - return EMPTY_VAL; + char *template = "XXXXXX"; + + if (argCount == 1) { + if (!IS_STRING(args[0])) { + runtimeError(vm, "mkdirTemp() first argument must be a string"); + return EMPTY_VAL; + } + + template = AS_CSTRING(args[0]); } - char *template = AS_CSTRING(args[0]); char *tmpl = {0}; + int size; if (template[0] != '\0') { - tmpl = ALLOCATE(vm, char, strlen(template)+1); + size = strlen(template) + 1; + tmpl = ALLOCATE(vm, char, size); strcpy(tmpl, template); } else { - tmpl = ALLOCATE(vm, char, 7); + size = 7; + tmpl = ALLOCATE(vm, char, size); strcpy(tmpl, "XXXXXX"); } char *tmpDir = mkdtemp(tmpl); - if (!tmpDir) { + if (!tmpDir) { + FREE_ARRAY(vm, char, tmpl, size); ERROR_RESULT; } - - return newResultSuccess(vm, OBJ_VAL(takeString(vm, tmpDir, strlen(tmpDir)))); + + return newResultSuccess(vm, OBJ_VAL(takeString(vm, tmpDir, size - 1))); } #endif diff --git a/tests/system/mkdirTemp.du b/tests/system/mkdirTemp.du index bf6d027b..4b929e44 100644 --- a/tests/system/mkdirTemp.du +++ b/tests/system/mkdirTemp.du @@ -11,18 +11,16 @@ import Path; import System; class TestSystemMkdirTest < UnitTest { - testSystemMkdirTest() { + testSystemMkdir(tempDirName) { if (System.platform != "windows") { - const tempDirName = "test_mkdir_temp"; - if (Path.exists(tempDirName)) { - this.assertTruthy(System.rmdir(tempDirName).success()); + this.assertSuccess(System.rmdir(tempDirName)); } - var res = System.mkdirTemp(tempDirName).match( + System.mkdirTemp(tempDirName).match( def (result) => { - this.assertEquals(result, tempDirName); - this.assertTruthy(System.rmdir(tempDirName).success()); + this.assertEquals(result[0:-6], tempDirName[0:-6]); + this.assertSuccess(System.rmdir(result)); }, def (error) => { this.assertEquals(error, "Invalid argument"); @@ -30,6 +28,13 @@ class TestSystemMkdirTest < UnitTest { ); } } + + testSystemMkdirProvider() { + return [ + 'test_mkdir_temp', + 'test_mkdir_tempXXXXXX' + ]; + } } TestSystemMkdirTest().run(); From 5363a7ac3b18119d6671b9b429cfeb54ddd1a498 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Sun, 9 Oct 2022 17:05:00 +0530 Subject: [PATCH 047/104] Added containsElements method for set collection --- docs/docs/collections/sets.md | 11 +++++++++++ src/vm/datatypes/sets.c | 24 ++++++++++++++++++++++++ tests/sets/containsElements.du | 20 ++++++++++++++++++++ tests/sets/import.du | 1 + 4 files changed, 56 insertions(+) create mode 100644 tests/sets/containsElements.du diff --git a/docs/docs/collections/sets.md b/docs/docs/collections/sets.md index db03ff75..0c237a8e 100644 --- a/docs/docs/collections/sets.md +++ b/docs/docs/collections/sets.md @@ -83,6 +83,17 @@ print(mySet.contains("Dictu!")); // true print(mySet.contains("Other!")); // false ``` +### set.containsElements(value) + +To checks if a set contains all elements in a given list use `containsElements()` + +```cs +var mySet = set("one",1,2,3);; +print(mySet.containsElements(["one",1])); // true +print(mySet.containsElements([1,2,3])); // true +print(mySet.containsElements(["one",1,2,3,"x"])); // false +``` + ### set.remove(value) To remove a value from a set use `.remove()`. diff --git a/src/vm/datatypes/sets.c b/src/vm/datatypes/sets.c index 59e49c98..26eeafe6 100644 --- a/src/vm/datatypes/sets.c +++ b/src/vm/datatypes/sets.c @@ -70,12 +70,36 @@ static Value containsSetItem(DictuVM *vm, int argCount, Value *args) { return setGet(set, args[1]) ? TRUE_VAL : FALSE_VAL; } +static Value containsElementsSet(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "containsElements() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if(!IS_LIST(args[1])){ + runtimeError(vm, "containsElements() argument must be a list"); + return EMPTY_VAL; + } + + ObjSet *set = AS_SET(args[0]); + ObjList *list = AS_LIST(args[1]); + + int listSize = list->values.count; + for(int index=0;indexvalues.values[index])==false){ + return FALSE_VAL; + } + } + return TRUE_VAL; +} + void declareSetMethods(DictuVM *vm) { defineNative(vm, &vm->setMethods, "toString", toStringSet); defineNative(vm, &vm->setMethods, "len", lenSet); defineNative(vm, &vm->setMethods, "add", addSetItem); defineNative(vm, &vm->setMethods, "remove", removeSetItem); defineNative(vm, &vm->setMethods, "contains", containsSetItem); + defineNative(vm, &vm->setMethods, "containsElements", containsElementsSet); defineNative(vm, &vm->setMethods, "toBool", boolNative); // Defined in util } diff --git a/tests/sets/containsElements.du b/tests/sets/containsElements.du new file mode 100644 index 00000000..7c1c8588 --- /dev/null +++ b/tests/sets/containsElements.du @@ -0,0 +1,20 @@ +/** + * containsElements.du + * + * Testing the containsElements method + * + * .containsElements() checks if a set contains all elements in a given list + */ +from UnitTest import UnitTest; + +class TestSetContainsElements < UnitTest { + testcontainsElements() { + var set_a = set("a",1,2,3); + this.assertTruthy(set_a.containsElements(["a"])); + this.assertTruthy(set_a.containsElements([1,2,3])); + this.assertTruthy(set_a.containsElements(["a",2,3,1])); + this.assertFalsey(set_a.containsElements(["a",2,3,1,"b"])); + } +} + +TestSetContainsElements().run(); \ No newline at end of file diff --git a/tests/sets/import.du b/tests/sets/import.du index 91cb63b5..e4f8d404 100644 --- a/tests/sets/import.du +++ b/tests/sets/import.du @@ -13,3 +13,4 @@ import "remove.du"; import "len.du"; import "toString.du"; import "toBool.du"; +import "containsElements.du"; From 553a0149549dbae2df921fb048ca746b1e09d6c4 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Sun, 9 Oct 2022 17:08:15 +0530 Subject: [PATCH 048/104] Fixed doc --- docs/docs/collections/sets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/collections/sets.md b/docs/docs/collections/sets.md index 0c237a8e..563d4a85 100644 --- a/docs/docs/collections/sets.md +++ b/docs/docs/collections/sets.md @@ -85,7 +85,7 @@ print(mySet.contains("Other!")); // false ### set.containsElements(value) -To checks if a set contains all elements in a given list use `containsElements()` +To check if a set contains all elements in a given list use `.containsElements()` ```cs var mySet = set("one",1,2,3);; From 0e88b10b13293cfd8cfbfa43ab5d1b24278f57d0 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Sun, 9 Oct 2022 23:40:15 +0530 Subject: [PATCH 049/104] Renamed to containsAll --- docs/docs/collections/sets.md | 10 +++++----- src/vm/datatypes/sets.c | 8 ++++---- tests/sets/containsElements.du | 20 -------------------- tests/sets/import.du | 2 +- 4 files changed, 10 insertions(+), 30 deletions(-) delete mode 100644 tests/sets/containsElements.du diff --git a/docs/docs/collections/sets.md b/docs/docs/collections/sets.md index 563d4a85..c541b945 100644 --- a/docs/docs/collections/sets.md +++ b/docs/docs/collections/sets.md @@ -83,15 +83,15 @@ print(mySet.contains("Dictu!")); // true print(mySet.contains("Other!")); // false ``` -### set.containsElements(value) +### set.containsAll(value) -To check if a set contains all elements in a given list use `.containsElements()` +To check if a set contains all elements in a given list use `.containsAll()` ```cs var mySet = set("one",1,2,3);; -print(mySet.containsElements(["one",1])); // true -print(mySet.containsElements([1,2,3])); // true -print(mySet.containsElements(["one",1,2,3,"x"])); // false +print(mySet.containsAll(["one",1])); // true +print(mySet.containsAll([1,2,3])); // true +print(mySet.containsAll(["one",1,2,3,"x"])); // false ``` ### set.remove(value) diff --git a/src/vm/datatypes/sets.c b/src/vm/datatypes/sets.c index 26eeafe6..f898a21f 100644 --- a/src/vm/datatypes/sets.c +++ b/src/vm/datatypes/sets.c @@ -70,14 +70,14 @@ static Value containsSetItem(DictuVM *vm, int argCount, Value *args) { return setGet(set, args[1]) ? TRUE_VAL : FALSE_VAL; } -static Value containsElementsSet(DictuVM *vm, int argCount, Value *args) { +static Value containsAllSet(DictuVM *vm, int argCount, Value *args) { if (argCount != 1) { - runtimeError(vm, "containsElements() takes 1 argument (%d given)", argCount); + runtimeError(vm, "containsAll() takes 1 argument (%d given)", argCount); return EMPTY_VAL; } if(!IS_LIST(args[1])){ - runtimeError(vm, "containsElements() argument must be a list"); + runtimeError(vm, "containsAll() argument must be a list"); return EMPTY_VAL; } @@ -99,7 +99,7 @@ void declareSetMethods(DictuVM *vm) { defineNative(vm, &vm->setMethods, "add", addSetItem); defineNative(vm, &vm->setMethods, "remove", removeSetItem); defineNative(vm, &vm->setMethods, "contains", containsSetItem); - defineNative(vm, &vm->setMethods, "containsElements", containsElementsSet); + defineNative(vm, &vm->setMethods, "containsAll", containsAllSet); defineNative(vm, &vm->setMethods, "toBool", boolNative); // Defined in util } diff --git a/tests/sets/containsElements.du b/tests/sets/containsElements.du deleted file mode 100644 index 7c1c8588..00000000 --- a/tests/sets/containsElements.du +++ /dev/null @@ -1,20 +0,0 @@ -/** - * containsElements.du - * - * Testing the containsElements method - * - * .containsElements() checks if a set contains all elements in a given list - */ -from UnitTest import UnitTest; - -class TestSetContainsElements < UnitTest { - testcontainsElements() { - var set_a = set("a",1,2,3); - this.assertTruthy(set_a.containsElements(["a"])); - this.assertTruthy(set_a.containsElements([1,2,3])); - this.assertTruthy(set_a.containsElements(["a",2,3,1])); - this.assertFalsey(set_a.containsElements(["a",2,3,1,"b"])); - } -} - -TestSetContainsElements().run(); \ No newline at end of file diff --git a/tests/sets/import.du b/tests/sets/import.du index e4f8d404..523e86d5 100644 --- a/tests/sets/import.du +++ b/tests/sets/import.du @@ -13,4 +13,4 @@ import "remove.du"; import "len.du"; import "toString.du"; import "toBool.du"; -import "containsElements.du"; +import "containsAll.du"; From 14d23ef603bb7c8194d76551f86dbb56fdd143ea Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Sun, 9 Oct 2022 23:42:51 +0530 Subject: [PATCH 050/104] Added missed file --- tests/sets/containsAll.du | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/sets/containsAll.du diff --git a/tests/sets/containsAll.du b/tests/sets/containsAll.du new file mode 100644 index 00000000..31a2a50a --- /dev/null +++ b/tests/sets/containsAll.du @@ -0,0 +1,20 @@ +/** + * containsAll.du + * + * Testing the containsAll method + * + * .containsAll() checks if a set contains all elements in a given list + */ +from UnitTest import UnitTest; + +class TestSetContainsAll < UnitTest { + testcontainsAll() { + var set_a = set("a",1,2,3); + this.assertTruthy(set_a.containsAll(["a"])); + this.assertTruthy(set_a.containsAll([1,2,3])); + this.assertTruthy(set_a.containsAll(["a",2,3,1])); + this.assertFalsey(set_a.containsAll(["a",2,3,1,"b"])); + } +} + +TestSetContainsAll().run(); \ No newline at end of file From f3f7a79b1fa7984c5287ca7258d96f2be090d093 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 10 Oct 2022 19:03:48 +0100 Subject: [PATCH 051/104] Correct documentation for Object module --- docs/docs/standard-lib/object.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/standard-lib/object.md b/docs/docs/standard-lib/object.md index 704e6cb5..88a8c67c 100644 --- a/docs/docs/standard-lib/object.md +++ b/docs/docs/standard-lib/object.md @@ -33,7 +33,7 @@ Returns a Result and unwraps to an Object upon success. ```cs class Test {} -Object.getClassRef("Test"); // +Object.getClassRef("Test").unwrap(); // ``` ### Object.createFrom(string) @@ -45,5 +45,5 @@ Returns a Result and unwraps to an Object upon success. ```cs class Test {} -Object.createFrom("Test"); // +Object.createFrom("Test").unwrap(); // ``` From 9b77aed7b5741a8a7607e0e7a4622785e16581ab Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 10 Oct 2022 19:07:15 +0100 Subject: [PATCH 052/104] Add to test suite --- tests/object/createFrom.du | 2 +- tests/object/import.du | 1 + tests/runTests.du | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/object/createFrom.du b/tests/object/createFrom.du index 7e81761c..d50ae649 100644 --- a/tests/object/createFrom.du +++ b/tests/object/createFrom.du @@ -14,7 +14,7 @@ class TestObjectCreateFrom < UnitTest { this.assertSuccess(Object.createFrom('Test')); this.assertError(Object.createFrom('Unknown')); - this.assertType(Object.createFrom('Test'), 'Test'); + this.assertType(Object.createFrom('Test').unwrap(), 'Test'); } } diff --git a/tests/object/import.du b/tests/object/import.du index 706e10de..2341a1fe 100644 --- a/tests/object/import.du +++ b/tests/object/import.du @@ -4,3 +4,4 @@ * General import file for all the Object module tests */ +import "createFrom.du"; \ No newline at end of file diff --git a/tests/runTests.du b/tests/runTests.du index 97300278..80b62f7d 100644 --- a/tests/runTests.du +++ b/tests/runTests.du @@ -40,6 +40,7 @@ import "modules/import.du"; import "imports/import.du"; import "random/import.du"; import "hashlib/import.du"; +import "object/import.du"; // If we got here no runtime errors were thrown, therefore all tests passed. print("All tests passed successfully!"); From d177afa50de1e97da1dbdeb7bea98fb5333f1f80 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 10 Oct 2022 19:13:27 +0100 Subject: [PATCH 053/104] Add note to Object module --- docs/docs/standard-lib/object.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/standard-lib/object.md b/docs/docs/standard-lib/object.md index 88a8c67c..4e4c742e 100644 --- a/docs/docs/standard-lib/object.md +++ b/docs/docs/standard-lib/object.md @@ -30,6 +30,8 @@ This method will attempt to get a class reference from the class name provided a Returns a Result and unwraps to an Object upon success. +**NOTE:** This currently only works if the class is defined in the global scope + ```cs class Test {} @@ -42,6 +44,8 @@ This method will attempt to create a new object from the class name provided as Returns a Result and unwraps to an Object upon success. +**NOTE:** This currently only works if the class is defined in the global scope + ```cs class Test {} From 6e78c45c24e2c439a7e20ae7ed933f7b9d3c4dc7 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:20:35 +0100 Subject: [PATCH 054/104] Do not inherit loop struct when defining a new compiler --- src/vm/compiler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index f530891e..d9199171 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -161,7 +161,6 @@ static void initCompiler(Parser *parser, Compiler *compiler, Compiler *parent, F if (parent != NULL) { compiler->class = parent->class; - compiler->loop = parent->loop; } compiler->type = type; @@ -2141,6 +2140,7 @@ static void forStatement(Compiler *compiler) { } static void breakStatement(Compiler *compiler) { + printf("%s\n", compiler->loop == NULL ? "null" : "no"); if (compiler->loop == NULL) { error(compiler->parser, "Cannot utilise 'break' outside of a loop."); return; From d97e9e018806a05bf6cd9d5f7dde22564840fc73 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:22:02 +0100 Subject: [PATCH 055/104] Remove print debug --- src/vm/compiler.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index d9199171..f5a33b98 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -2140,7 +2140,6 @@ static void forStatement(Compiler *compiler) { } static void breakStatement(Compiler *compiler) { - printf("%s\n", compiler->loop == NULL ? "null" : "no"); if (compiler->loop == NULL) { error(compiler->parser, "Cannot utilise 'break' outside of a loop."); return; From fc21b3917b55818a630388ae2847898d6db2fcb3 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:25:48 +0100 Subject: [PATCH 056/104] Add _name attribute to classes --- src/vm/object.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vm/object.c b/src/vm/object.c index 75da20b3..d2e23e6b 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -70,6 +70,14 @@ ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType initTable(&klass->publicProperties); initTable(&klass->publicConstantProperties); klass->annotations = NULL; + + push(vm, OBJ_VAL(klass)); + ObjString *nameString = copyString(vm, "_name", 5); + push(vm, OBJ_VAL(name)); + tableSet(vm, &klass->publicProperties, nameString, OBJ_VAL(name)); + pop(vm); + pop(vm); + return klass; } From 2994c320c818dde90364bc92cea520ac24104f45 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:30:17 +0100 Subject: [PATCH 057/104] Add a couple of tests for _name --- docs/docs/classes.md | 10 ++++++++++ tests/classes/properties.du | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/docs/docs/classes.md b/docs/docs/classes.md index 18320ff3..97d99dcd 100644 --- a/docs/docs/classes.md +++ b/docs/docs/classes.md @@ -318,6 +318,16 @@ class Test {} print(Test()._class); // ``` +### _name + +`_name` is a special attribute that is added to classes that returns a string representation of the class name. + +```cs +class Test {} + +print(Test.name); // Test +``` + ## Class variables A class variable is a variable that is defined on the class and not the instance. This means that all instances of the class will have access diff --git a/tests/classes/properties.du b/tests/classes/properties.du index 76fe6f63..b90c6e33 100644 --- a/tests/classes/properties.du +++ b/tests/classes/properties.du @@ -57,6 +57,11 @@ class TestClassProperties < UnitTest { this.assertEquals(Test()._class, Test); this.assertEquals(Inherit()._class, Inherit); } + + testClassName() { + this.assertEquals(Test._name, "Test"); + this.assertEquals(Inherit._name, "Inherit"); + } } TestClassProperties().run(); \ No newline at end of file From e3ecea3d19774c0da669452af8b0529642bf623c Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:41:09 +0100 Subject: [PATCH 058/104] Make _name a class constant --- src/vm/object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/object.c b/src/vm/object.c index d2e23e6b..97beb320 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -74,7 +74,7 @@ ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType push(vm, OBJ_VAL(klass)); ObjString *nameString = copyString(vm, "_name", 5); push(vm, OBJ_VAL(name)); - tableSet(vm, &klass->publicProperties, nameString, OBJ_VAL(name)); + tableSet(vm, &klass->publicConstantProperties, nameString, OBJ_VAL(name)); pop(vm); pop(vm); From ba6244b6639a76c15ce9dd881462f36273400842 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 11 Oct 2022 23:58:07 +0100 Subject: [PATCH 059/104] Push the correct value to stack to avoid GC --- src/vm/object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/object.c b/src/vm/object.c index 97beb320..d3d97c19 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -73,7 +73,7 @@ ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType push(vm, OBJ_VAL(klass)); ObjString *nameString = copyString(vm, "_name", 5); - push(vm, OBJ_VAL(name)); + push(vm, OBJ_VAL(nameString)); tableSet(vm, &klass->publicConstantProperties, nameString, OBJ_VAL(name)); pop(vm); pop(vm); From c21a4e6edaae4f9e9b099a376ea09d90d59e8d0e Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 12 Oct 2022 22:18:33 -0700 Subject: [PATCH 060/104] add Env.clearAll function --- docs/docs/standard-lib/env.md | 10 ++++++++++ src/optionals/env/env.c | 29 ++++++++++++++++++++++++++++- src/optionals/env/env.h | 1 + tests/env/env.du | 12 +++++++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 0d67dabf..51409d6c 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -48,6 +48,16 @@ Env.set("key", nil); // Remove env var Env.set("key", 10); // set() arguments must be a string or nil. ``` +### Env.clearAll() + +Clears all set environment variables. + +Note: This is not available on Windows systems. + +```cs +Env.clearAll(); +``` + ### Env.readFile(string: path -> optional) To read environment variables from a file this helper method is provided. diff --git a/src/optionals/env/env.c b/src/optionals/env/env.c index 7d2458a6..fbb8f2a1 100644 --- a/src/optionals/env/env.c +++ b/src/optionals/env/env.c @@ -78,6 +78,31 @@ static Value set(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } +#ifndef _WIN32 +static Value clearAll(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 0) { + runtimeError(vm, "clearAll() does not take arguments (%d given).", argCount); + return EMPTY_VAL; + } + + extern char **environ; + + char **env = environ; + for (; *env; ++env) { + char *name = strtok(*env, "="); + + int ret = unsetenv(name); + if (ret == -1) { + ERROR_RESULT; + } + } + + return newResultSuccess(vm, NIL_VAL); +} +#endif + Value createEnvModule(DictuVM *vm) { ObjClosure *closure = compileModuleToClosure(vm, "Env", DICTU_ENV_SOURCE); @@ -92,7 +117,9 @@ Value createEnvModule(DictuVM *vm) { */ defineNative(vm, &closure->function->module->values, "get", get); defineNative(vm, &closure->function->module->values, "set", set); - +#ifndef _WIN32 + defineNative(vm, &closure->function->module->values, "clearAll", clearAll); +#endif pop(vm); return OBJ_VAL(closure); diff --git a/src/optionals/env/env.h b/src/optionals/env/env.h index bd8948e8..a3bfb0e5 100644 --- a/src/optionals/env/env.h +++ b/src/optionals/env/env.h @@ -3,6 +3,7 @@ #include #include +#include #include "../optionals.h" #include "../../vm/vm.h" diff --git a/tests/env/env.du b/tests/env/env.du index fd8cb368..736b5bca 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -2,13 +2,14 @@ * end.du * * Testing the Env functions: - * - get(), set(), readFile() + * - get(), set(), clearAll(), readFile() * */ from UnitTest import UnitTest; import Env; import Path; +import System; class TestEnvModule < UnitTest { testEnvGet() { @@ -23,6 +24,15 @@ class TestEnvModule < UnitTest { this.assertEquals(Env.get("test"), nil); } + testEnvClearAll() { + if (System.platform != "windows") { + this.assertTruthy(Env.set("test", "test").success()); + this.assertTruthy(Env.clearAll().success()); + this.assertEquals(Env.get("test"), nil); + } + + } + testEnvRead() { const result = Env.readFile(Path.dirname(__file__) + '/.env'); this.assertEquals(Env.get("TEST"), "10"); From 5d7a61faa1c141538505898cfe7fdbbf80818db2 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Mon, 17 Oct 2022 19:36:17 -0700 Subject: [PATCH 061/104] windows support Signed-off-by: Brian Downs --- src/optionals/env/env.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/optionals/env/env.c b/src/optionals/env/env.c index fbb8f2a1..680afe44 100644 --- a/src/optionals/env/env.c +++ b/src/optionals/env/env.c @@ -78,7 +78,6 @@ static Value set(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } -#ifndef _WIN32 static Value clearAll(DictuVM *vm, int argCount, Value *args) { UNUSED(args); @@ -87,21 +86,27 @@ static Value clearAll(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } + char **env; +#ifdef _WIN32 + extern char **_environ; + env = _environ; +#else extern char **environ; - - char **env = environ; + env = environ; +#endif for (; *env; ++env) { char *name = strtok(*env, "="); - +#ifdef _WIN32 + unsetenv(name); +#else int ret = unsetenv(name); if (ret == -1) { ERROR_RESULT; } } - +#endif return newResultSuccess(vm, NIL_VAL); } -#endif Value createEnvModule(DictuVM *vm) { ObjClosure *closure = compileModuleToClosure(vm, "Env", DICTU_ENV_SOURCE); @@ -117,9 +122,7 @@ Value createEnvModule(DictuVM *vm) { */ defineNative(vm, &closure->function->module->values, "get", get); defineNative(vm, &closure->function->module->values, "set", set); -#ifndef _WIN32 defineNative(vm, &closure->function->module->values, "clearAll", clearAll); -#endif pop(vm); return OBJ_VAL(closure); From 8d21ce959348778d2b538b07af20cdd976f7a82b Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Mon, 17 Oct 2022 19:38:28 -0700 Subject: [PATCH 062/104] update docs Signed-off-by: Brian Downs --- docs/docs/standard-lib/env.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 51409d6c..1131cf69 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -52,8 +52,6 @@ Env.set("key", 10); // set() arguments must be a string or nil. Clears all set environment variables. -Note: This is not available on Windows systems. - ```cs Env.clearAll(); ``` From 0cb68a99645ff19f883146a4e6acafb74713042a Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Mon, 17 Oct 2022 21:32:41 -0700 Subject: [PATCH 063/104] update test Signed-off-by: Brian Downs --- tests/env/env.du | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/env/env.du b/tests/env/env.du index 736b5bca..64eb6aeb 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -25,12 +25,9 @@ class TestEnvModule < UnitTest { } testEnvClearAll() { - if (System.platform != "windows") { - this.assertTruthy(Env.set("test", "test").success()); - this.assertTruthy(Env.clearAll().success()); - this.assertEquals(Env.get("test"), nil); - } - + this.assertTruthy(Env.set("test", "test").success()); + this.assertTruthy(Env.clearAll().success()); + this.assertEquals(Env.get("test"), nil); } testEnvRead() { From 739e755ee88e8ab6a380d002b67f206cf4502479 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Wed, 19 Oct 2022 21:55:02 -0700 Subject: [PATCH 064/104] add proxy example Signed-off-by: Brian Downs --- examples/reverse_proxy.du | 106 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 examples/reverse_proxy.du diff --git a/examples/reverse_proxy.du b/examples/reverse_proxy.du new file mode 100644 index 00000000..b528e088 --- /dev/null +++ b/examples/reverse_proxy.du @@ -0,0 +1,106 @@ +// To test the code below, run the commmand below to setup +// some backend servers to connect to: +// +// for i in $(seq 10001 10003); do +// socat tcp-listen:${i},reuseaddr,fork exec:cat,nofork & +// done + +import Log; +import Random; +import Socket; +import System; + +const readBuffer = 2048; + +class Server { + init(var server, var port) {} +} + +const backendServers = [ + Server("127.0.0.1", 10001), + Server("127.0.0.1", 10002), + Server("127.0.0.1", 10003) +]; + +def selectServer() { + const i = Random.range(0, backendServers.len()-1); + return backendServers[i]; +} + +// main +{ + const log = Log.new(Log.stdout).unwrap(); + + log.println("Starting proxy server..."); + + var res = Socket.create(Socket.AF_INET, Socket.SOCK_STREAM); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + var socket = res.unwrap(); + + res = socket.setsockopt(Socket.SOL_SOCKET, Socket.SO_REUSEADDR); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + + res = socket.bind("127.0.0.1", 10000); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + + res = socket.listen(10000); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + + var [client, address] = socket.accept().unwrap(); + var remote = nil; + + while { + if (not remote) { + res = Socket.create(Socket.AF_INET, Socket.SOCK_STREAM); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + remote = res.unwrap(); + + const backendServer = selectServer(); + res = remote.connect(backendServer.server, backendServer.port); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + } + + res = client.recv(readBuffer); + if (not res.success()) { + log.println(res.unwrapError()); + break; + } + var userInput = res.unwrap(); + + res = remote.write(userInput); + if (not res.success()) { + log.println(res.unwrapError()); + break; + } + + res = remote.recv(readBuffer); + if (not res.success()) { + log.println(res.unwrapError()); + break; + } + + res = client.write(res.unwrap()); + if (not res.success()) { + log.println(res.unwrapError()); + break; + } + } + remote.close(); + socket.close(); + + log.println("Shutting down proxy server..."); + + System.exit(0); +} From 6a902d44ccd205ec68db9aecf0add7aafe80a4f7 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Thu, 20 Oct 2022 19:32:28 +0530 Subject: [PATCH 065/104] Fixed list unpacking issue --- src/vm/compiler.c | 40 +++++++++++++++++++++++++++++++ tests/variables/list-unpacking.du | 10 ++++++++ 2 files changed, 50 insertions(+) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 3f21a925..4ee47dfa 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -2364,6 +2364,44 @@ static void whileStatement(Compiler *compiler) { endLoop(compiler); } +static void unpackListStatement(Compiler *compiler){ + int varCount = 0; + Token variables[255]; + do { + consume(compiler, TOKEN_IDENTIFIER, "Expect variable name."); + variables[varCount] = compiler->parser->previous; + varCount++; + } while (match(compiler, TOKEN_COMMA)); + + consume(compiler, TOKEN_RIGHT_BRACKET, "Expect ']' after list destructure."); + consume(compiler, TOKEN_EQUAL, "Expect '=' after list destructure."); + + expression(compiler); + + emitBytes(compiler, OP_UNPACK_LIST, varCount); + + + for(int i=varCount-1;i>-1;i--){ + Token token=variables[i]; + + uint8_t setOp; + int arg = resolveLocal(compiler, &token, false); + if (arg != -1) { + setOp = OP_SET_LOCAL; + } else if ((arg = resolveUpvalue(compiler, &token)) != -1) { + setOp = OP_SET_UPVALUE; + } else { + arg = identifierConstant(compiler, &token); + setOp = OP_SET_MODULE; + } + checkConst(compiler, setOp, arg); + emitBytes(compiler, setOp, (uint8_t) arg); + emitByte(compiler, OP_POP); + } + + consume(compiler, TOKEN_SEMICOLON, "Expect ';' after variable declaration."); +} + static void synchronize(Parser *parser) { parser->panicMode = false; @@ -2444,6 +2482,8 @@ static void statement(Compiler *compiler) { breakStatement(compiler); } else if (match(compiler, TOKEN_WHILE)) { whileStatement(compiler); + } else if (match(compiler, TOKEN_LEFT_BRACKET)) { + unpackListStatement(compiler); } else if (match(compiler, TOKEN_LEFT_BRACE)) { Parser *parser = compiler->parser; Token previous = parser->previous; diff --git a/tests/variables/list-unpacking.du b/tests/variables/list-unpacking.du index 008306c9..1e9ebcac 100644 --- a/tests/variables/list-unpacking.du +++ b/tests/variables/list-unpacking.du @@ -10,6 +10,16 @@ assert(a == 1); assert(b == 2); assert(c == 3); + +var o,i,g; + +[o,i,g]=[111,222,333]; + +assert(o == 111); +assert(i == 222); +assert(g == 333); + + var [d, e, f] = [1 + 1, 2 + 2, 3 + 3]; assert(d == 2); From 3c8aae7d2715f8c53d2d2eeb5c2240eb7b417f86 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Thu, 20 Oct 2022 12:20:19 -0700 Subject: [PATCH 066/104] remove downstream from loop Signed-off-by: Brian Downs --- examples/reverse_proxy.du | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/reverse_proxy.du b/examples/reverse_proxy.du index b528e088..41155921 100644 --- a/examples/reverse_proxy.du +++ b/examples/reverse_proxy.du @@ -55,23 +55,20 @@ def selectServer() { } var [client, address] = socket.accept().unwrap(); - var remote = nil; - while { - if (not remote) { - res = Socket.create(Socket.AF_INET, Socket.SOCK_STREAM); - if (not res.success()) { - log.fatalln(res.unwrapError()); - } - remote = res.unwrap(); - - const backendServer = selectServer(); - res = remote.connect(backendServer.server, backendServer.port); - if (not res.success()) { - log.fatalln(res.unwrapError()); - } - } + res = Socket.create(Socket.AF_INET, Socket.SOCK_STREAM); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + var remote = res.unwrap(); + const backendServer = selectServer(); + res = remote.connect(backendServer.server, backendServer.port); + if (not res.success()) { + log.fatalln(res.unwrapError()); + } + + while { res = client.recv(readBuffer); if (not res.success()) { log.println(res.unwrapError()); From 227e966d30bf8c1ee8e1cd4b7d1a7c4df30622b4 Mon Sep 17 00:00:00 2001 From: Manohar Kakumani <42714264+manoharkakumani@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:38:26 +0530 Subject: [PATCH 067/104] Self Import Bug fix Fixed self import Segmentation fault --- src/vm/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/vm.c b/src/vm/vm.c index ef2e27f8..bfb2cf3c 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1589,7 +1589,7 @@ static DictuInterpretResult run(DictuVM *vm) { module->path = dirname(vm, path, strlen(path)); vm->lastModule = module; pop(vm); - + tableSet(vm, &vm->modules, fileName, OBJ_VAL(module)); push(vm, OBJ_VAL(module)); ObjFunction *function = compile(vm, module, source); pop(vm); From 9935a1ee30f8cc89363f98d4667977557e0d5404 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 23 Oct 2022 14:50:34 +0100 Subject: [PATCH 068/104] Update filename check in import --- src/vm/vm.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vm/vm.c b/src/vm/vm.c index bfb2cf3c..4f9417ac 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1565,31 +1565,32 @@ static DictuInterpretResult run(DictuVM *vm) { ObjString *fileName = READ_STRING(); Value moduleVal; + char path[PATH_MAX]; + if (!resolvePath(frame->closure->function->module->path->chars, fileName->chars, path)) { + RUNTIME_ERROR("Could not open file \"%s\".", fileName->chars); + } + + ObjString *pathObj = copyString(vm, path, strlen(path)); + push(vm, OBJ_VAL(pathObj)); + // If we have imported this file already, skip. - if (tableGet(&vm->modules, fileName, &moduleVal)) { + if (tableGet(&vm->modules, pathObj, &moduleVal)) { + pop(vm); vm->lastModule = AS_MODULE(moduleVal); push(vm, NIL_VAL); DISPATCH(); } - char path[PATH_MAX]; - if (!resolvePath(frame->closure->function->module->path->chars, fileName->chars, path)) { - RUNTIME_ERROR("Could not open file \"%s\".", fileName->chars); - } - char *source = readFile(vm, path); if (source == NULL) { RUNTIME_ERROR("Could not open file \"%s\".", fileName->chars); } - ObjString *pathObj = copyString(vm, path, strlen(path)); - push(vm, OBJ_VAL(pathObj)); ObjModule *module = newModule(vm, pathObj); module->path = dirname(vm, path, strlen(path)); vm->lastModule = module; pop(vm); - tableSet(vm, &vm->modules, fileName, OBJ_VAL(module)); push(vm, OBJ_VAL(module)); ObjFunction *function = compile(vm, module, source); pop(vm); From 839fb3a294898dc687e2599faa8fac070610a468 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 23 Oct 2022 12:46:17 -0700 Subject: [PATCH 069/104] make impl nix only Signed-off-by: Brian Downs --- docs/docs/standard-lib/env.md | 2 ++ src/optionals/env/env.c | 21 ++++++++------------- tests/env/env.du | 8 +++++--- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 1131cf69..51409d6c 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -52,6 +52,8 @@ Env.set("key", 10); // set() arguments must be a string or nil. Clears all set environment variables. +Note: This is not available on Windows systems. + ```cs Env.clearAll(); ``` diff --git a/src/optionals/env/env.c b/src/optionals/env/env.c index 680afe44..51aea6b8 100644 --- a/src/optionals/env/env.c +++ b/src/optionals/env/env.c @@ -78,6 +78,7 @@ static Value set(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } +#ifndef _WIN32 static Value clearAll(DictuVM *vm, int argCount, Value *args) { UNUSED(args); @@ -86,27 +87,19 @@ static Value clearAll(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - char **env; -#ifdef _WIN32 - extern char **_environ; - env = _environ; -#else extern char **environ; - env = environ; -#endif - for (; *env; ++env) { - char *name = strtok(*env, "="); -#ifdef _WIN32 - unsetenv(name); -#else + + for (; *environ; ++environ) { + char *name = strtok(*environ, "="); int ret = unsetenv(name); if (ret == -1) { ERROR_RESULT; } } -#endif + return newResultSuccess(vm, NIL_VAL); } +#endif Value createEnvModule(DictuVM *vm) { ObjClosure *closure = compileModuleToClosure(vm, "Env", DICTU_ENV_SOURCE); @@ -122,7 +115,9 @@ Value createEnvModule(DictuVM *vm) { */ defineNative(vm, &closure->function->module->values, "get", get); defineNative(vm, &closure->function->module->values, "set", set); +#ifndef _WIN32 defineNative(vm, &closure->function->module->values, "clearAll", clearAll); +#endif pop(vm); return OBJ_VAL(closure); diff --git a/tests/env/env.du b/tests/env/env.du index 64eb6aeb..ea9ed88e 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -25,9 +25,11 @@ class TestEnvModule < UnitTest { } testEnvClearAll() { - this.assertTruthy(Env.set("test", "test").success()); - this.assertTruthy(Env.clearAll().success()); - this.assertEquals(Env.get("test"), nil); + if (System.platform != "windows") { + this.assertTruthy(Env.set("test", "test").success()); + this.assertTruthy(Env.clearAll().success()); + this.assertEquals(Env.get("test"), nil); + } } testEnvRead() { From a514ff9a05989d132cdeb5fbb68c4dcf379009a3 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 23 Oct 2022 12:49:24 -0700 Subject: [PATCH 070/104] remove unnecessary include Signed-off-by: Brian Downs --- src/optionals/env/env.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/optionals/env/env.h b/src/optionals/env/env.h index a3bfb0e5..bd8948e8 100644 --- a/src/optionals/env/env.h +++ b/src/optionals/env/env.h @@ -3,7 +3,6 @@ #include #include -#include #include "../optionals.h" #include "../../vm/vm.h" From 563443a4787528ab041498a16cb3a4a6afc49061 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 30 Oct 2022 21:01:28 -0700 Subject: [PATCH 071/104] initial http client impl Signed-off-by: Brian Downs --- examples/http_client.du | 48 ++++++ src/optionals/http/http.c | 343 +++++++++++++++++++++++++++++++++++++- 2 files changed, 388 insertions(+), 3 deletions(-) create mode 100644 examples/http_client.du diff --git a/examples/http_client.du b/examples/http_client.du new file mode 100644 index 00000000..c5fa38d8 --- /dev/null +++ b/examples/http_client.du @@ -0,0 +1,48 @@ +import HTTP; +import JSON; +import System; + +// main +{ + const opts = { + "timeout": 20, + "headers": [ + "Content-Type: application/json", + "Accept: application/json", + "User-Agent: Dictu" + ], + "insecure": true, + "keyFile": "", + "certFile": "", + "keyPasswd": "" + }; + const ret = HTTP.newClient(opts); + if (not ret.success()) { + print(res.unwrapError()); + System.exit(1); + } + var httpClient = ret.unwrap(); + + var res = httpClient.get("https://httpbin.org/get"); + if (not res.success()) { + print(res.unwrapError()); + System.exit(1); + } + print("Status Code: {}".format(res.unwrap().statusCode)); + print(res.unwrap().json().unwrap()); + + res = httpClient.get("https://httpbin.org/uuid"); + if (not res.success()) { + print(res.unwrapError()); + System.exit(1); + } + //print(res.unwrap().statusCode); + //print(res); +} + + + + + + + diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 472e78ff..beb3e106 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -1,9 +1,9 @@ +#include #include #include #include #include "http.h" - #include "http-source.h" #define HTTP_METHOD_GET "GET" @@ -233,6 +233,8 @@ #define HTTP_MAX_HEADER_BYTES 1 << 20 // 1 MB +#define DEFAULT_REQUEST_TIMEOUT 20 + static void createResponse(DictuVM *vm, Response *response) { response->vm = vm; response->headers = newList(vm); @@ -408,7 +410,7 @@ static Value get(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - long timeout = 20; + long timeout = DEFAULT_REQUEST_TIMEOUT; ObjList *headers = NULL; if (argCount == 3) { @@ -499,7 +501,7 @@ static Value post(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - long timeout = 20; + long timeout = DEFAULT_REQUEST_TIMEOUT; ObjDict *postValuesDict = NULL; ObjString *postValueString = NULL; ObjList *headers = NULL; @@ -609,6 +611,339 @@ static Value post(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } +typedef struct { + CURL *curl; + // char *keyFile; + // char *certFile; +} HttpClient; + +#define AS_HTTP_CLIENT(v) ((HttpClient*)AS_ABSTRACT(v)->data) + +struct curl_slist *headerChunk = NULL; + +void freeHttpClient(DictuVM *vm, ObjAbstract *abstract) { + HttpClient *httpClient = (HttpClient*)abstract->data; + + if (headerChunk) { + curl_slist_free_all(headerChunk); + } + + curl_easy_cleanup(httpClient->curl); + curl_global_cleanup(); + + FREE(vm, HttpClient, abstract->data); +} + +char *httpClientToString(ObjAbstract *abstract) { + UNUSED(abstract); + + char *httpClientString = malloc(sizeof(char) * 12); + snprintf(httpClientString, 12, ""); + return httpClientString; +} + +static Value httpClientSetTimeout(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setTimeout() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[1])) { + runtimeError(vm, "timeout value must be a number"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + curl_easy_setopt(httpClient->curl, CURLOPT_TIMEOUT, AS_NUMBER(args[1])); + + return NIL_VAL; +} + +static Value httpClientSetInsecure(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setInsecure() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_BOOL(args[1])) { + runtimeError(vm, "insecure value must be a bool"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYPEER, 0); + + return NIL_VAL; +} + +static Value httpClientSetHeaders(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setHeaders() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_LIST(args[1])) { + runtimeError(vm, "hedaers value must be a ist"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + ObjList *headers = AS_LIST(args[1]); + + headerChunk = NULL; + + for (int h = 0; h < headers->values.count; h++) { + headerChunk = curl_slist_append(headerChunk, AS_STRING(headers->values.values[h])->chars); + } + + curl_easy_setopt(httpClient->curl, CURLOPT_HTTPHEADER, headerChunk); + + return NIL_VAL; +} + +static Value httpClientSetKeyFile(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setKeyFile() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "keyFile value must be a string"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + + return NIL_VAL; +} + +static Value httpClientSetCertFile(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setCertFile() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "certFile value must be a string"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + + return NIL_VAL; +} + +static Value httpClientSetKeyPasswd(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setKeyPasswd() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "keyPasswd value must be a string"); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + + return NIL_VAL; +} + +static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "get() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "URL passed to get() must be a string."); + return EMPTY_VAL; + } + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + CURLcode curlResponse; + + if (httpClient->curl) { + Response response; + createResponse(vm, &response); + char *url = AS_CSTRING(args[1]); + + curl_easy_setopt(httpClient->curl, CURLOPT_URL, url); + curl_easy_setopt(httpClient->curl, CURLOPT_WRITEFUNCTION, writeResponse); + curl_easy_setopt(httpClient->curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(httpClient->curl, CURLOPT_HEADERDATA, &response); + + curlResponse = curl_easy_perform(httpClient->curl); + + if (curlResponse != CURLE_OK) { + pop(vm); + + char *errorString = (char *)curl_easy_strerror(curlResponse); + return newResultError(vm, errorString); + } + + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient->curl, response))); + } + + pop(vm); + + char *errorString = (char *)curl_easy_strerror(CURLE_FAILED_INIT); + return newResultError(vm, errorString); +} + +ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { + ObjAbstract *abstract = newAbstract(vm, freeHttpClient, httpClientToString); + push(vm, OBJ_VAL(abstract)); + + HttpClient *httpClient = ALLOCATE(vm, HttpClient, 1); + httpClient->curl = curl_easy_init(); + + curl_easy_setopt(httpClient->curl, CURLOPT_HEADERFUNCTION, writeHeaders); + + if (opts->count != 0) { + for (int i = 0; i <= opts->capacityMask; i++) { + DictItem *entry = &opts->entries[i]; + if (IS_EMPTY(entry->key)) { + continue; + } + + char *key; + + if (IS_STRING(entry->key)) { + ObjString *s = AS_STRING(entry->key); + key = s->chars; + } else { + runtimeError(vm, "HTTP client options key must be a string"); + return abstract; + } + + if (strstr(key, "timeout")) { + if (IS_EMPTY(entry->value)) { + continue; + } + if (!IS_NUMBER(entry->value)) { + runtimeError(vm, "HTTP client option \"timeout\" value must be a number"); + return abstract; + } + + curl_easy_setopt(httpClient->curl, CURLOPT_TIMEOUT, AS_NUMBER(entry->value)); + } else if (strstr(key, "headers")) { + if (IS_EMPTY(entry->value)) { + continue; + } + + if (!IS_LIST(entry->value)) { + runtimeError(vm, "HTTP client option \"headers\" value must be a list"); + return abstract; + } + + ObjList *headers = AS_LIST(entry->value); + + for (int h = 0; h < headers->values.count; h++) { + headerChunk = curl_slist_append(headerChunk, AS_STRING(headers->values.values[h])->chars); + } + + curl_easy_setopt(httpClient->curl, CURLOPT_HTTPHEADER, headerChunk); + } else if (strstr(key, "insecure")) { + if (!IS_BOOL(entry->value)) { + runtimeError(vm, "HTTP client option \"insecure\" value must be a bool"); + return abstract; + } + + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYPEER, 0); + } else if (strstr(key, "keyFile")) { + if (!IS_STRING(entry->value)) { + runtimeError(vm, "HTTP client option \"keyFile\" value must be a string"); + return abstract; + } + + char *keyFile = AS_STRING(entry->value)->chars; + if (keyFile[0] == '\0') { + continue; + } + + curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, keyFile); + } else if (strstr(key, "certFile")) { + if (!IS_STRING(entry->value)) { + runtimeError(vm, "HTTP client option \"certFile\" value must be a string"); + return abstract; + } + + char *certFile = AS_STRING(entry->value)->chars; + if (certFile[0] == '\0') { + continue; + } + + curl_easy_setopt(httpClient->curl, CURLOPT_SSLCERT, certFile); + } else if (strstr(key, "keyPasswd")) { + if (!IS_STRING(entry->value)) { + runtimeError(vm, "HTTP client option key \"keyPasswd\" value must be a string"); + return abstract; + } + + char *keyPasswd = AS_STRING(entry->value)->chars; + if (keyPasswd[0] == '\0') { + continue; + } + + curl_easy_setopt(httpClient->curl, CURLOPT_KEYPASSWD, keyPasswd); + } + } + } + + abstract->data = httpClient; + + /** + * Setup HTTP object methods + */ + defineNative(vm, &abstract->values, "get", httpClientGet); + defineNative(vm, &abstract->values, "post", httpClientGet); + defineNative(vm, &abstract->values, "delete", httpClientGet); + defineNative(vm, &abstract->values, "put", httpClientGet); + defineNative(vm, &abstract->values, "patch", httpClientGet); + defineNative(vm, &abstract->values, "head", httpClientGet); + defineNative(vm, &abstract->values, "setTimeout", httpClientSetTimeout); + defineNative(vm, &abstract->values, "addHeader", httpClientSetHeaders); + defineNative(vm, &abstract->values, "setInsecure", httpClientSetInsecure); + defineNative(vm, &abstract->values, "setKeyFile", httpClientSetKeyFile); + defineNative(vm, &abstract->values, "setCertfile", httpClientSetCertFile); + defineNative(vm, &abstract->values, "setKeyPasswd", httpClientSetKeyPasswd); + pop(vm); + + return abstract; +} + +static Value newClient(DictuVM *vm, int argCount, Value *args) { + if (argCount > 1) { + runtimeError(vm, "newClient() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_DICT(args[0])) { + runtimeError(vm, "Options value passed to newClient() must be a dict."); + return EMPTY_VAL; + } + + ObjDict *opts = AS_DICT(args[0]); + + ObjAbstract *hc = newHttpClient(vm, opts); + return newResultSuccess(vm, OBJ_VAL(hc)); +} + Value createHTTPModule(DictuVM *vm) { ObjClosure *closure = compileModuleToClosure(vm, "HTTP", DICTU_HTTP_SOURCE); @@ -853,6 +1188,8 @@ Value createHTTPModule(DictuVM *vm) { defineNative(vm, &module->values, "get", get); defineNative(vm, &module->values, "post", post); + defineNative(vm, &module->values, "newClient", newClient); + pop(vm); return OBJ_VAL(closure); From 251d57cf0bf1f43038b108d7fd9d1df9e1c3309f Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 30 Oct 2022 21:18:05 -0700 Subject: [PATCH 072/104] simplify client Signed-off-by: Brian Downs --- src/optionals/http/http.c | 58 ++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index beb3e106..7e3f41e7 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -611,11 +611,7 @@ static Value post(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } -typedef struct { - CURL *curl; - // char *keyFile; - // char *certFile; -} HttpClient; +typedef CURL *HttpClient; #define AS_HTTP_CLIENT(v) ((HttpClient*)AS_ABSTRACT(v)->data) @@ -628,7 +624,7 @@ void freeHttpClient(DictuVM *vm, ObjAbstract *abstract) { curl_slist_free_all(headerChunk); } - curl_easy_cleanup(httpClient->curl); + curl_easy_cleanup(httpClient); curl_global_cleanup(); FREE(vm, HttpClient, abstract->data); @@ -655,7 +651,7 @@ static Value httpClientSetTimeout(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient->curl, CURLOPT_TIMEOUT, AS_NUMBER(args[1])); + curl_easy_setopt(httpClient, CURLOPT_TIMEOUT, AS_NUMBER(args[1])); return NIL_VAL; } @@ -673,9 +669,9 @@ static Value httpClientSetInsecure(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYSTATUS, 0); - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); return NIL_VAL; } @@ -701,7 +697,7 @@ static Value httpClientSetHeaders(DictuVM *vm, int argCount, Value *args) { headerChunk = curl_slist_append(headerChunk, AS_STRING(headers->values.values[h])->chars); } - curl_easy_setopt(httpClient->curl, CURLOPT_HTTPHEADER, headerChunk); + curl_easy_setopt(httpClient, CURLOPT_HTTPHEADER, headerChunk); return NIL_VAL; } @@ -719,7 +715,7 @@ static Value httpClientSetKeyFile(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); return NIL_VAL; } @@ -737,7 +733,7 @@ static Value httpClientSetCertFile(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); return NIL_VAL; } @@ -755,7 +751,7 @@ static Value httpClientSetKeyPasswd(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); return NIL_VAL; } @@ -775,17 +771,17 @@ static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { CURLcode curlResponse; - if (httpClient->curl) { + if (httpClient) { Response response; createResponse(vm, &response); char *url = AS_CSTRING(args[1]); - curl_easy_setopt(httpClient->curl, CURLOPT_URL, url); - curl_easy_setopt(httpClient->curl, CURLOPT_WRITEFUNCTION, writeResponse); - curl_easy_setopt(httpClient->curl, CURLOPT_WRITEDATA, &response); - curl_easy_setopt(httpClient->curl, CURLOPT_HEADERDATA, &response); + curl_easy_setopt(httpClient, CURLOPT_URL, url); + curl_easy_setopt(httpClient, CURLOPT_WRITEFUNCTION, writeResponse); + curl_easy_setopt(httpClient, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(httpClient, CURLOPT_HEADERDATA, &response); - curlResponse = curl_easy_perform(httpClient->curl); + curlResponse = curl_easy_perform(httpClient); if (curlResponse != CURLE_OK) { pop(vm); @@ -794,7 +790,7 @@ static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } - return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient->curl, response))); + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient, response))); } pop(vm); @@ -808,9 +804,9 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { push(vm, OBJ_VAL(abstract)); HttpClient *httpClient = ALLOCATE(vm, HttpClient, 1); - httpClient->curl = curl_easy_init(); + httpClient = curl_easy_init(); - curl_easy_setopt(httpClient->curl, CURLOPT_HEADERFUNCTION, writeHeaders); + curl_easy_setopt(httpClient, CURLOPT_HEADERFUNCTION, writeHeaders); if (opts->count != 0) { for (int i = 0; i <= opts->capacityMask; i++) { @@ -838,7 +834,7 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { return abstract; } - curl_easy_setopt(httpClient->curl, CURLOPT_TIMEOUT, AS_NUMBER(entry->value)); + curl_easy_setopt(httpClient, CURLOPT_TIMEOUT, AS_NUMBER(entry->value)); } else if (strstr(key, "headers")) { if (IS_EMPTY(entry->value)) { continue; @@ -855,16 +851,16 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { headerChunk = curl_slist_append(headerChunk, AS_STRING(headers->values.values[h])->chars); } - curl_easy_setopt(httpClient->curl, CURLOPT_HTTPHEADER, headerChunk); + curl_easy_setopt(httpClient, CURLOPT_HTTPHEADER, headerChunk); } else if (strstr(key, "insecure")) { if (!IS_BOOL(entry->value)) { runtimeError(vm, "HTTP client option \"insecure\" value must be a bool"); return abstract; } - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYSTATUS, 0); - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(httpClient->curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); } else if (strstr(key, "keyFile")) { if (!IS_STRING(entry->value)) { runtimeError(vm, "HTTP client option \"keyFile\" value must be a string"); @@ -876,7 +872,7 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { continue; } - curl_easy_setopt(httpClient->curl, CURLOPT_SSLKEY, keyFile); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, keyFile); } else if (strstr(key, "certFile")) { if (!IS_STRING(entry->value)) { runtimeError(vm, "HTTP client option \"certFile\" value must be a string"); @@ -888,7 +884,7 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { continue; } - curl_easy_setopt(httpClient->curl, CURLOPT_SSLCERT, certFile); + curl_easy_setopt(httpClient, CURLOPT_SSLCERT, certFile); } else if (strstr(key, "keyPasswd")) { if (!IS_STRING(entry->value)) { runtimeError(vm, "HTTP client option key \"keyPasswd\" value must be a string"); @@ -900,7 +896,7 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { continue; } - curl_easy_setopt(httpClient->curl, CURLOPT_KEYPASSWD, keyPasswd); + curl_easy_setopt(httpClient, CURLOPT_KEYPASSWD, keyPasswd); } } } From d47942e6d9f0c36e486932a83bf530ca585c23f0 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 30 Oct 2022 22:17:57 -0700 Subject: [PATCH 073/104] increase allocation Signed-off-by: Brian Downs --- src/optionals/http/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 7e3f41e7..d1756b0f 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -633,7 +633,7 @@ void freeHttpClient(DictuVM *vm, ObjAbstract *abstract) { char *httpClientToString(ObjAbstract *abstract) { UNUSED(abstract); - char *httpClientString = malloc(sizeof(char) * 12); + char *httpClientString = malloc(sizeof(char) * 13); snprintf(httpClientString, 12, ""); return httpClientString; } From 0ad3169e787464d359160f493f3e997f53772863 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Sun, 30 Oct 2022 22:21:32 -0700 Subject: [PATCH 074/104] increase allocation Signed-off-by: Brian Downs --- src/optionals/http/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index d1756b0f..8f0a3757 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -634,7 +634,7 @@ char *httpClientToString(ObjAbstract *abstract) { UNUSED(abstract); char *httpClientString = malloc(sizeof(char) * 13); - snprintf(httpClientString, 12, ""); + snprintf(httpClientString, 13, ""); return httpClientString; } From 62a2be4deebf14e09ecf92ceadbec89ad2b92684 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Mon, 31 Oct 2022 19:55:20 -0700 Subject: [PATCH 075/104] resolve curl handle reuse issue Signed-off-by: Brian Downs --- examples/http_client.du | 7 +++++-- src/optionals/http/http.c | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/http_client.du b/examples/http_client.du index c5fa38d8..d44b9209 100644 --- a/examples/http_client.du +++ b/examples/http_client.du @@ -36,8 +36,11 @@ import System; print(res.unwrapError()); System.exit(1); } - //print(res.unwrap().statusCode); - //print(res); + print(res.unwrap().statusCode); + const uuid = res.unwrap().json().unwrap()["uuid"]; + print("UUID: {}".format(uuid)); + + System.exit(0); } diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 8f0a3757..991df8b3 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -352,7 +352,7 @@ static bool setRequestHeaders(DictuVM *vm, struct curl_slist *list, CURL *curl, return true; } -static ObjInstance *endRequest(DictuVM *vm, CURL *curl, Response response) { +static ObjInstance *endRequest(DictuVM *vm, CURL *curl, Response response, bool cleanup) { // Get status code curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.statusCode); ObjString *content; @@ -397,10 +397,12 @@ static ObjInstance *endRequest(DictuVM *vm, CURL *curl, Response response) { // Pop headers from createResponse pop(vm); - /* always cleanup */ - curl_easy_cleanup(curl); - curl_global_cleanup(); - + if (cleanup) { + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + } + return responseInstance; } @@ -483,7 +485,7 @@ static Value get(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } - return newResultSuccess(vm, OBJ_VAL(endRequest(vm, curl, response))); + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, curl, response, true))); } /* always cleanup */ @@ -599,7 +601,7 @@ static Value post(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } - return newResultSuccess(vm, OBJ_VAL(endRequest(vm, curl, response))); + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, curl, response, true))); } /* always cleanup */ @@ -790,7 +792,7 @@ static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } - return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient, response))); + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient, response, false))); } pop(vm); From 21741509e08d1c746883310f7b6af2183bdc948c Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Mon, 31 Oct 2022 20:08:20 -0700 Subject: [PATCH 076/104] working example Signed-off-by: Brian Downs --- examples/http_client.du | 2 +- src/optionals/http/http.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/http_client.du b/examples/http_client.du index d44b9209..0ed9bc22 100644 --- a/examples/http_client.du +++ b/examples/http_client.du @@ -36,7 +36,7 @@ import System; print(res.unwrapError()); System.exit(1); } - print(res.unwrap().statusCode); + const uuid = res.unwrap().json().unwrap()["uuid"]; print("UUID: {}".format(uuid)); diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 991df8b3..1dd293c0 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -621,14 +621,14 @@ struct curl_slist *headerChunk = NULL; void freeHttpClient(DictuVM *vm, ObjAbstract *abstract) { HttpClient *httpClient = (HttpClient*)abstract->data; - + printf("here 1\n"); if (headerChunk) { curl_slist_free_all(headerChunk); } - + printf("here 2\n"); curl_easy_cleanup(httpClient); curl_global_cleanup(); - + printf("here 3\n"); FREE(vm, HttpClient, abstract->data); } From 87a5e146ecbb3ba20ed148149d947bfcd06c5460 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Tue, 1 Nov 2022 20:22:37 -0700 Subject: [PATCH 077/104] fix insecure and ssl code Signed-off-by: Brian Downs --- examples/http_client.du | 12 +++++++----- src/optionals/http/http.c | 32 ++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/examples/http_client.du b/examples/http_client.du index 0ed9bc22..0e55398e 100644 --- a/examples/http_client.du +++ b/examples/http_client.du @@ -11,7 +11,7 @@ import System; "Accept: application/json", "User-Agent: Dictu" ], - "insecure": true, + "insecure": false, "keyFile": "", "certFile": "", "keyPasswd": "" @@ -31,14 +31,16 @@ import System; print("Status Code: {}".format(res.unwrap().statusCode)); print(res.unwrap().json().unwrap()); - res = httpClient.get("https://httpbin.org/uuid"); + httpClient.setInsecure(true); + // res = httpClient.get("https://httpbin.org/uuid"); + res = httpClient.get("https://localhost:4433"); if (not res.success()) { print(res.unwrapError()); System.exit(1); } - - const uuid = res.unwrap().json().unwrap()["uuid"]; - print("UUID: {}".format(uuid)); + print(res.unwrap().content); + // const uuid = res.unwrap().json().unwrap()["uuid"]; + // print("UUID: {}".format(uuid)); System.exit(0); } diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 1dd293c0..2faf92bd 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -621,14 +621,14 @@ struct curl_slist *headerChunk = NULL; void freeHttpClient(DictuVM *vm, ObjAbstract *abstract) { HttpClient *httpClient = (HttpClient*)abstract->data; - printf("here 1\n"); + if (headerChunk) { curl_slist_free_all(headerChunk); } - printf("here 2\n"); + curl_easy_cleanup(httpClient); curl_global_cleanup(); - printf("here 3\n"); + FREE(vm, HttpClient, abstract->data); } @@ -671,9 +671,15 @@ static Value httpClientSetInsecure(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); + if (AS_BOOL(args[1])) { + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); + } else { + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 1); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 1); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 1); + } return NIL_VAL; } @@ -717,7 +723,7 @@ static Value httpClientSetKeyFile(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])->chars); return NIL_VAL; } @@ -735,7 +741,7 @@ static Value httpClientSetCertFile(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])->chars); return NIL_VAL; } @@ -753,7 +759,7 @@ static Value httpClientSetKeyPasswd(DictuVM *vm, int argCount, Value *args) { HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); - curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])); + curl_easy_setopt(httpClient, CURLOPT_SSLKEY, AS_STRING(args[1])->chars); return NIL_VAL; } @@ -860,9 +866,11 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { return abstract; } - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); + if (!AS_BOOL(entry->value)) { + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYSTATUS, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(httpClient, CURLOPT_SSL_VERIFYPEER, 0); + } } else if (strstr(key, "keyFile")) { if (!IS_STRING(entry->value)) { runtimeError(vm, "HTTP client option \"keyFile\" value must be a string"); From ddabbe4d3c123d4c05507ae7e88be4b410d0269f Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Tue, 1 Nov 2022 21:23:14 -0700 Subject: [PATCH 078/104] implement post on http client Signed-off-by: Brian Downs --- examples/http_client.du | 8 ++++ src/optionals/http/http.c | 77 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/examples/http_client.du b/examples/http_client.du index 0e55398e..8833d847 100644 --- a/examples/http_client.du +++ b/examples/http_client.du @@ -42,6 +42,14 @@ import System; // const uuid = res.unwrap().json().unwrap()["uuid"]; // print("UUID: {}".format(uuid)); + res = httpClient.post("https://httpbin.org/post", {}); + if (not res.success()) { + print(res.unwrapError()); + System.exit(1); + } + print("Status Code: {}".format(res.unwrap().statusCode)); + print(res.unwrap().json().unwrap()); + System.exit(0); } diff --git a/src/optionals/http/http.c b/src/optionals/http/http.c index 2faf92bd..c75c0f3c 100644 --- a/src/optionals/http/http.c +++ b/src/optionals/http/http.c @@ -785,6 +785,7 @@ static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { char *url = AS_CSTRING(args[1]); curl_easy_setopt(httpClient, CURLOPT_URL, url); + curl_easy_setopt(httpClient, CURLOPT_ACCEPT_ENCODING, "gzip"); curl_easy_setopt(httpClient, CURLOPT_WRITEFUNCTION, writeResponse); curl_easy_setopt(httpClient, CURLOPT_WRITEDATA, &response); curl_easy_setopt(httpClient, CURLOPT_HEADERDATA, &response); @@ -807,6 +808,76 @@ static Value httpClientGet(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } +static Value httpClientPost(DictuVM *vm, int argCount, Value *args) { + if (argCount < 1 || argCount > 2) { + runtimeError(vm, "post() takes at least 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + ObjDict *postValuesDict = NULL; + ObjString *postValueString = NULL; + + if (argCount == 3) { + if (IS_DICT(args[2])) { + postValuesDict = AS_DICT(args[2]); + } else if (IS_STRING(args[2])) { + postValueString = AS_STRING(args[2]); + } else { + runtimeError(vm, "Post values passed to post() must be a dictionary or a string."); + return EMPTY_VAL; + } + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "URL passed to post() must be a string."); + return EMPTY_VAL; + } + + CURLcode curlResponse; + + HttpClient *httpClient = AS_HTTP_CLIENT(args[0]); + + if (httpClient) { + Response response; + createResponse(vm, &response); + char *url = AS_CSTRING(args[1]); + char *postValue = ""; + + if (postValuesDict != NULL) { + postValue = dictToPostArgs(postValuesDict); + } else if (postValueString != NULL) { + postValue = postValueString->chars; + } + + curl_easy_setopt(httpClient, CURLOPT_URL, url); + curl_easy_setopt(httpClient, CURLOPT_ACCEPT_ENCODING, "gzip"); + curl_easy_setopt(httpClient, CURLOPT_POSTFIELDS, postValue); + curl_easy_setopt(httpClient, CURLOPT_WRITEFUNCTION, writeResponse); + curl_easy_setopt(httpClient, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(httpClient, CURLOPT_HEADERDATA, &response); + + curlResponse = curl_easy_perform(httpClient); + + if (postValuesDict != NULL) { + free(postValue); + } + + if (curlResponse != CURLE_OK) { + pop(vm); + + char *errorString = (char *)curl_easy_strerror(curlResponse); + return newResultError(vm, errorString); + } + + return newResultSuccess(vm, OBJ_VAL(endRequest(vm, httpClient, response, false))); + } + + pop(vm); + + char *errorString = (char *) curl_easy_strerror(CURLE_FAILED_INIT); + return newResultError(vm, errorString); +} + ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { ObjAbstract *abstract = newAbstract(vm, freeHttpClient, httpClientToString); push(vm, OBJ_VAL(abstract)); @@ -917,11 +988,7 @@ ObjAbstract *newHttpClient(DictuVM *vm, ObjDict *opts) { * Setup HTTP object methods */ defineNative(vm, &abstract->values, "get", httpClientGet); - defineNative(vm, &abstract->values, "post", httpClientGet); - defineNative(vm, &abstract->values, "delete", httpClientGet); - defineNative(vm, &abstract->values, "put", httpClientGet); - defineNative(vm, &abstract->values, "patch", httpClientGet); - defineNative(vm, &abstract->values, "head", httpClientGet); + defineNative(vm, &abstract->values, "post", httpClientPost); defineNative(vm, &abstract->values, "setTimeout", httpClientSetTimeout); defineNative(vm, &abstract->values, "addHeader", httpClientSetHeaders); defineNative(vm, &abstract->values, "setInsecure", httpClientSetInsecure); From a88a7f7b015a4ec01aa302aa2519caf3e17375c2 Mon Sep 17 00:00:00 2001 From: Brian Downs Date: Tue, 1 Nov 2022 21:51:34 -0700 Subject: [PATCH 079/104] first pass at docs update Signed-off-by: Brian Downs --- docs/docs/standard-lib/http.md | 67 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/docs/docs/standard-lib/http.md b/docs/docs/standard-lib/http.md index 5f9a273d..407d0853 100644 --- a/docs/docs/standard-lib/http.md +++ b/docs/docs/standard-lib/http.md @@ -39,7 +39,7 @@ HTTP.get("https://httpbin.org/get", ["Content-Type: application/json"], 1); ### HTTP.post(string, dictionary: postArgs -> optional, list: headers -> optional, number: timeout -> optional) -Sends a HTTP POST request to a given URL.Timeout is given in seconds. +Sends a HTTP POST request to a given URL. Timeout is given in seconds. Returns a Result and unwraps to a Response upon success. ```cs @@ -49,9 +49,56 @@ HTTP.post("https://httpbin.org/post", {"test": 10}, ["Content-Type: application/ HTTP.post("https://httpbin.org/post", {"test": 10}, ["Content-Type: application/json"], 1); ``` +### HTTP.newClient(dict) + +Creates a new HTTP client with a given set of options. +Returns a Result and neecds to be unwraped upon success. + +```cs +const opts = { + "timeout": 20, + "headers": [ + "Content-Type: application/json", + "Accept: application/json", + "User-Agent: Dictu" + ], + "insecure": false, + "keyFile": "", + "certFile": "", + "keyPasswd": "" +}; +const ret = HTTP.newClient(opts); +if (not ret.success()) { + print(res.unwrapError()); + System.exit(1); +} +var httpClient = ret.unwrap(); +``` + +### httpClient.get(string) + +Sends a HTTP GET request to a given URL. +Returns a Result and unwraps to a Response upon success. + +```cs +httpClient.get("https://httpbin.org/get"); + +{"content": "...", "headers": ["...", "..."], "statusCode": 200} +``` + +### HTTP.post(string, dictionary: postArgs) + +Sends a HTTP POST request to a given URL. +Returns a Result and unwraps to a Response upon success. + +```cs +httpClient.post("https://httpbin.org/post"); +httpClient.post("https://httpbin.org/post", {"test": 10}); +``` + ### Response -Both HTTP.get() and HTTP.post() return a Result that unwraps a Response object on success, or nil on error. +Both HTTP.get(), HTTP.post(), httpClient.get(), and httpClient.post() return a Result that unwraps a Response object on success, or nil on error. The Response object returned has 3 public properties, "content", "headers" and "statusCode". "content" is the actual content returned from the HTTP request as a string, "headers" is a list of all the response headers and "statusCode" is a number denoting the status code from the response @@ -59,17 +106,17 @@ the response #### Quick Reference Table ##### Properties -| Property | Description | -|------------|--------------------------------------------------------| -| content | Raw string content returned from the HTTP request | -| headers | A list of headers returned from the HTTP request | -| statusCode | The status code returned from the HTTP request | +| Property | Description | +| ---------- | ------------------------------------------------- | +| content | Raw string content returned from the HTTP request | +| headers | A list of headers returned from the HTTP request | +| statusCode | The status code returned from the HTTP request | ##### Methods -| Method | Description | -|------------|--------------------------------------------------------| -| json | Convert the content property to JSON | +| Method | Description | +| ------ | ------------------------------------ | +| json | Convert the content property to JSON | Example response from [httpbin.org](https://httpbin.org) From f4be54a1f7383f17746b9b934322c4693b336a49 Mon Sep 17 00:00:00 2001 From: Manohar Kakumani <42714264+manoharkakumani@users.noreply.github.com> Date: Thu, 3 Nov 2022 00:13:27 +0530 Subject: [PATCH 080/104] Update object.c Fixed elf referencing datatypes #354 --- src/vm/object.c | 739 ++++++++++++++++++++++++++++-------------------- 1 file changed, 437 insertions(+), 302 deletions(-) diff --git a/src/vm/object.c b/src/vm/object.c index d3d97c19..cde213ad 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -9,11 +9,12 @@ #include "vm.h" #define ALLOCATE_OBJ(vm, type, objectType) \ - (type*)allocateObject(vm, sizeof(type), objectType) + (type *)allocateObject(vm, sizeof(type), objectType) -static Obj *allocateObject(DictuVM *vm, size_t size, ObjType type) { +static Obj *allocateObject(DictuVM *vm, size_t size, ObjType type) +{ Obj *object; - object = (Obj *) reallocate(vm, NULL, 0, size); + object = (Obj *)reallocate(vm, NULL, 0, size); object->type = type; object->isDark = false; object->next = vm->objects; @@ -26,9 +27,11 @@ static Obj *allocateObject(DictuVM *vm, size_t size, ObjType type) { return object; } -ObjModule *newModule(DictuVM *vm, ObjString *name) { +ObjModule *newModule(DictuVM *vm, ObjString *name) +{ Value moduleVal; - if (tableGet(&vm->modules, name, &moduleVal)) { + if (tableGet(&vm->modules, name, &moduleVal)) + { return AS_MODULE(moduleVal); } @@ -50,7 +53,8 @@ ObjModule *newModule(DictuVM *vm, ObjString *name) { return module; } -ObjBoundMethod *newBoundMethod(DictuVM *vm, Value receiver, ObjClosure *method) { +ObjBoundMethod *newBoundMethod(DictuVM *vm, Value receiver, ObjClosure *method) +{ ObjBoundMethod *bound = ALLOCATE_OBJ(vm, ObjBoundMethod, OBJ_BOUND_METHOD); @@ -59,7 +63,8 @@ ObjBoundMethod *newBoundMethod(DictuVM *vm, Value receiver, ObjClosure *method) return bound; } -ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType type) { +ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType type) +{ ObjClass *klass = ALLOCATE_OBJ(vm, ObjClass, OBJ_CLASS); klass->name = name; klass->superclass = superclass; @@ -81,16 +86,19 @@ ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType return klass; } -ObjEnum *newEnum(DictuVM *vm, ObjString *name) { - ObjEnum *enumObj = ALLOCATE_OBJ(vm, ObjEnum , OBJ_ENUM); +ObjEnum *newEnum(DictuVM *vm, ObjString *name) +{ + ObjEnum *enumObj = ALLOCATE_OBJ(vm, ObjEnum, OBJ_ENUM); enumObj->name = name; initTable(&enumObj->values); return enumObj; } -ObjClosure *newClosure(DictuVM *vm, ObjFunction *function) { - ObjUpvalue **upvalues = ALLOCATE(vm, ObjUpvalue*, function->upvalueCount); - for (int i = 0; i < function->upvalueCount; i++) { +ObjClosure *newClosure(DictuVM *vm, ObjFunction *function) +{ + ObjUpvalue **upvalues = ALLOCATE(vm, ObjUpvalue *, function->upvalueCount); + for (int i = 0; i < function->upvalueCount; i++) + { upvalues[i] = NULL; } @@ -101,7 +109,8 @@ ObjClosure *newClosure(DictuVM *vm, ObjFunction *function) { return closure; } -ObjFunction *newFunction(DictuVM *vm, ObjModule *module, FunctionType type, AccessLevel level) { +ObjFunction *newFunction(DictuVM *vm, ObjModule *module, FunctionType type, AccessLevel level) +{ ObjFunction *function = ALLOCATE_OBJ(vm, ObjFunction, OBJ_FUNCTION); function->arity = 0; function->arityOptional = 0; @@ -122,7 +131,8 @@ ObjFunction *newFunction(DictuVM *vm, ObjModule *module, FunctionType type, Acce return function; } -ObjInstance *newInstance(DictuVM *vm, ObjClass *klass) { +ObjInstance *newInstance(DictuVM *vm, ObjClass *klass) +{ ObjInstance *instance = ALLOCATE_OBJ(vm, ObjInstance, OBJ_INSTANCE); instance->klass = klass; initTable(&instance->publicFields); @@ -138,14 +148,16 @@ ObjInstance *newInstance(DictuVM *vm, ObjClass *klass) { return instance; } -ObjNative *newNative(DictuVM *vm, NativeFn function) { +ObjNative *newNative(DictuVM *vm, NativeFn function) +{ ObjNative *native = ALLOCATE_OBJ(vm, ObjNative, OBJ_NATIVE); native->function = function; return native; } static ObjString *allocateString(DictuVM *vm, char *chars, int length, - uint32_t hash) { + uint32_t hash) +{ ObjString *string = ALLOCATE_OBJ(vm, ObjString, OBJ_STRING); string->length = length; string->chars = chars; @@ -156,13 +168,15 @@ static ObjString *allocateString(DictuVM *vm, char *chars, int length, return string; } -ObjList *newList(DictuVM *vm) { +ObjList *newList(DictuVM *vm) +{ ObjList *list = ALLOCATE_OBJ(vm, ObjList, OBJ_LIST); initValueArray(&list->values); return list; } -ObjDict *newDict(DictuVM *vm) { +ObjDict *newDict(DictuVM *vm) +{ ObjDict *dict = ALLOCATE_OBJ(vm, ObjDict, OBJ_DICT); dict->count = 0; dict->capacityMask = -1; @@ -170,7 +184,8 @@ ObjDict *newDict(DictuVM *vm) { return dict; } -ObjSet *newSet(DictuVM *vm) { +ObjSet *newSet(DictuVM *vm) +{ ObjSet *set = ALLOCATE_OBJ(vm, ObjSet, OBJ_SET); set->count = 0; set->capacityMask = -1; @@ -178,11 +193,13 @@ ObjSet *newSet(DictuVM *vm) { return set; } -ObjFile *newFile(DictuVM *vm) { +ObjFile *newFile(DictuVM *vm) +{ return ALLOCATE_OBJ(vm, ObjFile, OBJ_FILE); } -ObjAbstract *newAbstract(DictuVM *vm, AbstractFreeFn func, AbstractTypeFn type) { +ObjAbstract *newAbstract(DictuVM *vm, AbstractFreeFn func, AbstractTypeFn type) +{ ObjAbstract *abstract = ALLOCATE_OBJ(vm, ObjAbstract, OBJ_ABSTRACT); abstract->data = NULL; abstract->func = func; @@ -192,7 +209,8 @@ ObjAbstract *newAbstract(DictuVM *vm, AbstractFreeFn func, AbstractTypeFn type) return abstract; } -ObjResult *newResult(DictuVM *vm, ResultStatus status, Value value) { +ObjResult *newResult(DictuVM *vm, ResultStatus status, Value value) +{ ObjResult *result = ALLOCATE_OBJ(vm, ObjResult, OBJ_RESULT); result->status = status; result->value = value; @@ -200,14 +218,16 @@ ObjResult *newResult(DictuVM *vm, ResultStatus status, Value value) { return result; } -Value newResultSuccess(DictuVM *vm, Value value) { +Value newResultSuccess(DictuVM *vm, Value value) +{ push(vm, value); ObjResult *result = newResult(vm, SUCCESS, value); pop(vm); return OBJ_VAL(result); } -Value newResultError(DictuVM *vm, char *errorMsg) { +Value newResultError(DictuVM *vm, char *errorMsg) +{ Value error = OBJ_VAL(copyString(vm, errorMsg, strlen(errorMsg))); push(vm, error); ObjResult *result = newResult(vm, ERR, error); @@ -215,10 +235,12 @@ Value newResultError(DictuVM *vm, char *errorMsg) { return OBJ_VAL(result); } -static uint32_t hashString(const char *key, int length) { +static uint32_t hashString(const char *key, int length) +{ uint32_t hash = 2166136261u; - for (int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) + { hash ^= key[i]; hash *= 16777619; } @@ -226,11 +248,13 @@ static uint32_t hashString(const char *key, int length) { return hash; } -ObjString *takeString(DictuVM *vm, char *chars, int length) { +ObjString *takeString(DictuVM *vm, char *chars, int length) +{ uint32_t hash = hashString(chars, length); ObjString *interned = tableFindString(&vm->strings, chars, length, hash); - if (interned != NULL) { + if (interned != NULL) + { FREE_ARRAY(vm, char, chars, length + 1); return interned; } @@ -240,11 +264,13 @@ ObjString *takeString(DictuVM *vm, char *chars, int length) { return allocateString(vm, chars, length, hash); } -ObjString *copyString(DictuVM *vm, const char *chars, int length) { +ObjString *copyString(DictuVM *vm, const char *chars, int length) +{ uint32_t hash = hashString(chars, length); ObjString *interned = tableFindString(&vm->strings, chars, length, hash); - if (interned != NULL) return interned; + if (interned != NULL) + return interned; char *heapChars = ALLOCATE(vm, char, length + 1); memcpy(heapChars, chars, length); @@ -252,7 +278,8 @@ ObjString *copyString(DictuVM *vm, const char *chars, int length) { return allocateString(vm, heapChars, length, hash); } -ObjUpvalue *newUpvalue(DictuVM *vm, Value *slot) { +ObjUpvalue *newUpvalue(DictuVM *vm, Value *slot) +{ ObjUpvalue *upvalue = ALLOCATE_OBJ(vm, ObjUpvalue, OBJ_UPVALUE); upvalue->closed = NIL_VAL; upvalue->value = slot; @@ -261,38 +288,52 @@ ObjUpvalue *newUpvalue(DictuVM *vm, Value *slot) { return upvalue; } -char *listToString(Value value) { +char *listToString(Value value) +{ int size = 50; ObjList *list = AS_LIST(value); char *listString = malloc(sizeof(char) * size); memcpy(listString, "[", 1); int listStringLength = 1; - for (int i = 0; i < list->values.count; ++i) { + for (int i = 0; i < list->values.count; ++i) + { Value listValue = list->values.values[i]; char *element; int elementSize; - - if (IS_STRING(listValue)) { + if (listValue == value) + { + element = "[...]"; + elementSize = 5; + } + else if (IS_STRING(listValue)) + { ObjString *s = AS_STRING(listValue); element = s->chars; elementSize = s->length; - } else { + } + else + { element = valueToString(listValue); elementSize = strlen(element); } - if (elementSize > (size - listStringLength - 6)) { - if (elementSize > size) { + if (elementSize > (size - listStringLength - 6)) + { + if (elementSize > size) + { size = size + elementSize * 2 + 6; - } else { + } + else + { size = size * 2 + 6; } char *newB = realloc(listString, sizeof(char) * size); - if (newB == NULL) { + if (newB == NULL) + { printf("Unable to allocate memory\n"); exit(71); } @@ -300,18 +341,27 @@ char *listToString(Value value) { listString = newB; } - if (IS_STRING(listValue)) { + if (listValue == value) + { + memcpy(listString + listStringLength, element, elementSize); + listStringLength += elementSize; + } + else if (IS_STRING(listValue)) + { memcpy(listString + listStringLength, "\"", 1); memcpy(listString + listStringLength + 1, element, elementSize); memcpy(listString + listStringLength + 1 + elementSize, "\"", 1); listStringLength += elementSize + 2; - } else { + } + else + { memcpy(listString + listStringLength, element, elementSize); listStringLength += elementSize; free(element); } - if (i != list->values.count - 1) { + if (i != list->values.count - 1) + { memcpy(listString + listStringLength, ", ", 2); listStringLength += 2; } @@ -323,116 +373,152 @@ char *listToString(Value value) { return listString; } -char *dictToString(Value value) { - int count = 0; - int size = 50; - ObjDict *dict = AS_DICT(value); - char *dictString = malloc(sizeof(char) * size); - memcpy(dictString, "{", 1); - int dictStringLength = 1; - - for (int i = 0; i <= dict->capacityMask; ++i) { - DictItem *item = &dict->entries[i]; - if (IS_EMPTY(item->key)) { - continue; - } - - count++; - - char *key; - int keySize; - - if (IS_STRING(item->key)) { - ObjString *s = AS_STRING(item->key); - key = s->chars; - keySize = s->length; - } else { - key = valueToString(item->key); - keySize = strlen(key); - } - - if (keySize > (size - dictStringLength - keySize - 4)) { - if (keySize > size) { - size += keySize * 2 + 4; - } else { - size *= 2 + 4; - } - - char *newB = realloc(dictString, sizeof(char) * size); - - if (newB == NULL) { - printf("Unable to allocate memory\n"); - exit(71); - } - - dictString = newB; - } - - if (IS_STRING(item->key)) { - memcpy(dictString + dictStringLength, "\"", 1); - memcpy(dictString + dictStringLength + 1, key, keySize); - memcpy(dictString + dictStringLength + 1 + keySize, "\": ", 3); - dictStringLength += 4 + keySize; - } else { - memcpy(dictString + dictStringLength, key, keySize); - memcpy(dictString + dictStringLength + keySize, ": ", 2); - dictStringLength += 2 + keySize; - free(key); - } - - char *element; - int elementSize; - - if (IS_STRING(item->value)) { - ObjString *s = AS_STRING(item->value); - element = s->chars; - elementSize = s->length; - } else { - element = valueToString(item->value); - elementSize = strlen(element); - } - - if (elementSize > (size - dictStringLength - elementSize - 6)) { - if (elementSize > size) { - size += elementSize * 2 + 6; - } else { - size = size * 2 + 6; - } - - char *newB = realloc(dictString, sizeof(char) * size); - - if (newB == NULL) { - printf("Unable to allocate memory\n"); - exit(71); - } - - dictString = newB; - } - - if (IS_STRING(item->value)) { - memcpy(dictString + dictStringLength, "\"", 1); - memcpy(dictString + dictStringLength + 1, element, elementSize); - memcpy(dictString + dictStringLength + 1 + elementSize, "\"", 1); - dictStringLength += 2 + elementSize; - } else { - memcpy(dictString + dictStringLength, element, elementSize); - dictStringLength += elementSize; - free(element); - } - - if (count != dict->count) { - memcpy(dictString + dictStringLength, ", ", 2); - dictStringLength += 2; - } - } - - memcpy(dictString + dictStringLength, "}", 1); - dictString[dictStringLength + 1] = '\0'; - - return dictString; +char *dictToString(Value value) +{ + int count = 0; + int size = 50; + ObjDict *dict = AS_DICT(value); + char *dictString = malloc(sizeof(char) * size); + memcpy(dictString, "{", 1); + int dictStringLength = 1; + + for (int i = 0; i <= dict->capacityMask; ++i) + { + DictItem *item = &dict->entries[i]; + if (IS_EMPTY(item->key)) + { + continue; + } + + count++; + + char *key; + int keySize; + + if (IS_STRING(item->key)) + { + ObjString *s = AS_STRING(item->key); + key = s->chars; + keySize = s->length; + } + else + { + key = valueToString(item->key); + keySize = strlen(key); + } + + if (keySize > (size - dictStringLength - keySize - 4)) + { + if (keySize > size) + { + size += keySize * 2 + 4; + } + else + { + size *= 2 + 4; + } + + char *newB = realloc(dictString, sizeof(char) * size); + + if (newB == NULL) + { + printf("Unable to allocate memory\n"); + exit(71); + } + + dictString = newB; + } + + if (IS_STRING(item->key)) + { + memcpy(dictString + dictStringLength, "\"", 1); + memcpy(dictString + dictStringLength + 1, key, keySize); + memcpy(dictString + dictStringLength + 1 + keySize, "\": ", 3); + dictStringLength += 4 + keySize; + } + else + { + memcpy(dictString + dictStringLength, key, keySize); + memcpy(dictString + dictStringLength + keySize, ": ", 2); + dictStringLength += 2 + keySize; + free(key); + } + + char *element; + int elementSize; + + if (item->value == value) + { + element = "{...}"; + elementSize = 5; + } + else if (IS_STRING(item->value)) + { + ObjString *s = AS_STRING(item->value); + element = s->chars; + elementSize = s->length; + } + else + { + element = valueToString(item->value); + elementSize = strlen(element); + } + + if (elementSize > (size - dictStringLength - elementSize - 6)) + { + if (elementSize > size) + { + size += elementSize * 2 + 6; + } + else + { + size = size * 2 + 6; + } + + char *newB = realloc(dictString, sizeof(char) * size); + + if (newB == NULL) + { + printf("Unable to allocate memory\n"); + exit(71); + } + + dictString = newB; + } + if (item->value == value) + { + memcpy(dictString + dictStringLength, element, elementSize); + dictStringLength += elementSize; + } + else if (IS_STRING(item->value)) + { + memcpy(dictString + dictStringLength, "\"", 1); + memcpy(dictString + dictStringLength + 1, element, elementSize); + memcpy(dictString + dictStringLength + 1 + elementSize, "\"", 1); + dictStringLength += 2 + elementSize; + } + else + { + memcpy(dictString + dictStringLength, element, elementSize); + dictStringLength += elementSize; + free(element); + } + + if (count != dict->count) + { + memcpy(dictString + dictStringLength, ", ", 2); + dictStringLength += 2; + } + } + + memcpy(dictString + dictStringLength, "}", 1); + dictString[dictStringLength + 1] = '\0'; + + return dictString; } -char *setToString(Value value) { +char *setToString(Value value) +{ int count = 0; int size = 50; ObjSet *set = AS_SET(value); @@ -440,7 +526,8 @@ char *setToString(Value value) { memcpy(setString, "{", 1); int setStringLength = 1; - for (int i = 0; i <= set->capacityMask; ++i) { + for (int i = 0; i <= set->capacityMask; ++i) + { SetItem *item = &set->entries[i]; if (IS_EMPTY(item->value) || item->deleted) continue; @@ -450,25 +537,33 @@ char *setToString(Value value) { char *element; int elementSize; - if (IS_STRING(item->value)) { + if (IS_STRING(item->value)) + { ObjString *s = AS_STRING(item->value); element = s->chars; elementSize = s->length; - } else { + } + else + { element = valueToString(item->value); elementSize = strlen(element); } - if (elementSize > (size - setStringLength - 5)) { - if (elementSize > size * 2) { + if (elementSize > (size - setStringLength - 5)) + { + if (elementSize > size * 2) + { size += elementSize * 2 + 5; - } else { + } + else + { size = size * 2 + 5; } char *newB = realloc(setString, sizeof(char) * size); - if (newB == NULL) { + if (newB == NULL) + { printf("Unable to allocate memory\n"); exit(71); } @@ -476,19 +571,22 @@ char *setToString(Value value) { setString = newB; } - - if (IS_STRING(item->value)) { + if (IS_STRING(item->value)) + { memcpy(setString + setStringLength, "\"", 1); memcpy(setString + setStringLength + 1, element, elementSize); memcpy(setString + setStringLength + 1 + elementSize, "\"", 1); setStringLength += 2 + elementSize; - } else { + } + else + { memcpy(setString + setStringLength, element, elementSize); setStringLength += elementSize; free(element); } - if (count != set->count) { + if (count != set->count) + { memcpy(setString + setStringLength, ", ", 2); setStringLength += 2; } @@ -500,7 +598,8 @@ char *setToString(Value value) { return setString; } -char *classToString(Value value) { +char *classToString(Value value) +{ ObjClass *klass = AS_CLASS(value); char *classString = malloc(sizeof(char) * (klass->name->length + 7)); memcpy(classString, "klass->name->length + 12)); memcpy(instanceString, "<", 1); @@ -520,172 +620,207 @@ char *instanceToString(Value value) { return instanceString; } -char *objectToString(Value value) { - switch (OBJ_TYPE(value)) { - case OBJ_MODULE: { - ObjModule *module = AS_MODULE(value); - char *moduleString = malloc(sizeof(char) * (module->name->length + 11)); - snprintf(moduleString, (module->name->length + 10), "", module->name->chars); - return moduleString; - } - - case OBJ_CLASS: { - if (IS_TRAIT(value)) { - ObjClass *trait = AS_CLASS(value); - char *traitString = malloc(sizeof(char) * (trait->name->length + 10)); - snprintf(traitString, trait->name->length + 9, "", trait->name->chars); - return traitString; - } - - return classToString(value); - } - - case OBJ_ENUM: { - ObjEnum *enumObj = AS_ENUM(value); - char *enumString = malloc(sizeof(char) * (enumObj->name->length + 8)); - memcpy(enumString, "name->chars, enumObj->name->length); - memcpy(enumString + 6 + enumObj->name->length, ">", 1); - - enumString[7 + enumObj->name->length] = '\0'; - - return enumString; - } - - case OBJ_BOUND_METHOD: { - ObjBoundMethod *method = AS_BOUND_METHOD(value); - char *methodString; - - if (method->method->function->name != NULL) { - switch (method->method->function->type) { - case TYPE_STATIC: { - methodString = malloc(sizeof(char) * (method->method->function->name->length + 17)); - snprintf(methodString, method->method->function->name->length + 17, "", method->method->function->name->chars); - break; - } - - default: { - methodString = malloc(sizeof(char) * (method->method->function->name->length + 16)); - snprintf(methodString, method->method->function->name->length + 16, "", method->method->function->name->chars); - break; - } - } - } else { - switch (method->method->function->type) { - case TYPE_STATIC: { - methodString = malloc(sizeof(char) * 16); - memcpy(methodString, "", 15); - methodString[15] = '\0'; - break; - } - - default: { - methodString = malloc(sizeof(char) * 15); - memcpy(methodString, "", 14); - methodString[14] = '\0'; - break; - } - } - } +char *objectToString(Value value) +{ + switch (OBJ_TYPE(value)) + { + case OBJ_MODULE: + { + ObjModule *module = AS_MODULE(value); + char *moduleString = malloc(sizeof(char) * (module->name->length + 11)); + snprintf(moduleString, (module->name->length + 10), "", module->name->chars); + return moduleString; + } - return methodString; + case OBJ_CLASS: + { + if (IS_TRAIT(value)) + { + ObjClass *trait = AS_CLASS(value); + char *traitString = malloc(sizeof(char) * (trait->name->length + 10)); + snprintf(traitString, trait->name->length + 9, "", trait->name->chars); + return traitString; } - case OBJ_CLOSURE: { - ObjClosure *closure = AS_CLOSURE(value); - char *closureString; + return classToString(value); + } - if (closure->function->name != NULL) { - closureString = malloc(sizeof(char) * (closure->function->name->length + 6)); - snprintf(closureString, closure->function->name->length + 6, "", closure->function->name->chars); - } else { - closureString = malloc(sizeof(char) * 9); - memcpy(closureString, "