From f16ead978008e488876b5a4ab8b92db090035a33 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 22:21:08 -0400 Subject: [PATCH 01/32] Add path walker example Once we figure out how to use Dictu code in the standard library the `Walker` class in this example can be added to it as `Path.Walker`. --- examples/pathWalker.du | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/pathWalker.du diff --git a/examples/pathWalker.du b/examples/pathWalker.du new file mode 100644 index 00000000..a4f5eca9 --- /dev/null +++ b/examples/pathWalker.du @@ -0,0 +1,51 @@ +import Path; + +class Walker { + private prev; // The previous dirpath, to be joined to the values in toAdd + private toAdd; // Directories to be added to the stack, unless pruned by the user + private stack; // Directories to be traversed later on + + init(root='.') { + this.prev = nil; + this.stack = [root]; + this.toAdd = []; + } + + next() { + this.stack.extend(this.toAdd.map(def (x) => Path.join(this.prev, x))); + + if (!this.stack.len()) return nil; + + var current = this.stack.pop(); + var dirList = Path.listDir(current); + var files = []; + var dirs = []; + + for (var i = 0; i < dirList.len(); i += 1) { + var list = Path.isDir(Path.join(current, dirList[i])) ? dirs : files; + list.push(dirList[i]); + } + + this.toAdd = dirs; // Edits made to dirs by the user will affect what is added to the stack + + this.prev = current; + return [current, dirs, files]; + } + +} + +var walker = Walker(); + +while { + var tmp = walker.next(); + if (!tmp) break; + var [dirpath, dirnames, filenames] = tmp; + + print([dirpath, dirnames, filenames]); + print(); + + var prune = ['.git']; + for (var j = 0; j < prune.len(); j += 1) + if (dirnames.contains(prune[j])) + dirnames.remove(prune[j]); +} From 9c5df3caa28fa77fafabd265181ee50b65e3a48a Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 12:16:44 +0100 Subject: [PATCH 02/32] Add list.findIndex and extend list.find --- docs/docs/collections/lists.md | 20 ++++++++++++++++++-- src/vm/datatypes/lists/list-source.h | 12 ++++++++++-- src/vm/datatypes/lists/list.du | 12 ++++++++++-- tests/lists/find.du | 6 +++++- tests/lists/findIndex.du | 17 +++++++++++++++++ tests/lists/import.du | 1 + 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 tests/lists/findIndex.du diff --git a/docs/docs/collections/lists.md b/docs/docs/collections/lists.md index 612d0a97..945c5f7b 100644 --- a/docs/docs/collections/lists.md +++ b/docs/docs/collections/lists.md @@ -285,14 +285,30 @@ By default the initial value for `.reduce()` is 0, however we can change this to print(["Dictu ", "is", " great!"].reduce(def (accumulate, element) => accumulate + element, "")); // 'Dictu is great!' ``` -### list.find(func) +### list.find(func, number: start -> optional, number: end -> optional) To find a single item within a list we use `.find()`. Find will search through each item in the list and as soon as the callback returns a truthy value, the item that satisfied the callback is returned, if none of the items satisfy the callback -function then `nil` is returned. +function then `nil` is returned. The optional start and end parameters change the points at which the list will be searched. Note: The first item to satisfy the callback is returned. ```cs print([1, 2, 3].find(def (item) => item == 2)); // 2 +print([1, 2, 3, 4, 5, 6].find(def (item) => item % 2 == 0, 2)); // 4 +print([1, 2, 3, 4, 5, 6].find(def (item) => item % 2 == 0, 2, 3)); // nil +``` + +### list.findIndex(func, number: start -> optional, number: end -> optional) + +To find a single item within a list we use `.findIndex()`. Find will search through each item in the list and as soon as the +callback returns a truthy value, the index at which the item that satisfied the callback is returned, if none of the items satisfy the callback +function then `nil` is returned. The optional start and end parameters change the points at which the list will be searched. + +Note: The first item to satisfy the callback is returned. + +```cs +print([1, 2, 3].findIndex(def (item) => item == 2)); // 1 +print([1, 2, 3, 4, 5, 6].findIndex(def (item) => item % 2 == 0, 2)); // 3 +print([1, 2, 3, 4, 5, 6].findIndex(def (item) => item % 2 == 0, 2, 3)); // nil ``` \ No newline at end of file diff --git a/src/vm/datatypes/lists/list-source.h b/src/vm/datatypes/lists/list-source.h index ddbcb72c..8e1254f1 100644 --- a/src/vm/datatypes/lists/list-source.h +++ b/src/vm/datatypes/lists/list-source.h @@ -43,11 +43,19 @@ " }\n" \ "}\n" \ "\n" \ -"def find(list, func) {\n" \ -" for (var i = 0; i < list.len(); i += 1) {\n" \ +"def find(list, func, start=0, end=list.len()) {\n" \ +" for (var i = start; i < end; i += 1) {\n" \ " if (func(list[i])) {\n" \ " return list[i];\n" \ " }\n" \ " }\n" \ "}\n" \ +"\n" \ +"def findIndex(list, func, start=0, end=list.len()) {\n" \ +" for (var i = start; i < end; i += 1) {\n" \ +" if (func(list[i])) {\n" \ +" return i;\n" \ +" }\n" \ +" }\n" \ +"}\n" \ diff --git a/src/vm/datatypes/lists/list.du b/src/vm/datatypes/lists/list.du index c14ed152..88c9cabe 100644 --- a/src/vm/datatypes/lists/list.du +++ b/src/vm/datatypes/lists/list.du @@ -43,10 +43,18 @@ def forEach(list, func) { } } -def find(list, func) { - for (var i = 0; i < list.len(); i += 1) { +def find(list, func, start=0, end=list.len()) { + for (var i = start; i < end; i += 1) { if (func(list[i])) { return list[i]; } } +} + +def findIndex(list, func, start=0, end=list.len()) { + for (var i = start; i < end; i += 1) { + if (func(list[i])) { + return i; + } + } } \ No newline at end of file diff --git a/tests/lists/find.du b/tests/lists/find.du index 4d53ad77..5aedf86e 100644 --- a/tests/lists/find.du +++ b/tests/lists/find.du @@ -10,4 +10,8 @@ const myList = [1, 2, 3, 4, 5]; assert(myList.find(def (item) => item == 3) == 3); -assert(myList.find(def (item) => item == 10) == nil); \ No newline at end of file +assert(myList.find(def (item) => item == 10) == nil); +assert(myList.find(def (item) => item % 2 == 0) == 2); +assert(myList.find(def (item) => item % 2 == 0, 2) == 4); +assert(myList.find(def (item) => item % 2 == 0, 2, 3) == nil); +assert(myList.find(def (item) => item % 2 == 0, 5) == nil); \ No newline at end of file diff --git a/tests/lists/findIndex.du b/tests/lists/findIndex.du new file mode 100644 index 00000000..b7990834 --- /dev/null +++ b/tests/lists/findIndex.du @@ -0,0 +1,17 @@ +/** + * find.du + * + * Testing the list.findIndex() method + * + * .findIndex() runs a user defined function on each element in the list and returns the index of the item in the + * list that satisfies the callback + */ + +const myList = [1, 2, 3, 4, 5]; + +assert(myList.findIndex(def (item) => item == 3) == 2); +assert(myList.findIndex(def (item) => item == 10) == nil); +assert(myList.findIndex(def (item) => item % 2 == 0) == 1); +assert(myList.findIndex(def (item) => item % 2 == 0, 2) == 3); +assert(myList.findIndex(def (item) => item % 2 == 0, 2, 3) == nil); +assert(myList.findIndex(def (item) => item % 2 == 0, 5) == nil); diff --git a/tests/lists/import.du b/tests/lists/import.du index 441e87d2..46aec85a 100644 --- a/tests/lists/import.du +++ b/tests/lists/import.du @@ -24,4 +24,5 @@ import "filter.du"; import "reduce.du"; import "forEach.du"; import "find.du"; +import "findIndex.du"; import "reverse.du"; From bf74aca78af69a3d17f8b915cac4aaedb11fe724 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sun, 3 Oct 2021 11:33:52 -0400 Subject: [PATCH 03/32] Add easy way to prune tree with `Walker` --- examples/pathWalker.du | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/pathWalker.du b/examples/pathWalker.du index a4f5eca9..cc7e0678 100644 --- a/examples/pathWalker.du +++ b/examples/pathWalker.du @@ -5,14 +5,17 @@ class Walker { private toAdd; // Directories to be added to the stack, unless pruned by the user private stack; // Directories to be traversed later on - init(root='.') { + init(root='.', prune=[]) { this.prev = nil; this.stack = [root]; this.toAdd = []; + this.prune = prune; // Public attribute; can be updated in between calls to `next`. } next() { - this.stack.extend(this.toAdd.map(def (x) => Path.join(this.prev, x))); + var filtered = this.toAdd.filter(def (x) => !this.prune.contains(x)); + var joined = filtered.map(def (x) => Path.join(this.prev, x)); + this.stack.extend(joined); if (!this.stack.len()) return nil; @@ -34,7 +37,9 @@ class Walker { } -var walker = Walker(); +// Traverse the file system starting from the current directory. Prune every directory named 'docs' +// from the tree. +var walker = Walker('.', ['docs']); while { var tmp = walker.next(); @@ -44,6 +49,10 @@ while { print([dirpath, dirnames, filenames]); print(); + // This is another way to prune the traversal, and can also be used to expand it if you create + // new directories in between calls to `walker.next`. It's more flexible than providing a + // `prune` list when the class is instantiated, but less convenient when the flexibility is not + // needed. As demonstrated here, both of these methods of pruning the search can be combined. var prune = ['.git']; for (var j = 0; j < prune.len(); j += 1) if (dirnames.contains(prune[j])) From 5a483ec1e5698abd5a5d5477970d39f6ef7c6497 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sun, 3 Oct 2021 12:04:01 -0400 Subject: [PATCH 04/32] Add `Walker.exhaust` --- examples/pathWalker.du | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/examples/pathWalker.du b/examples/pathWalker.du index cc7e0678..40a3faf4 100644 --- a/examples/pathWalker.du +++ b/examples/pathWalker.du @@ -35,11 +35,25 @@ class Walker { return [current, dirs, files]; } + // Convenience method to perform a full traversal. Be careful with this, as it can infinitely + // loop if there are cycles in the file system (e.g. with symbolic links), and consume a large + // amount of time and memory. + exhaust() { + var results = []; + while { + var x = this.next(); + if (!x) break; + results.push(x); + } + return results; + } } -// Traverse the file system starting from the current directory. Prune every directory named 'docs' +var dictuDir = Path.join(Path.dirname(__file__), '..'); + +// Traverse the file system starting from the project directory. Prune every directory named 'docs' // from the tree. -var walker = Walker('.', ['docs']); +var walker = Walker(dictuDir, ['docs']); while { var tmp = walker.next(); @@ -58,3 +72,7 @@ while { if (dirnames.contains(prune[j])) dirnames.remove(prune[j]); } + + +print('Example using `Walker.exhaust`:'); +print(Walker(Path.join(dictuDir, '.github'), []).exhaust()); From 1ace7ff3a54da8d8ed48e9034385ce02cbcb294a Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sun, 3 Oct 2021 12:18:13 -0400 Subject: [PATCH 05/32] Add `Walker.exhaust(Dirs|Files|All)` --- examples/pathWalker.du | 50 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/examples/pathWalker.du b/examples/pathWalker.du index 40a3faf4..0b155f70 100644 --- a/examples/pathWalker.du +++ b/examples/pathWalker.du @@ -35,9 +35,10 @@ class Walker { return [current, dirs, files]; } - // Convenience method to perform a full traversal. Be careful with this, as it can infinitely - // loop if there are cycles in the file system (e.g. with symbolic links), and consume a large - // amount of time and memory. + // Convenience methods to perform full traversals. Be careful with these, as they can + // infinitely loop if there are cycles in the file system (e.g. with symbolic links), and + // consume a large amount of time and memory. + exhaust() { var results = []; while { @@ -47,6 +48,36 @@ class Walker { } return results; } + + exhaustDirs() { + var results = []; + while { + var x = this.next(); + if (!x) break; + results.push(x[0]); + } + return results; + } + + exhaustFiles() { + var results = []; + while { + var x = this.next(); + if (!x) break; + results.extend(x[2].map(def (file) => Path.join(x[0], file))); + } + return results; + } + + exhaustAll() { + var results = []; + while { + var x = this.next(); + if (!x) break; + [1, 2].forEach(def (i) => results.extend(x[i].map(def (y) => Path.join(x[0], y)))); + } + return results; + } } var dictuDir = Path.join(Path.dirname(__file__), '..'); @@ -74,5 +105,18 @@ while { } +print(); print('Example using `Walker.exhaust`:'); print(Walker(Path.join(dictuDir, '.github'), []).exhaust()); + +print(); +print('Example using `Walker.exhaustDirs`:'); +print(Walker(Path.join(dictuDir, '.github'), []).exhaustDirs()); + +print(); +print('Example using `Walker.exhaustFiles`:'); +print(Walker(Path.join(dictuDir, '.github'), []).exhaustFiles()); + +print(); +print('Example using `Walker.exhaustAll`:'); +print(Walker(Path.join(dictuDir, '.github'), []).exhaustAll()); From 5f3c5f92dd04862d8df2d2fc8fcb5d7356f57ce7 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 23:09:54 +0100 Subject: [PATCH 06/32] Allow enums to have a generated value --- docs/docs/enum.md | 21 +++++++++++++++------ src/vm/compiler.c | 11 +++++++++-- tests/enum/enum.du | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/docs/enum.md b/docs/docs/enum.md index 5aaad198..681df0b1 100644 --- a/docs/docs/enum.md +++ b/docs/docs/enum.md @@ -18,17 +18,26 @@ nav_order: 10 ## Enums Enums are a collection of constants which can be accessed via a name rather than -an index to document intent. Unlike other languages, enums in Dictu must be assigned -to a value when declaring the enum and no automatic value will be generated. +an index to document intent. Unlike other languages, enums in Dictu do not generate a value +based on the previous entry, instead if no value is assigned it will be given it's position +within the enum, 0-based, as a value. ```cs +enum MyEnum { + a, // 0 + b, // 1 + c // 2 +} + +print(MyEnum.a); // 0 + enum Test { - a = 0, - b = 1, - c = 2 + a = 10, // 10 + b, // 1 + c // 2 } -print(Test.a); // 0 +print(Test.a); // 10 ``` Enums in Dictu also do not care about the value being stored within the enum, so diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 3f21a925..2b24e3ea 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -1800,6 +1800,8 @@ static void enumDeclaration(Compiler *compiler) { consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' before enum body."); + int index = 0; + do { if (check(compiler, TOKEN_RIGHT_BRACE)) { error(compiler->parser, "Trailing comma in enum declaration"); @@ -1808,9 +1810,14 @@ static void enumDeclaration(Compiler *compiler) { consume(compiler, TOKEN_IDENTIFIER, "Expect enum value identifier."); uint8_t name = identifierConstant(compiler, &compiler->parser->previous); - consume(compiler, TOKEN_EQUAL, "Expect '=' after enum value identifier."); - expression(compiler); + if (match(compiler, TOKEN_EQUAL)) { + expression(compiler); + } else { + emitConstant(compiler, NUMBER_VAL(index)); + } + emitBytes(compiler, OP_SET_ENUM_VALUE, name); + index++; } while (match(compiler, TOKEN_COMMA)); consume(compiler, TOKEN_RIGHT_BRACE, "Expect '}' after enum body."); diff --git a/tests/enum/enum.du b/tests/enum/enum.du index 499c60c9..63d85650 100644 --- a/tests/enum/enum.du +++ b/tests/enum/enum.du @@ -4,6 +4,16 @@ * Testing enums */ +enum MyEnum { + a, + b, + c +} + +assert(MyEnum.a == 0); +assert(MyEnum.b == 1); +assert(MyEnum.c == 2); + enum Test { a = 1, b = 2, @@ -14,6 +24,18 @@ assert(Test.a == 1); assert(Test.b == 2); assert(Test.c == 3); +enum AnotherTest { + a, + b = "test", + c = 30, + d +} + +assert(AnotherTest.a == 0); +assert(AnotherTest.b == "test"); +assert(AnotherTest.c == 30); +assert(AnotherTest.d == 3); + const func = def () => 10; enum HeterogeneousEnum { From 000f6ba7d1f28379ddc41ffc584db6c9d1ecbb86 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 4 Oct 2021 19:20:42 +0100 Subject: [PATCH 07/32] Make it so the System module needs importing --- src/optionals/optionals.c | 1 + src/optionals/system.c | 9 +++++---- src/optionals/system.h | 2 +- src/vm/vm.c | 5 +++-- src/vm/vm.h | 2 ++ 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/optionals/optionals.c b/src/optionals/optionals.c index 1e28bb8e..9c455ea7 100644 --- a/src/optionals/optionals.c +++ b/src/optionals/optionals.c @@ -12,6 +12,7 @@ BuiltinModules modules[] = { {"Hashlib", &createHashlibModule}, {"Sqlite", &createSqliteModule}, {"Process", &createProcessModule}, + {"System", &createSystemModule}, #ifndef DISABLE_HTTP {"HTTP", &createHTTPModule}, #endif diff --git a/src/optionals/system.c b/src/optionals/system.c index 626faa8f..483c25f3 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -281,7 +281,7 @@ static Value exitNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; /* satisfy the tcc compiler */ } -void initArgv(DictuVM *vm, Table *table, int argc, char *argv[]) { +void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); @@ -346,7 +346,7 @@ void setVersion(DictuVM *vm, Table *table) { pop(vm); } -void createSystemModule(DictuVM *vm, int argc, char *argv[]) { +ObjModule *createSystemModule(DictuVM *vm) { ObjString *name = copyString(vm, "System", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -382,7 +382,7 @@ void createSystemModule(DictuVM *vm, int argc, char *argv[]) { */ if (!vm->repl) { // Set argv variable - initArgv(vm, &module->values, argc, argv); + initArgv(vm, &module->values, vm->argc, vm->argv); } initPlatform(vm, &module->values); @@ -409,7 +409,8 @@ void createSystemModule(DictuVM *vm, int argc, char *argv[]) { defineNativeProperty(vm, &module->values, "R_OK", NUMBER_VAL(R_OK)); #endif - tableSet(vm, &vm->globals, name, OBJ_VAL(module)); pop(vm); pop(vm); + + return module; } diff --git a/src/optionals/system.h b/src/optionals/system.h index 4a702a72..9eb183d7 100644 --- a/src/optionals/system.h +++ b/src/optionals/system.h @@ -24,6 +24,6 @@ #include "../vm/vm.h" #include "../vm/memory.h" -void createSystemModule(DictuVM *vm, int argc, char *argv[]); +ObjModule *createSystemModule(DictuVM *vm); #endif //dictu_system_h diff --git a/src/vm/vm.c b/src/vm/vm.c index 561ece68..69f9ec4b 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -62,7 +62,7 @@ void runtimeError(DictuVM *vm, const char *format, ...) { resetStack(vm); } -DictuVM *dictuInitVM(bool repl, int argc, char *argv[]) { +DictuVM *dictuInitVM(bool repl, int argc, char **argv) { DictuVM *vm = malloc(sizeof(*vm)); if (vm == NULL) { @@ -85,6 +85,8 @@ DictuVM *dictuInitVM(bool repl, int argc, char *argv[]) { vm->grayCapacity = 0; vm->grayStack = NULL; vm->lastModule = NULL; + vm->argc = argc; + vm->argv = argv; initTable(&vm->modules); initTable(&vm->globals); initTable(&vm->constants); @@ -125,7 +127,6 @@ DictuVM *dictuInitVM(bool repl, int argc, char *argv[]) { * Native classes which are not required to be * imported. For imported modules see optionals.c */ - createSystemModule(vm, argc, argv); createCModule(vm); if (vm->repl) { diff --git a/src/vm/vm.h b/src/vm/vm.h index 1f6e7cfb..82aa072d 100644 --- a/src/vm/vm.h +++ b/src/vm/vm.h @@ -48,6 +48,8 @@ struct _vm { int grayCount; int grayCapacity; Obj **grayStack; + int argc; + char **argv; }; #define OK 0 From fb2904d038f59ffd4bb4e61c307ab39747d44100 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 4 Oct 2021 19:24:55 +0100 Subject: [PATCH 08/32] Update tests, docs and examples to include the System import --- docs/docs/standard-lib/system.md | 5 +++++ examples/factorial.du | 2 ++ examples/guessingGame.du | 1 + tests/builtins/__file__.du | 1 + tests/builtins/type.du | 1 + tests/datetime/strptime.du | 1 + tests/path/isDir.du | 1 + tests/path/realpath.du | 1 + tests/process/exec.du | 1 + tests/process/run.du | 1 + tests/system/access.du | 1 + tests/system/clock.du | 1 + tests/system/constants.du | 1 + tests/system/getCWD.du | 1 + tests/system/mkdir.du | 1 + tests/system/process.du | 1 + tests/system/remove.du | 1 + tests/system/setCWD.du | 1 + tests/system/sleep.du | 1 + tests/system/time.du | 1 + tests/system/version.du | 1 + 21 files changed, 26 insertions(+) diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index 866d7830..8587d279 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -17,6 +17,11 @@ parent: Standard Library --- ## System +To make use of the System module an import is required. + +```js +import System; +``` ### Constants diff --git a/examples/factorial.du b/examples/factorial.du index a3ab8052..619a50c7 100644 --- a/examples/factorial.du +++ b/examples/factorial.du @@ -1,3 +1,5 @@ +import System; + var amount; /** diff --git a/examples/guessingGame.du b/examples/guessingGame.du index 59a775e9..28c810ec 100644 --- a/examples/guessingGame.du +++ b/examples/guessingGame.du @@ -1,4 +1,5 @@ import Random; +import System; const guess = 10; var maxGuesses = 5; diff --git a/tests/builtins/__file__.du b/tests/builtins/__file__.du index f077dcb1..76700356 100644 --- a/tests/builtins/__file__.du +++ b/tests/builtins/__file__.du @@ -4,6 +4,7 @@ * Testing __file__ */ import Path; +import System; assert(Path.basename(__file__) == "__file__.du"); if (System.platform == "windows") { diff --git a/tests/builtins/type.du b/tests/builtins/type.du index c2d9f5a3..c438c744 100644 --- a/tests/builtins/type.du +++ b/tests/builtins/type.du @@ -5,6 +5,7 @@ * * type() returns the type of a value as a string */ +import System; assert(type(nil) == "nil"); assert(type(true) == "bool"); diff --git a/tests/datetime/strptime.du b/tests/datetime/strptime.du index ee35e7fa..1e1c93f6 100644 --- a/tests/datetime/strptime.du +++ b/tests/datetime/strptime.du @@ -5,6 +5,7 @@ * */ import Datetime; +import System; if (System.platform != "windows") { // 1577836800 is 1/1/2020 at 12:00 am diff --git a/tests/path/isDir.du b/tests/path/isDir.du index f67ada09..4366443a 100644 --- a/tests/path/isDir.du +++ b/tests/path/isDir.du @@ -6,6 +6,7 @@ * Returns true if the given string is a path to a directory, else false. (Linux only) */ import Path; +import System; if (System.platform != "windows") { assert(Path.isDir("/usr/bin") == true); diff --git a/tests/path/realpath.du b/tests/path/realpath.du index 858f83a1..3d500b93 100644 --- a/tests/path/realpath.du +++ b/tests/path/realpath.du @@ -6,6 +6,7 @@ * Returns the canonicalized absolute pathname or nil on error. */ import Path; +import System; if (System.platform != "windows") { assert(Path.delimiter == ":"); diff --git a/tests/process/exec.du b/tests/process/exec.du index cb4b4f3e..e2337774 100644 --- a/tests/process/exec.du +++ b/tests/process/exec.du @@ -6,6 +6,7 @@ * exec() executes a new process and does not wait for a return. */ import Process; +import System; if (System.platform == "windows") { assert(Process.exec(["cmd.exe", "/c", "dir"]).success()); diff --git a/tests/process/run.du b/tests/process/run.du index 060bd7a4..bc96e54e 100644 --- a/tests/process/run.du +++ b/tests/process/run.du @@ -6,6 +6,7 @@ * run() executes a new process and does wait for a return. */ import Process; +import System; if (System.platform == "windows") { assert(Process.run(["cmd.exe", "/c", "dir"]).success()); diff --git a/tests/system/access.du b/tests/system/access.du index 139f4c19..5b3e5871 100644 --- a/tests/system/access.du +++ b/tests/system/access.du @@ -5,6 +5,7 @@ * * access() returns if user has the requested permissions for a file. */ +import System; var F_OK = System.F_OK, diff --git a/tests/system/clock.du b/tests/system/clock.du index a916965b..b23da617 100644 --- a/tests/system/clock.du +++ b/tests/system/clock.du @@ -5,6 +5,7 @@ * * clock() returns number of clock ticks since the start of the program, useful for benchmarks. */ +import System; assert(type(System.clock()) == 'number'); assert(System.clock() > 0); diff --git a/tests/system/constants.du b/tests/system/constants.du index 584300a8..d56dfdb9 100644 --- a/tests/system/constants.du +++ b/tests/system/constants.du @@ -4,6 +4,7 @@ * Testing the System constants * */ +import System; /** * argv is a list of all parameters passed to the interpreter. diff --git a/tests/system/getCWD.du b/tests/system/getCWD.du index e4ff2c2e..427300c1 100644 --- a/tests/system/getCWD.du +++ b/tests/system/getCWD.du @@ -5,6 +5,7 @@ * * getCWD() gets the current working directory of the process */ +import System; assert(type(System.getCWD().unwrap()) == 'string'); assert(System.getCWD().unwrap().len() > 0); diff --git a/tests/system/mkdir.du b/tests/system/mkdir.du index a2609622..236817a3 100644 --- a/tests/system/mkdir.du +++ b/tests/system/mkdir.du @@ -6,6 +6,7 @@ * mkdir() creates a new directory with the given permissions * rmdir() removes a directory */ +import System; var S_IRWXU = System.S_IRWXU, diff --git a/tests/system/process.du b/tests/system/process.du index 09945013..90cfbd5f 100644 --- a/tests/system/process.du +++ b/tests/system/process.du @@ -6,6 +6,7 @@ * getpid(), getppid(), getuid(), geteuid(), getgid(), getegid() all return information * about the running process. */ +import System; if (System.platform != "windows") { assert(type(System.getpid()) == "number"); diff --git a/tests/system/remove.du b/tests/system/remove.du index eb18dd3e..1689983e 100644 --- a/tests/system/remove.du +++ b/tests/system/remove.du @@ -5,6 +5,7 @@ * * remove() removes a file from the system */ +import System; if (System.platform == 'darwin') { var sys_test_remove_file = "sys_test_remove."; diff --git a/tests/system/setCWD.du b/tests/system/setCWD.du index 384afa99..2e039f0b 100644 --- a/tests/system/setCWD.du +++ b/tests/system/setCWD.du @@ -5,6 +5,7 @@ * * setCWD() sets the current working directory */ +import System; var cwd = System.getCWD().unwrap(); assert(cwd != nil); diff --git a/tests/system/sleep.du b/tests/system/sleep.du index 087ecd49..316e01bd 100644 --- a/tests/system/sleep.du +++ b/tests/system/sleep.du @@ -5,6 +5,7 @@ * * sleep() pauses execution for a given amount of seconds */ +import System; var start = System.time(); diff --git a/tests/system/time.du b/tests/system/time.du index 62fd9c86..642a7c83 100644 --- a/tests/system/time.du +++ b/tests/system/time.du @@ -5,6 +5,7 @@ * * time() returns a UNIX timestamp. */ +import System; // Time is also tested within sleep.du diff --git a/tests/system/version.du b/tests/system/version.du index 45266a3e..9d0a4621 100644 --- a/tests/system/version.du +++ b/tests/system/version.du @@ -5,6 +5,7 @@ * * version is a dictionary denoting the Dictu version. */ +import System; assert(type(System.version) == "dict"); assert(System.version.len() == 3); From ac58d6ac5d8091db95abf36b290f33fe854c7dbd Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 4 Oct 2021 19:40:47 +0100 Subject: [PATCH 09/32] Update pull_request_template.md --- .github/pull_request_template.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 380d07e7..1089af6d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,19 +10,19 @@ - I worked on the ..... + # ### Context of the change : - + + - - Why is this change required ? -- Does it solve a problem ? (please link the issue) +Resolves: # @@ -45,9 +45,4 @@ ### Preview (Screenshots) : - - - - -

If it is possible, please link screenshots of your changes preview ! -

+ From cc586bf4ac9a3fa29aa1ed69dde19c3c9d807bc3 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 4 Oct 2021 22:18:10 +0100 Subject: [PATCH 10/32] Add pathWalker to runExamples --- examples/runExamples.du | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/runExamples.du b/examples/runExamples.du index a8bb52fe..eca6ee2c 100644 --- a/examples/runExamples.du +++ b/examples/runExamples.du @@ -16,3 +16,4 @@ import "guessingGame.du"; import "inheritance.du"; import "isPalindrome.du"; import "factorial.du"; +import "pathWalker.du"; From 32fef140e9864740a35eb8a8b32f97770172f9fd Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 5 Oct 2021 21:27:31 +0100 Subject: [PATCH 11/32] Range of updates --- docs/docs/control-flow.md | 8 ++++---- docs/docs/standard-lib/math.md | 2 +- docs/docs/strings.md | 2 +- examples/README.md | 2 +- examples/design-patterns/README.md | 4 ++-- src/optionals/math.c | 2 +- src/vm/memory.c | 2 +- src/vm/vm.c | 4 ++-- tests/maths/maths.du | 4 ++-- tests/modules/from.du | 4 ++-- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/docs/control-flow.md b/docs/docs/control-flow.md index 7906c240..e0b465d5 100644 --- a/docs/docs/control-flow.md +++ b/docs/docs/control-flow.md @@ -49,7 +49,7 @@ if (x == 6) { var i = 0; while (i < 10) { print(i); - ++i; + i += 1; } ``` @@ -68,7 +68,7 @@ while { ```cs // For loop -for (var i = 0; i < 10; ++i) { +for (var i = 0; i < 10; i += 1) { print(i); } ``` @@ -79,7 +79,7 @@ Continue allows execution of a loop to restart prematurely. ```cs // For loop -for (var i = 0; i < 10; ++i) { +for (var i = 0; i < 10; i += 1) { if (i % 2 == 0) continue; // Skip all even numbers @@ -93,7 +93,7 @@ Break allows execution of a loop to stop prematurely. ```cs // For loop -for (var i = 0; i < 10; ++i) { +for (var i = 0; i < 10; i += 1) { if (i > 5) break; // Exit the loop here diff --git a/docs/docs/standard-lib/math.md b/docs/docs/standard-lib/math.md index ee72baa9..ac98d6fe 100644 --- a/docs/docs/standard-lib/math.md +++ b/docs/docs/standard-lib/math.md @@ -29,7 +29,7 @@ import Math; | Constant | Description | |--------------|--------------------------------------------------------| -| Math.PI | The mathematical constant: 3.14159265358979 | +| Math.pi | The mathematical constant: 3.14159265358979 | | Math.e | The mathematical constant: 2.71828182845905 | ### Math.min(iterable) diff --git a/docs/docs/strings.md b/docs/docs/strings.md index 95131dc9..0f8a5e3f 100644 --- a/docs/docs/strings.md +++ b/docs/docs/strings.md @@ -38,7 +38,7 @@ string[0]; // D string[-1]; // u string[100]; // String index out of bounds. -for (var i = 0; i < x.len(); ++i) { +for (var i = 0; i < x.len(); i += 1) { print(string[i]); } // D diff --git a/examples/README.md b/examples/README.md index 551f723d..a6024ccf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -62,7 +62,7 @@ var amount = input("Enter a number: ").toNumber(); var num = 1; if (amount > 0) { - for (var i = 1; i < amount + 1; ++i) { + for (var i = 1; i < amount + 1; i += 1) { num *= i; } diff --git a/examples/design-patterns/README.md b/examples/design-patterns/README.md index 622c77b0..8d1fb804 100644 --- a/examples/design-patterns/README.md +++ b/examples/design-patterns/README.md @@ -148,7 +148,7 @@ class DogHandler < BaseHandler { def businessLogic(handler) { var food = ["Nut", "Banana", "Coffee"]; - for (var i = 0; i < food.len(); ++i) { + for (var i = 0; i < food.len(); i += 1) { print("Who wants a: {}".format(food[i])); var response = handler.handle(food[i]); @@ -199,7 +199,7 @@ class Publisher { * Notify all subscribed observers */ notify() { - for (var i = 0; i < this.observers.len(); ++i) { + for (var i = 0; i < this.observers.len(); i += 1) { this.observers[i].update(); } } diff --git a/src/optionals/math.c b/src/optionals/math.c index d091566d..d9b84dda 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -354,7 +354,7 @@ ObjModule *createMathsModule(DictuVM *vm) { /** * Define Math properties */ - defineNativeProperty(vm, &module->values, "PI", NUMBER_VAL(3.14159265358979)); + defineNativeProperty(vm, &module->values, "pi", NUMBER_VAL(3.14159265358979)); defineNativeProperty(vm, &module->values, "e", NUMBER_VAL(2.71828182845905)); pop(vm); pop(vm); diff --git a/src/vm/memory.c b/src/vm/memory.c index 81d47465..0f9f78ba 100644 --- a/src/vm/memory.c +++ b/src/vm/memory.c @@ -16,7 +16,7 @@ void *reallocate(DictuVM *vm, void *previous, size_t oldSize, size_t newSize) { vm->bytesAllocated += newSize - oldSize; #ifdef DEBUG_TRACE_MEM - printf("Total memory usage: %zu\nNew allocation: %zu\nOld allocation: %zu\n\n", vm->bytesAllocated, newSize, oldSize); + printf("Total bytes allocated: %zu\nNew allocation: %zu\nOld allocation: %zu\n\n", vm->bytesAllocated, newSize, oldSize); #endif if (newSize > oldSize) { diff --git a/src/vm/vm.c b/src/vm/vm.c index 561ece68..bf974e43 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -162,9 +162,9 @@ void dictuFreeVM(DictuVM *vm) { #if defined(DEBUG_TRACE_MEM) || defined(DEBUG_FINAL_MEM) #ifdef __MINGW32__ - printf("Total memory usage: %lu\n", (unsigned long)vm->bytesAllocated); + printf("Total bytes lost: %lu\n", (unsigned long)vm->bytesAllocated); #else - printf("Total memory usage: %zu\n", vm->bytesAllocated); + printf("Total bytes lost: %zu\n", vm->bytesAllocated); #endif #endif diff --git a/tests/maths/maths.du b/tests/maths/maths.du index e8fcf388..2ce7cd3d 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -5,7 +5,7 @@ * - min(), max(), average(), sum(), floor(), round(), ceil(), abs() * * Testing the Math constants: - * - PI, e + * - pi, e * */ import Math; @@ -39,5 +39,5 @@ assert(Math.gcd([32, 24, 12]) == 4); assert(Math.lcm(32, 24, 12) == 96); assert(Math.lcm([32, 24, 12]) == 96); -assert(Math.PI == 3.14159265358979); +assert(Math.pi == 3.14159265358979); assert(Math.e == 2.71828182845905); \ No newline at end of file diff --git a/tests/modules/from.du b/tests/modules/from.du index 0ccd4cfd..066aee0d 100644 --- a/tests/modules/from.du +++ b/tests/modules/from.du @@ -25,7 +25,7 @@ assert(parse("10").unwrap() == 10); assert(parse("10").unwrap() == 10); { - from Math import PI; - assert(PI == 3.14159265358979); + from Math import pi; + assert(pi == 3.14159265358979); } } \ No newline at end of file From f9c3c36b324b7ead18a3038a99d143fc0a07a7ca Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 5 Oct 2021 21:34:51 +0100 Subject: [PATCH 12/32] Update the CI/CD check for memory leak --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 15e93f0f..97f41dbe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: run: | cmake -DCMAKE_BUILD_TYPE=Debug -DDISABLE_HTTP=1 -B ./build cmake --build ./build - ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total memory usage: 0' + ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total bytes lost: 0' - name: Remove build directory run: | rm -rf build @@ -30,7 +30,7 @@ jobs: sudo apt-get install -y libcurl4-openssl-dev cmake -DCMAKE_BUILD_TYPE=Debug -B ./build cmake --build ./build - ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total memory usage: 0' + ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total bytes lost: 0' test-mac-cmake: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -44,7 +44,7 @@ jobs: run: | cmake -DCMAKE_BUILD_TYPE=Debug -DDISABLE_HTTP=1 -B ./build cmake --build ./build - ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total memory usage: 0' + ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total bytes lost: 0' - name: Remove build directory run: | rm -rf build @@ -52,7 +52,7 @@ jobs: run: | cmake -DCMAKE_BUILD_TYPE=Debug -B ./build cmake --build ./build - ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total memory usage: 0' + ./dictu tests/runTests.du | tee /dev/stderr | grep -q 'Total bytes lost: 0' test-windows-cmake: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -78,4 +78,4 @@ jobs: sudo apt-get install -y libcurl4-openssl-dev cmake -DCMAKE_BUILD_TYPE=Debug -B ./build cmake --build ./build - ./dictu examples/runExamples.du | tee /dev/stderr | grep -q 'Total memory usage: 0' + ./dictu examples/runExamples.du | tee /dev/stderr | grep -q 'Total bytes lost: 0' From d6ea10dbee3abbac6a0bc3a6246c0ca0fd529211 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 5 Oct 2021 21:51:45 +0100 Subject: [PATCH 13/32] Add System import to example program --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0d6a935c..97956303 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Documentation for Dictu can be found [here](https://dictu-lang.com/) ## Example programs ```js +import System; + const guess = 10; while { From dd604fd2a12f1a8196faa090e407ccd7a19e0f9a Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 6 Oct 2021 01:29:39 +0100 Subject: [PATCH 14/32] obj.getAttribute to return bound method rather than function --- src/vm/datatypes/instance.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vm/datatypes/instance.c b/src/vm/datatypes/instance.c index 51aca3c8..34f7ca03 100644 --- a/src/vm/datatypes/instance.c +++ b/src/vm/datatypes/instance.c @@ -66,7 +66,9 @@ static Value getAttribute(DictuVM *vm, int argCount, Value *args) { } if (tableGet(&instance->klass->publicMethods, AS_STRING(key), &value)) { - return value; + ObjBoundMethod *bound = newBoundMethod(vm, OBJ_VAL(instance), AS_CLOSURE(value)); + + return OBJ_VAL(bound); } // Check class for properties From f802ec6b05a20076ab176926c6c1cbe42c0960d9 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 6 Oct 2021 14:33:17 +0100 Subject: [PATCH 15/32] Add new methods() method which returns list of public methods --- docs/docs/classes.md | 22 +++++++++++ src/vm/datatypes/class.c | 24 ++++++++++++ src/vm/datatypes/instance.c | 39 +++++++++++++++++++ tests/classes/import.du | 3 +- tests/classes/methods.du | 74 +++++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 tests/classes/methods.du diff --git a/docs/docs/classes.md b/docs/docs/classes.md index 89f51b72..99a87b43 100644 --- a/docs/docs/classes.md +++ b/docs/docs/classes.md @@ -146,6 +146,28 @@ print(TestOverload().toString()); // 'Testing object' ``` +### methods + +Sometimes we need to programmatically access methods that are stored within a class, this can be aided through the use of `.methods()`. This +will return a list of strings, where the strings are the names of all public methods stored within a class. + +Note: The order of the list is not the same order the methods are defined. + +```cs +class Test { + init() { + + } + + someOther() { + + } +} + +print(Test.methods()); // ["someOther", "init"] +print(Test().methods()); // Works on instances too - ["someOther", "init"] +``` + ## This `this` is a variable which is passed to all methods which are not marked as static. `this` is a reference to the object you are currently accessing. `this` allows you to modify instance variables of a particular object. diff --git a/src/vm/datatypes/class.c b/src/vm/datatypes/class.c index 21304e7c..6c787d81 100644 --- a/src/vm/datatypes/class.c +++ b/src/vm/datatypes/class.c @@ -16,6 +16,30 @@ static Value toString(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(string); } +static Value methods(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0) { + runtimeError(vm, "methods() takes no arguments (%d given)", argCount); + return EMPTY_VAL; + } + + ObjClass *klass = AS_CLASS(args[0]); + + ObjList *list = newList(vm); + push(vm, OBJ_VAL(list)); + + for (int i = 0; i < klass->publicMethods.capacityMask; ++i) { + if (klass->publicMethods.entries[i].key == NULL) { + continue; + } + + writeValueArray(vm, &list->values, OBJ_VAL(klass->publicMethods.entries[i].key)); + } + pop(vm); + + return OBJ_VAL(list); +} + void declareClassMethods(DictuVM *vm) { defineNative(vm, &vm->classMethods, "toString", toString); + defineNative(vm, &vm->classMethods, "methods", methods); } diff --git a/src/vm/datatypes/instance.c b/src/vm/datatypes/instance.c index 34f7ca03..95ea5502 100644 --- a/src/vm/datatypes/instance.c +++ b/src/vm/datatypes/instance.c @@ -36,6 +36,21 @@ static Value hasAttribute(DictuVM *vm, int argCount, Value *args) { return TRUE_VAL; } + if (tableGet(&instance->klass->publicMethods, AS_STRING(value), &value)) { + return TRUE_VAL; + } + + // Check class for properties + ObjClass *klass = instance->klass; + + while (klass != NULL) { + if (tableGet(&klass->publicProperties, AS_STRING(value), &value)) { + return TRUE_VAL; + } + + klass = klass->superclass; + } + return FALSE_VAL; } @@ -156,6 +171,29 @@ static Value copyDeep(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(instance); } +static Value methods(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0) { + runtimeError(vm, "methods() takes no arguments (%d given)", argCount); + return EMPTY_VAL; + } + + ObjInstance *instance = AS_INSTANCE(args[0]); + + ObjList *list = newList(vm); + push(vm, OBJ_VAL(list)); + + for (int i = 0; i < instance->klass->publicMethods.capacityMask; ++i) { + if (instance->klass->publicMethods.entries[i].key == NULL) { + continue; + } + + writeValueArray(vm, &list->values, OBJ_VAL(instance->klass->publicMethods.entries[i].key)); + } + pop(vm); + + return OBJ_VAL(list); +} + void declareInstanceMethods(DictuVM *vm) { defineNative(vm, &vm->instanceMethods, "toString", toString); defineNative(vm, &vm->instanceMethods, "hasAttribute", hasAttribute); @@ -164,4 +202,5 @@ void declareInstanceMethods(DictuVM *vm) { defineNative(vm, &vm->instanceMethods, "isInstance", isInstance); defineNative(vm, &vm->instanceMethods, "copy", copyShallow); defineNative(vm, &vm->instanceMethods, "deepCopy", copyDeep); + defineNative(vm, &vm->instanceMethods, "methods", methods); } diff --git a/tests/classes/import.du b/tests/classes/import.du index 331911b4..6e221983 100644 --- a/tests/classes/import.du +++ b/tests/classes/import.du @@ -19,4 +19,5 @@ import "constructor.du"; import "optionalChaining.du"; import "private.du"; import "annotations.du"; -import "getAttributes.du"; \ No newline at end of file +import "getAttributes.du"; +import "methods.du"; \ No newline at end of file diff --git a/tests/classes/methods.du b/tests/classes/methods.du new file mode 100644 index 00000000..1288ea8e --- /dev/null +++ b/tests/classes/methods.du @@ -0,0 +1,74 @@ +/** + * toString.du + * + * Testing the class.methods() and instance.methods() method + * + * .methods() returns the a list of all public methods attached to a class + */ + +class Test { + init() {} + + someOtherFunc() {} + + static someStaticFunc() {} + + private somePrivateFunc() {} +} + +const expectedMethods = [ + "init", + "someOtherFunc", + "someStaticFunc" +]; + +assert(Test.methods().len() == 3); +expectedMethods.forEach(def (method) => { + assert(Test.methods().contains(method)); +}); + +assert(!Test.methods().contains("somePrivateFunc")); + +/** + * Test instance + */ +assert(Test().methods().len() == 3); +expectedMethods.forEach(def (method) => { + assert(Test().methods().contains(method)); +}); + +assert(!Test().methods().contains("somePrivateFunc")); + +/** + * Inheritance + */ + +class AnotherTest < Test { + anotherFunc() {} + + private anotherPrivateFunc() {} +} + +const expectedMethodsInheritance = [ + "init", + "someOtherFunc", + "someStaticFunc", + "anotherFunc" +]; + +assert(AnotherTest.methods().len() == 4); +expectedMethodsInheritance.forEach(def (method) => { + assert(AnotherTest.methods().contains(method)); +}); + +assert(!AnotherTest.methods().contains("somePrivateFunc")); + +/** + * Test instance + */ +assert(AnotherTest().methods().len() == 4); +expectedMethodsInheritance.forEach(def (method) => { + assert(AnotherTest().methods().contains(method)); +}); + +assert(!AnotherTest().methods().contains("somePrivateFunc")); \ No newline at end of file From 4035d24e824c60d099f72a81e72f93ad62330e9c Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Thu, 7 Oct 2021 19:35:14 +0530 Subject: [PATCH 16/32] Added merge utility method for dictionary --- docs/docs/collections/dictionaries.md | 13 +++++++++++++ src/vm/datatypes/dicts/dict-source.h | 9 +++++++++ src/vm/datatypes/dicts/dict.du | 10 ++++++++++ tests/dicts/merge.du | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 tests/dicts/merge.du diff --git a/docs/docs/collections/dictionaries.md b/docs/docs/collections/dictionaries.md index 63c850e6..e2d443d9 100644 --- a/docs/docs/collections/dictionaries.md +++ b/docs/docs/collections/dictionaries.md @@ -143,4 +143,17 @@ const myDict = {"key": 1, "key1": 2}; myDict.forEach(def (key, value) => { print("Key: {} Value: {}".format(key, value)); }); +``` + +### dict.merge(anotherDict) + +To merge with another dictionary. This operation will produce a new object. If another dictionary contains a key that exists in invoking one, the invoking dictionary's key will be taken. + +```cs +const dictOne = {"key": 1, "key1": 2, "key2": 3}; +const dictTwo = {"key3": 4,"key1":0}; + +const mergedDict=dictOne.merge(dictTwo); + +mergedDict; //{"key2": 3, "key": 1, "key3": 4, "key1": 2} ``` \ No newline at end of file diff --git a/src/vm/datatypes/dicts/dict-source.h b/src/vm/datatypes/dicts/dict-source.h index 022e782a..e768f328 100644 --- a/src/vm/datatypes/dicts/dict-source.h +++ b/src/vm/datatypes/dicts/dict-source.h @@ -11,4 +11,13 @@ " func(dictKeys[i], dict[dictKeys[i]]);\n" \ " }\n" \ "}\n" \ +"def merge(dict, anotherDict) {\n"\ +" const dictKeys = dict.keys();\n"\ +" const newDict= anotherDict.copy();\n"\ +"\n"\ +" for (var i = 0; i < dictKeys.len(); i += 1) {\n"\ +" newDict[dictKeys[i]]=dict[dictKeys[i]];\n"\ +" }\n"\ +" return newDict;\n"\ +"\n}"\ diff --git a/src/vm/datatypes/dicts/dict.du b/src/vm/datatypes/dicts/dict.du index 5730a8af..112a50d5 100644 --- a/src/vm/datatypes/dicts/dict.du +++ b/src/vm/datatypes/dicts/dict.du @@ -11,3 +11,13 @@ def forEach(dict, func) { func(dictKeys[i], dict[dictKeys[i]]); } } + +def merge(dict, anotherDict) { + const dictKeys = dict.keys(); + const newDict= anotherDict.copy(); + + for (var i = 0; i < dictKeys.len(); i += 1) { + newDict[dictKeys[i]]=dict[dictKeys[i]]; + } + return newDict; +} diff --git a/tests/dicts/merge.du b/tests/dicts/merge.du new file mode 100644 index 00000000..c3e51c07 --- /dev/null +++ b/tests/dicts/merge.du @@ -0,0 +1,24 @@ +/** + * merge.du + * + * Testing the dict.merge() method + * + * .merge() To merge with another dictionary. This operation will produce a new object. If another dictionary contains a key that exists in invoking one, the invoking dictionary's key will be taken.. + */ + +const dictOne = {"key": 1, "key1": 2, "key2": 3}; +const dictTwo = {"key3": 4,"key1":0}; + +const mergedDict=dictOne.merge(dictTwo); + +assert(mergedDict.keys().len() == 4); + +assert(mergedDict.exists("key")); +assert(mergedDict.exists("key1")); +assert(mergedDict.exists("key2")); +assert(mergedDict.exists("key3")); + +assert(mergedDict["key"] ==1); +assert(mergedDict["key1"]==2); +assert(mergedDict["key2"]==3); +assert(mergedDict["key3"]==4); From 39a47517999b697eb9f11a29faeb9deaa694b196 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 7 Oct 2021 16:37:24 +0100 Subject: [PATCH 17/32] Update pull_request_template.md --- .github/pull_request_template.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1089af6d..a00f3ea8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -12,25 +12,17 @@ -# - -### Context of the change : - - - - - Resolves: # -### Type of change : +### Type of change: - + @@ -43,6 +35,13 @@ Resolves: # +### Housekeeping + + +- [ ] Tests have been updated to reflect the changes done within this PR (if applicable). + +- [ ] Documentation has been updated to reflect the changes done within this PR (if applicable). + ### Preview (Screenshots) : From b4c4ffae83906f788065d23303d5d4d17a40582a Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Thu, 7 Oct 2021 21:53:08 +0530 Subject: [PATCH 18/32] Added merge utility method for dictionary --- docs/docs/collections/dictionaries.md | 2 +- tests/dicts/import.du | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/collections/dictionaries.md b/docs/docs/collections/dictionaries.md index e2d443d9..d4fbe5bf 100644 --- a/docs/docs/collections/dictionaries.md +++ b/docs/docs/collections/dictionaries.md @@ -153,7 +153,7 @@ To merge with another dictionary. This operation will produce a new object. If a const dictOne = {"key": 1, "key1": 2, "key2": 3}; const dictTwo = {"key3": 4,"key1":0}; -const mergedDict=dictOne.merge(dictTwo); +const mergedDict = dictOne.merge(dictTwo); mergedDict; //{"key2": 3, "key": 1, "key3": 4, "key1": 2} ``` \ No newline at end of file diff --git a/tests/dicts/import.du b/tests/dicts/import.du index 6a0d39d2..f755da74 100644 --- a/tests/dicts/import.du +++ b/tests/dicts/import.du @@ -14,3 +14,4 @@ import "len.du"; import "toString.du"; import "toBool.du"; import "forEach.du"; +import "merge.du"; From bbdb2d8be9f43d05446943cdb77243d7377c7413 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Mon, 11 Oct 2021 21:46:36 +0530 Subject: [PATCH 19/32] Added Env.getOrDefault method --- docs/docs/standard-lib/env.md | 9 +++++++++ src/optionals/env.c | 27 +++++++++++++++++++++++++++ tests/env/env.du | 4 +++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 191fc052..675c2ed8 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -44,3 +44,12 @@ Env.set("key", "test"); Env.set("key", nil); // Remove env var Env.set("key", 10); // set() arguments must be a string or nil. ``` + +### Env.getOrDefault(string) + +Get an environment variable , if the variable not exists then take default value. `.getOrDefault()` will return a string if a valid environment variable is found otherwise returns default value that is passed one. + +```cs +Env.get("valid key"); // "value" +Env.getOrDefault("bad key","default value!!!"); //"default value!!!" +``` diff --git a/src/optionals/env.c b/src/optionals/env.c index a466ba32..3b5de296 100644 --- a/src/optionals/env.c +++ b/src/optionals/env.c @@ -35,6 +35,32 @@ static Value get(DictuVM *vm, int argCount, Value *args) { return NIL_VAL; } +static Value getOrDefault(DictuVM *vm, int argCount, Value *args) { + if (argCount != 2) { + runtimeError(vm, "getOrDefault() takes 2 arguments (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0])||!IS_STRING(args[1])) { + runtimeError(vm, "getOrDefault() arguments must be a string."); + return EMPTY_VAL; + } + + char *value = getenv(AS_CSTRING(args[0])); + char *defaultValue = AS_CSTRING(args[1]); + + if (defaultValue == NULL) { + runtimeError(vm, "getOrDefault() second argument must not be null"); + return EMPTY_VAL; + } + + if (value != NULL) { + return OBJ_VAL(copyString(vm, value, strlen(value))); + } + + return OBJ_VAL(copyString(vm, defaultValue, strlen(defaultValue))); +} + static Value set(DictuVM *vm, int argCount, Value *args) { if (argCount != 2) { runtimeError(vm, "set() takes 2 arguments (%d given).", argCount); @@ -73,6 +99,7 @@ ObjModule *createEnvModule(DictuVM *vm) { * Define Env methods */ defineNative(vm, &module->values, "get", get); + defineNative(vm, &module->values, "getOrDefault", getOrDefault); defineNative(vm, &module->values, "set", set); pop(vm); diff --git a/tests/env/env.du b/tests/env/env.du index 0b912572..a6c7950c 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -2,7 +2,7 @@ * end.du * * Testing the Env functions: - * - get(), set() + * - get(), set(), -getOrDefault() * */ import Env; @@ -13,3 +13,5 @@ assert(Env.set("test", "test").success() == true); assert(Env.get("test") == "test"); assert(Env.set("test", nil).success() == true); assert(Env.get("test") == nil); + +assert(Env.getOrDefault("good_key","default_value") == "default_value"); From 871c32f860e3dd5aaf961209de38926ee58d434f Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Mon, 11 Oct 2021 21:48:04 +0530 Subject: [PATCH 20/32] Added Env.getOrDefault method --- docs/docs/standard-lib/env.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 675c2ed8..ef871b2e 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -45,7 +45,7 @@ Env.set("key", nil); // Remove env var Env.set("key", 10); // set() arguments must be a string or nil. ``` -### Env.getOrDefault(string) +### Env.getOrDefault(string,defaultValue) Get an environment variable , if the variable not exists then take default value. `.getOrDefault()` will return a string if a valid environment variable is found otherwise returns default value that is passed one. From 6b21155536185b573af5c0359154a04258913440 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Tue, 12 Oct 2021 13:49:11 +0530 Subject: [PATCH 21/32] Added Env.getOrDefault method --- docs/docs/standard-lib/env.md | 14 ++++-------- src/optionals/env.c | 43 ++++++++++++----------------------- tests/env/env.du | 6 ++--- 3 files changed, 22 insertions(+), 41 deletions(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index ef871b2e..c5dc57de 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -24,13 +24,16 @@ To make use of the Env module an import is required. import Env; ``` -### Env.get(string) +### Env.get(string,string: defaultValue -> optional) Get an environment variable. `.get()` will return a string if a valid environment variable is found otherwise nil. +If default value is passed, then it will return that incase environment variable not found. + ```cs Env.get("bad key!"); // nil Env.get("valid key"); // "value" +Env.get("bad key!","default value!!!"); //"default value!!!" ``` ### Env.set(string, value) @@ -44,12 +47,3 @@ Env.set("key", "test"); Env.set("key", nil); // Remove env var Env.set("key", 10); // set() arguments must be a string or nil. ``` - -### Env.getOrDefault(string,defaultValue) - -Get an environment variable , if the variable not exists then take default value. `.getOrDefault()` will return a string if a valid environment variable is found otherwise returns default value that is passed one. - -```cs -Env.get("valid key"); // "value" -Env.getOrDefault("bad key","default value!!!"); //"default value!!!" -``` diff --git a/src/optionals/env.c b/src/optionals/env.c index 3b5de296..7e40d2fc 100644 --- a/src/optionals/env.c +++ b/src/optionals/env.c @@ -16,49 +16,37 @@ int setenv(const char *name, const char *value, int overwrite) { #endif static Value get(DictuVM *vm, int argCount, Value *args) { - if (argCount != 1) { - runtimeError(vm, "get() takes 1 argument (%d given).", argCount); + if (argCount != 1 && argCount != 2 ) { + runtimeError(vm, "get() takes either 1 or 2 arguments (%d given).", argCount); return EMPTY_VAL; } if (!IS_STRING(args[0])) { - runtimeError(vm, "get() argument must be a string."); + runtimeError(vm, "get() arguments must be a string."); return EMPTY_VAL; } - + char *value = getenv(AS_CSTRING(args[0])); - if (value != NULL) { - return OBJ_VAL(copyString(vm, value, strlen(value))); - } + if(argCount == 2){ + if (!IS_STRING(args[1])) { + runtimeError(vm, "get() arguments must be a string."); + return EMPTY_VAL; + } - return NIL_VAL; -} + if (value != NULL) { + return OBJ_VAL(copyString(vm, value, strlen(value))); + } -static Value getOrDefault(DictuVM *vm, int argCount, Value *args) { - if (argCount != 2) { - runtimeError(vm, "getOrDefault() takes 2 arguments (%d given).", argCount); - return EMPTY_VAL; - } - - if (!IS_STRING(args[0])||!IS_STRING(args[1])) { - runtimeError(vm, "getOrDefault() arguments must be a string."); - return EMPTY_VAL; - } - - char *value = getenv(AS_CSTRING(args[0])); - char *defaultValue = AS_CSTRING(args[1]); - - if (defaultValue == NULL) { - runtimeError(vm, "getOrDefault() second argument must not be null"); - return EMPTY_VAL; + char *defaultValue = AS_CSTRING(args[1]); + return OBJ_VAL(copyString(vm, defaultValue, strlen(defaultValue))); } if (value != NULL) { return OBJ_VAL(copyString(vm, value, strlen(value))); } - return OBJ_VAL(copyString(vm, defaultValue, strlen(defaultValue))); + return NIL_VAL; } static Value set(DictuVM *vm, int argCount, Value *args) { @@ -99,7 +87,6 @@ ObjModule *createEnvModule(DictuVM *vm) { * Define Env methods */ defineNative(vm, &module->values, "get", get); - defineNative(vm, &module->values, "getOrDefault", getOrDefault); defineNative(vm, &module->values, "set", set); pop(vm); diff --git a/tests/env/env.du b/tests/env/env.du index a6c7950c..063f4ace 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -2,16 +2,16 @@ * end.du * * Testing the Env functions: - * - get(), set(), -getOrDefault() + * - get(), set() * */ import Env; assert(Env.get("bad key") == nil); +assert(Env.get("bad key","optional value!!!") == "optional value!!!"); + assert(Env.set("test", "test").success() == true); assert(Env.get("test") == "test"); assert(Env.set("test", nil).success() == true); assert(Env.get("test") == nil); - -assert(Env.getOrDefault("good_key","default_value") == "default_value"); From 12ab0ac10a45a0dce7bb2271e8e80c2d1334562a Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 12 Oct 2021 20:56:28 +0530 Subject: [PATCH 22/32] Update src/optionals/env.c Co-authored-by: Jason_000 --- src/optionals/env.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/optionals/env.c b/src/optionals/env.c index 7e40d2fc..0e626b4a 100644 --- a/src/optionals/env.c +++ b/src/optionals/env.c @@ -38,8 +38,7 @@ static Value get(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(copyString(vm, value, strlen(value))); } - char *defaultValue = AS_CSTRING(args[1]); - return OBJ_VAL(copyString(vm, defaultValue, strlen(defaultValue))); + return args[1]; } if (value != NULL) { From 52a8700e1e51796e11bd578b3484236c0befedc8 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 12 Oct 2021 20:56:35 +0530 Subject: [PATCH 23/32] Update docs/docs/standard-lib/env.md Co-authored-by: Jason_000 --- docs/docs/standard-lib/env.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index c5dc57de..bfa86f1d 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -28,7 +28,7 @@ import Env; Get an environment variable. `.get()` will return a string if a valid environment variable is found otherwise nil. -If default value is passed, then it will return that incase environment variable not found. +If default value is passed that will be returned if the specified environment variable could not be found. ```cs Env.get("bad key!"); // nil From 59a4492436ff805e526d6172a191c9c301ec9f50 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 12 Oct 2021 20:56:40 +0530 Subject: [PATCH 24/32] Update src/optionals/env.c Co-authored-by: Jason_000 --- src/optionals/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionals/env.c b/src/optionals/env.c index 0e626b4a..e125e1b7 100644 --- a/src/optionals/env.c +++ b/src/optionals/env.c @@ -28,7 +28,7 @@ static Value get(DictuVM *vm, int argCount, Value *args) { char *value = getenv(AS_CSTRING(args[0])); - if(argCount == 2){ + if (argCount == 2) { if (!IS_STRING(args[1])) { runtimeError(vm, "get() arguments must be a string."); return EMPTY_VAL; From c3129d6a5b9049325fcd8fc2f15886c7c8a7c7ed Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 12 Oct 2021 20:56:55 +0530 Subject: [PATCH 25/32] Update docs/docs/standard-lib/env.md Co-authored-by: Jason_000 --- docs/docs/standard-lib/env.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index bfa86f1d..f4082e20 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -24,7 +24,7 @@ To make use of the Env module an import is required. import Env; ``` -### Env.get(string,string: defaultValue -> optional) +### Env.get(string, string: defaultValue -> optional) Get an environment variable. `.get()` will return a string if a valid environment variable is found otherwise nil. From 9ed63b05ddac0512db75bd1002e12eda6e0a6bdf Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 12 Oct 2021 17:20:43 +0100 Subject: [PATCH 26/32] Update docs/docs/standard-lib/env.md --- docs/docs/standard-lib/env.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index f4082e20..0b8cd607 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -33,7 +33,7 @@ If default value is passed that will be returned if the specified environment va ```cs Env.get("bad key!"); // nil Env.get("valid key"); // "value" -Env.get("bad key!","default value!!!"); //"default value!!!" +Env.get("bad key!", "default value!!!"); // "default value!!!" ``` ### Env.set(string, value) From 8f9b69e4c744a89a288a23490f74c2168a472f82 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 19 Oct 2021 19:36:01 +0100 Subject: [PATCH 27/32] Implement resultWrap --- src/vm/datatypes/dicts/dict-source.h | 19 ++++++++++--------- src/vm/datatypes/result/result-source.h | 8 ++++++++ src/vm/datatypes/result/result.du | 8 ++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/vm/datatypes/dicts/dict-source.h b/src/vm/datatypes/dicts/dict-source.h index e768f328..91a4dbd9 100644 --- a/src/vm/datatypes/dicts/dict-source.h +++ b/src/vm/datatypes/dicts/dict-source.h @@ -11,13 +11,14 @@ " func(dictKeys[i], dict[dictKeys[i]]);\n" \ " }\n" \ "}\n" \ -"def merge(dict, anotherDict) {\n"\ -" const dictKeys = dict.keys();\n"\ -" const newDict= anotherDict.copy();\n"\ -"\n"\ -" for (var i = 0; i < dictKeys.len(); i += 1) {\n"\ -" newDict[dictKeys[i]]=dict[dictKeys[i]];\n"\ -" }\n"\ -" return newDict;\n"\ -"\n}"\ +"\n" \ +"def merge(dict, anotherDict) {\n" \ +" const dictKeys = dict.keys();\n" \ +" const newDict= anotherDict.copy();\n" \ +"\n" \ +" for (var i = 0; i < dictKeys.len(); i += 1) {\n" \ +" newDict[dictKeys[i]]=dict[dictKeys[i]];\n" \ +" }\n" \ +" return newDict;\n" \ +"}\n" \ diff --git a/src/vm/datatypes/result/result-source.h b/src/vm/datatypes/result/result-source.h index 7670fb94..30afe765 100644 --- a/src/vm/datatypes/result/result-source.h +++ b/src/vm/datatypes/result/result-source.h @@ -12,4 +12,12 @@ "\n" \ " return errorCallback(result.unwrapError());\n" \ "}\n" \ +"\n" \ +"def matchWrap(result, successCallback, errorCallback) {\n" \ +" if (result.success()) {\n" \ +" return Success(successCallback(result.unwrap()));\n" \ +" }\n" \ +"\n" \ +" return Error(errorCallback(result.unwrapError()));\n" \ +"}\n" \ diff --git a/src/vm/datatypes/result/result.du b/src/vm/datatypes/result/result.du index 0d0be909..89bc1a4e 100644 --- a/src/vm/datatypes/result/result.du +++ b/src/vm/datatypes/result/result.du @@ -11,4 +11,12 @@ def match(result, successCallback, errorCallback) { } return errorCallback(result.unwrapError()); +} + +def matchWrap(result, successCallback, errorCallback) { + if (result.success()) { + return Success(successCallback(result.unwrap())); + } + + return Error(errorCallback(result.unwrapError())); } \ No newline at end of file From 64ca5b7053f9974b02495d2b2c04faa3b6617319 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 19 Oct 2021 19:43:55 +0100 Subject: [PATCH 28/32] Add docs / tests for matchWrap --- docs/docs/error-handling.md | 20 ++++++++++++++++- tests/result/import.du | 3 ++- tests/result/matchWrap.du | 45 +++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/result/matchWrap.du diff --git a/docs/docs/error-handling.md b/docs/docs/error-handling.md index 4b097152..fdad53ef 100644 --- a/docs/docs/error-handling.md +++ b/docs/docs/error-handling.md @@ -104,4 +104,22 @@ var number = "number".toNumber().match( ); print(number); -``` \ No newline at end of file +``` + +### .matchWrap(func: success, func: error) + +`.matchWrap` is exactly the same as `.wrap` however, the value returned from either callback +function is implicitly wrapped back up into a Result object. This allows us to easily deal +with the error at a different call site and avoids the necessity for explicit wrapping. + +```cs +var response = HTTP.get("https://some/endpoint").matchWrap( + def (data) => JSON.parse(data).unwrap(), + def (error) => error +); + +print(response); // +``` + +In the above example we can handle the case that we need to do some data transformation, however, we +also need to ensure that a Result object is returned in case we hit the error callback. \ No newline at end of file diff --git a/tests/result/import.du b/tests/result/import.du index 043d5697..f4f21762 100644 --- a/tests/result/import.du +++ b/tests/result/import.du @@ -5,4 +5,5 @@ */ import "unwrap.du"; -import "match.du"; \ No newline at end of file +import "match.du"; +import "matchWrap.du"; \ No newline at end of file diff --git a/tests/result/matchWrap.du b/tests/result/matchWrap.du new file mode 100644 index 00000000..97a63a0f --- /dev/null +++ b/tests/result/matchWrap.du @@ -0,0 +1,45 @@ +/** +* matchWrap.du +* +* Testing Result.matchWrap() +* +*/ + +def success(result) { + return result; +} + +def error(err) { + return err; +} + +var result = Success(10).matchWrap(success, error); +assert(result.success()); +assert(result.unwrap() == 10); + +result = Error("Error!").matchWrap(success, error); +assert(!result.success()); +assert(result.unwrapError() == "Error!"); + + +assert( + Success([1, 2, 3]).matchWrap( + def (result) => result, + def (err) => err + ).unwrap() == [1, 2, 3] +); + +assert( + Error("test").matchWrap( + def (result) => result, + def (err) => err + ).unwrapError() == "test" +); + +var number = "10".toNumber().matchWrap(success, error); +assert(number.success()); +assert(number.unwrap() == 10); + +var number = "test".toNumber().matchWrap(success, error); +assert(!number.success()); +assert(number.unwrapError() == "Can not convert 'test' to number"); From 4a09fadf4b763f5fb2b86849c8a36e7ae1362633 Mon Sep 17 00:00:00 2001 From: Nikunj Prajapati Date: Fri, 22 Oct 2021 23:37:53 +0530 Subject: [PATCH 29/32] Added some test cases for maths operations --- tests/maths/maths.du | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/maths/maths.du b/tests/maths/maths.du index 2ce7cd3d..1d0af1e2 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -10,32 +10,59 @@ */ import Math; + +assert(Math.min(1) == 1); +assert(Math.min(1, 2) == 1); assert(Math.min(1, 2, 3) == 1); assert(Math.min([1, 2, 3]) == 1); -assert(Math.min([3, 2, 1]) == 1); +assert(Math.min(-1, -2, -3) == -3); +assert(Math.min([-1, -2, -3]) == -3); +assert(Math.min(0.1,0.2,0.3) == 0.1); +assert(Math.min(1,0.5,-1,0) == -1); + +assert(Math.max(1) == 1); +assert(Math.max(1,2) == 2); assert(Math.max(1, 2, 3) == 3); assert(Math.max([1, 2, 3]) == 3); -assert(Math.max([3, 2, 1]) == 3); +assert(Math.max(-1, -2, -3) == -1); +assert(Math.max([-1, -2, -3]) == -1); +assert(Math.max(0.1,0.2,0.3) == 0.3); +assert(Math.max(1,0.5,-1,0) == 1); + assert(Math.average(1, 2, 3) == 2); assert(Math.average([1, 2, 3]) == 2); +// assert(Math.average(1, 6, 1.2, -19, -98) == -21.76); + assert(Math.sum(1, 2, 3) == 6); assert(Math.sum([1, 2, 3]) == 6); +assert(Math.sum(1,6,1.2,-19,-98) == -108.8); + assert(Math.floor(17.999) == 17); +assert(Math.floor(-1.59) == -2); + assert(Math.round(17.5) == 18); + assert(Math.ceil(17.0001) == 18); + assert(Math.abs(18) == 18); assert(Math.abs(-18) == 18); + assert(Math.sqrt(25) == 5); assert(Math.sqrt(100) == 10); assert(Math.sqrt(20) == 4.47213595499958); assert(Math.sqrt(100_00) == 1_00); + assert(Math.sin(1) > 0.84); assert(Math.sin(1) < 0.845); + assert(Math.cos(0) == 1); + assert(Math.tan(1) > 1.5); assert(Math.tan(1) < 1.6); + assert(Math.gcd(32, 24, 12) == 4); assert(Math.gcd([32, 24, 12]) == 4); + assert(Math.lcm(32, 24, 12) == 96); assert(Math.lcm([32, 24, 12]) == 96); From df0bae79e1840af56d2017e1d0009636b261fb3b Mon Sep 17 00:00:00 2001 From: Nikunj Prajapati Date: Sun, 24 Oct 2021 11:21:21 +0530 Subject: [PATCH 30/32] Resolved changes --- tests/maths/maths.du | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/maths/maths.du b/tests/maths/maths.du index 1d0af1e2..f25871c3 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -31,7 +31,6 @@ assert(Math.max(1,0.5,-1,0) == 1); assert(Math.average(1, 2, 3) == 2); assert(Math.average([1, 2, 3]) == 2); -// assert(Math.average(1, 6, 1.2, -19, -98) == -21.76); assert(Math.sum(1, 2, 3) == 6); assert(Math.sum([1, 2, 3]) == 6); From cdd6417653cb524afc9fff5fce3a661f8621e872 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 24 Oct 2021 18:52:53 +0100 Subject: [PATCH 31/32] Bump version to 0.22.0 --- docs/_config.yml | 2 +- src/include/dictu_include.h | 2 +- src/vm/common.h | 3 --- src/vm/value.c | 13 ------------- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index 2b0df27e..7c3238b6 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -5,7 +5,7 @@ description: >- color_scheme: "dictu" # Custom theme logo: "/assets/images/dictu-logo/dictu-wordmark.svg" -version: "0.21.0" +version: "0.22.0" github_username: dictu-lang search_enabled: true diff --git a/src/include/dictu_include.h b/src/include/dictu_include.h index 4a51593d..bddbb8ae 100644 --- a/src/include/dictu_include.h +++ b/src/include/dictu_include.h @@ -4,7 +4,7 @@ #include #define DICTU_MAJOR_VERSION "0" -#define DICTU_MINOR_VERSION "21" +#define DICTU_MINOR_VERSION "22" #define DICTU_PATCH_VERSION "0" #define DICTU_STRING_VERSION "Dictu Version: " DICTU_MAJOR_VERSION "." DICTU_MINOR_VERSION "." DICTU_PATCH_VERSION "\n" diff --git a/src/vm/common.h b/src/vm/common.h index 86bfb239..32a3ceb1 100644 --- a/src/vm/common.h +++ b/src/vm/common.h @@ -44,7 +44,6 @@ return newResultError(vm, buf); \ #define IS_DIR_SEPARATOR(c) (c == DIR_SEPARATOR) #endif -#define NAN_TAGGING #define DEBUG_PRINT_CODE #define DEBUG_TRACE_EXECUTION #define DEBUG_TRACE_GC @@ -64,6 +63,4 @@ return newResultError(vm, buf); \ #define UINT8_COUNT (UINT8_MAX + 1) -// typedef struct _vm DictuVM; - #endif diff --git a/src/vm/value.c b/src/vm/value.c index a43ab0c9..b0a18f8b 100644 --- a/src/vm/value.c +++ b/src/vm/value.c @@ -542,8 +542,6 @@ static bool setComparison(Value a, Value b) { } bool valuesEqual(Value a, Value b) { -#ifdef NAN_TAGGING - if (IS_OBJ(a) && IS_OBJ(b)) { if (AS_OBJ(a)->type != AS_OBJ(b)->type) return false; @@ -567,15 +565,4 @@ bool valuesEqual(Value a, Value b) { } return a == b; -#else - if (a.type != b.type) return false; - - switch (a.type) { - case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b); - case VAL_NIL: return true; - case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b); - case VAL_OBJ: - return AS_OBJ(a) == AS_OBJ(b); - } -#endif } \ No newline at end of file From b0df3b92d7c74e614d5ed55a1e3e6d9b3fcc0307 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 24 Oct 2021 19:09:01 +0100 Subject: [PATCH 32/32] Update dict.merge --- docs/docs/collections/dictionaries.md | 6 ++++-- src/vm/datatypes/dicts/dict-source.h | 10 +++++----- src/vm/datatypes/dicts/dict.du | 12 ++++++------ tests/dicts/merge.du | 14 +++++++------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/docs/collections/dictionaries.md b/docs/docs/collections/dictionaries.md index d4fbe5bf..e2887751 100644 --- a/docs/docs/collections/dictionaries.md +++ b/docs/docs/collections/dictionaries.md @@ -147,7 +147,9 @@ myDict.forEach(def (key, value) => { ### dict.merge(anotherDict) -To merge with another dictionary. This operation will produce a new object. If another dictionary contains a key that exists in invoking one, the invoking dictionary's key will be taken. +To merge two dicts together we use `.merge`. This operation will take a shallow copy of the dict the `.merge` method +was called on and add any items from the dictionary passed into the method. If there are keys that exist in both dictionaries +the value from the passed in dictionary is the one that will be used. ```cs const dictOne = {"key": 1, "key1": 2, "key2": 3}; @@ -155,5 +157,5 @@ const dictTwo = {"key3": 4,"key1":0}; const mergedDict = dictOne.merge(dictTwo); -mergedDict; //{"key2": 3, "key": 1, "key3": 4, "key1": 2} +mergedDict; //{"key2": 3, "key": 1, "key3": 4, "key1": 0} ``` \ No newline at end of file diff --git a/src/vm/datatypes/dicts/dict-source.h b/src/vm/datatypes/dicts/dict-source.h index 91a4dbd9..0e6a6732 100644 --- a/src/vm/datatypes/dicts/dict-source.h +++ b/src/vm/datatypes/dicts/dict-source.h @@ -13,12 +13,12 @@ "}\n" \ "\n" \ "def merge(dict, anotherDict) {\n" \ -" const dictKeys = dict.keys();\n" \ -" const newDict= anotherDict.copy();\n" \ +" const newDict = dict.copy();\n" \ +"\n" \ +" forEach(anotherDict, def (key, value) => {\n" \ +" newDict[key] = value;\n" \ +" });\n" \ "\n" \ -" for (var i = 0; i < dictKeys.len(); i += 1) {\n" \ -" newDict[dictKeys[i]]=dict[dictKeys[i]];\n" \ -" }\n" \ " return newDict;\n" \ "}\n" \ diff --git a/src/vm/datatypes/dicts/dict.du b/src/vm/datatypes/dicts/dict.du index 112a50d5..8fc79237 100644 --- a/src/vm/datatypes/dicts/dict.du +++ b/src/vm/datatypes/dicts/dict.du @@ -13,11 +13,11 @@ def forEach(dict, func) { } def merge(dict, anotherDict) { - const dictKeys = dict.keys(); - const newDict= anotherDict.copy(); + const newDict = dict.copy(); + + forEach(anotherDict, def (key, value) => { + newDict[key] = value; + }); - for (var i = 0; i < dictKeys.len(); i += 1) { - newDict[dictKeys[i]]=dict[dictKeys[i]]; - } return newDict; -} +} \ No newline at end of file diff --git a/tests/dicts/merge.du b/tests/dicts/merge.du index c3e51c07..e1784e49 100644 --- a/tests/dicts/merge.du +++ b/tests/dicts/merge.du @@ -3,13 +3,13 @@ * * Testing the dict.merge() method * - * .merge() To merge with another dictionary. This operation will produce a new object. If another dictionary contains a key that exists in invoking one, the invoking dictionary's key will be taken.. + * .merge() merges two dictionaries together. */ const dictOne = {"key": 1, "key1": 2, "key2": 3}; -const dictTwo = {"key3": 4,"key1":0}; +const dictTwo = {"key3": 4,"key1": 0}; -const mergedDict=dictOne.merge(dictTwo); +const mergedDict = dictOne.merge(dictTwo); assert(mergedDict.keys().len() == 4); @@ -18,7 +18,7 @@ assert(mergedDict.exists("key1")); assert(mergedDict.exists("key2")); assert(mergedDict.exists("key3")); -assert(mergedDict["key"] ==1); -assert(mergedDict["key1"]==2); -assert(mergedDict["key2"]==3); -assert(mergedDict["key3"]==4); +assert(mergedDict["key"] == 1); +assert(mergedDict["key1"] == 0); +assert(mergedDict["key2"] == 3); +assert(mergedDict["key3"] == 4);