From 71eb07b52f629da4779fa4dd20db6d7ba9525dfc Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 6 Jul 2021 19:04:40 +0100 Subject: [PATCH 01/52] Begin work on annotations --- src/vm/compiler.c | 3 +++ src/vm/compiler.h | 1 + src/vm/memory.c | 3 +++ src/vm/object.c | 1 + src/vm/object.h | 1 + src/vm/scanner.c | 2 ++ src/vm/scanner.h | 2 +- src/vm/vm.c | 13 +++++++++++++ 8 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 70741f7a..0f0a6641 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -1412,6 +1412,7 @@ ParseRule rules[] = { {NULL, binary, PREC_FACTOR}, // TOKEN_STAR {NULL, binary, PREC_INDICES}, // TOKEN_STAR_STAR {NULL, binary, PREC_FACTOR}, // TOKEN_PERCENT + {NULL, NULL, PREC_NONE}, // TOKEN_AT {NULL, binary, PREC_BITWISE_AND}, // TOKEN_AMPERSAND {NULL, binary, PREC_BITWISE_XOR}, // TOKEN_CARET {NULL, binary, PREC_BITWISE_OR}, // TOKEN_PIPE @@ -1577,6 +1578,7 @@ static void setupClassCompiler(Compiler *compiler, ClassCompiler *classCompiler, classCompiler->enclosing = compiler->class; classCompiler->staticMethod = false; classCompiler->abstractClass = abstract; + classCompiler->annotations = NULL; initTable(&classCompiler->privateVariables); compiler->class = classCompiler; } @@ -2454,6 +2456,7 @@ void grayCompilerRoots(DictuVM *vm) { ClassCompiler *classCompiler = vm->compiler->class; while (classCompiler != NULL) { + grayObject(vm, (Obj *) classCompiler->annotations); grayTable(vm, &classCompiler->privateVariables); classCompiler = classCompiler->enclosing; } diff --git a/src/vm/compiler.h b/src/vm/compiler.h index 0e582fbc..3cf95dfb 100644 --- a/src/vm/compiler.h +++ b/src/vm/compiler.h @@ -63,6 +63,7 @@ typedef struct ClassCompiler { bool staticMethod; bool abstractClass; Table privateVariables; + ObjDict *annotations; } ClassCompiler; typedef struct Loop { diff --git a/src/vm/memory.c b/src/vm/memory.c index 1d2204af..7895f46a 100644 --- a/src/vm/memory.c +++ b/src/vm/memory.c @@ -93,6 +93,7 @@ static void blackenObject(DictuVM *vm, Obj *object) { case OBJ_BOUND_METHOD: { ObjBoundMethod *bound = (ObjBoundMethod *) object; grayValue(vm, bound->receiver); + grayValue(vm, bound->annotations); grayObject(vm, (Obj *) bound->method); break; } @@ -194,6 +195,8 @@ void freeObject(DictuVM *vm, Obj *object) { } case OBJ_BOUND_METHOD: { + // if (!IS_NIL()) + FREE(vm, ObjBoundMethod, object); break; } diff --git a/src/vm/object.c b/src/vm/object.c index 033f10fc..6c8f0821 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -56,6 +56,7 @@ ObjBoundMethod *newBoundMethod(DictuVM *vm, Value receiver, ObjClosure *method) bound->receiver = receiver; bound->method = method; + bound->annotations = NIL_VAL; return bound; } diff --git a/src/vm/object.h b/src/vm/object.h index 015f5972..a25b16a6 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -241,6 +241,7 @@ typedef struct { Obj obj; Value receiver; ObjClosure *method; + Value annotations; } ObjBoundMethod; ObjModule *newModule(DictuVM *vm, ObjString *name); diff --git a/src/vm/scanner.c b/src/vm/scanner.c index a70bc9c2..90db8691 100644 --- a/src/vm/scanner.c +++ b/src/vm/scanner.c @@ -359,6 +359,8 @@ Token scanToken(Scanner *scanner) { if (isDigit(c)) return hexNumber(scanner); switch (c) { + case '@': + return makeToken(scanner, TOKEN_AT); case '(': return makeToken(scanner, TOKEN_LEFT_PAREN); case ')': diff --git a/src/vm/scanner.h b/src/vm/scanner.h index 33a7769f..206d7e7c 100644 --- a/src/vm/scanner.h +++ b/src/vm/scanner.h @@ -15,7 +15,7 @@ typedef enum { TOKEN_SEMICOLON, TOKEN_COLON, TOKEN_SLASH, TOKEN_STAR, TOKEN_STAR_STAR, - TOKEN_PERCENT, + TOKEN_PERCENT, TOKEN_AT, // Bitwise TOKEN_AMPERSAND, TOKEN_CARET, TOKEN_PIPE, diff --git a/src/vm/vm.c b/src/vm/vm.c index 0beb497d..4cbc629e 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1046,6 +1046,19 @@ static DictuInterpretResult run(DictuVM *vm) { RUNTIME_ERROR("'%s' enum has no property: '%s'.", enumObj->name->chars, name->chars); } + case OBJ_BOUND_METHOD: { + ObjBoundMethod *methodObj = AS_BOUND_METHOD(receiver); + ObjString *name = READ_STRING(); + + if (strcmp(name->chars, "annotations") == 0) { + pop(vm); // Method + push(vm, methodObj->annotations); + DISPATCH(); + } + + RUNTIME_ERROR("'%s' bound method has no property: '%s'.", methodObj->method->function->name->chars, name->chars); + } + default: { RUNTIME_ERROR_TYPE("'%s' type has no properties", 0); } From 6868c1f14914e6542134a11819c67013d309f1e1 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 6 Jul 2021 22:53:03 +0100 Subject: [PATCH 02/52] Get annotations working on classes --- src/vm/compiler.c | 36 ++++++++++++++++++++++++++++++++++-- src/vm/compiler.h | 1 + src/vm/debug.c | 2 ++ src/vm/memory.c | 1 + src/vm/object.c | 1 + src/vm/object.h | 1 + src/vm/opcodes.h | 1 + src/vm/vm.c | 15 +++++++++++++++ 8 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 0f0a6641..2ccc962a 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -157,6 +157,7 @@ static void initCompiler(Parser *parser, Compiler *compiler, Compiler *parent, F compiler->class = NULL; compiler->loop = NULL; compiler->withBlock = false; + compiler->annotations = NULL; if (parent != NULL) { compiler->class = parent->class; @@ -1586,6 +1587,12 @@ static void setupClassCompiler(Compiler *compiler, ClassCompiler *classCompiler, static void endClassCompiler(Compiler *compiler, ClassCompiler *classCompiler) { freeTable(compiler->parser->vm, &classCompiler->privateVariables); compiler->class = compiler->class->enclosing; + + if (compiler->annotations != NULL) { + int importConstant = makeConstant(compiler, OBJ_VAL(compiler->annotations)); + emitBytes(compiler, OP_DEFINE_CLASS_ANNOTATIONS, importConstant); + compiler->annotations = NULL; + } } static void parseClassBody(Compiler *compiler) { @@ -1622,6 +1629,20 @@ static void parseClassBody(Compiler *compiler) { } } +static void parseAttributes(Compiler *compiler) { + DictuVM *vm = compiler->parser->vm; + compiler->annotations = newDict(vm); + + do { + consume(compiler, TOKEN_IDENTIFIER, "Expected annotation identifier"); + Value annotationName = OBJ_VAL(copyString(vm, compiler->parser->previous.start, + compiler->parser->previous.length)); + push(vm, annotationName); + dictSet(vm, compiler->annotations, annotationName, NIL_VAL); + pop(vm); + } while (match(compiler, TOKEN_AT)); +} + static void classDeclaration(Compiler *compiler) { consume(compiler, TOKEN_IDENTIFIER, "Expect class name."); uint8_t nameConstant = identifierConstant(compiler, &compiler->parser->previous); @@ -1659,8 +1680,8 @@ static void classDeclaration(Compiler *compiler) { emitByte(compiler, OP_END_CLASS); } - defineVariable(compiler, nameConstant, false); endClassCompiler(compiler, &classCompiler); + defineVariable(compiler, nameConstant, false); } static void abstractClassDeclaration(Compiler *compiler) { @@ -2332,7 +2353,15 @@ static void synchronize(Parser *parser) { static void declaration(Compiler *compiler) { if (match(compiler, TOKEN_CLASS)) { classDeclaration(compiler); - } else if (match(compiler, TOKEN_TRAIT)) { + if (compiler->parser->panicMode) synchronize(compiler->parser); + return; + } + + if (compiler->annotations != NULL) { + errorAtCurrent(compiler->parser, "Annotations can only be applied to classes"); + } + + if (match(compiler, TOKEN_TRAIT)) { traitDeclaration(compiler); } else if (match(compiler, TOKEN_ABSTRACT)) { abstractClassDeclaration(compiler); @@ -2344,6 +2373,8 @@ static void declaration(Compiler *compiler) { varDeclaration(compiler, true); } else if (match(compiler, TOKEN_ENUM)) { enumDeclaration(compiler); + } else if (match(compiler, TOKEN_AT)) { + parseAttributes(compiler); } else { statement(compiler); } @@ -2461,6 +2492,7 @@ void grayCompilerRoots(DictuVM *vm) { classCompiler = classCompiler->enclosing; } + grayObject(vm, (Obj *) compiler->annotations); grayObject(vm, (Obj *) compiler->function); grayTable(vm, &compiler->stringConstants); compiler = compiler->enclosing; diff --git a/src/vm/compiler.h b/src/vm/compiler.h index 3cf95dfb..e78329f6 100644 --- a/src/vm/compiler.h +++ b/src/vm/compiler.h @@ -102,6 +102,7 @@ typedef struct Compiler { int scopeDepth; bool withBlock; + ObjDict *annotations; } Compiler; typedef void (*ParsePrefixFn)(Compiler *compiler, bool canAssign); diff --git a/src/vm/debug.c b/src/vm/debug.c index b034298d..8c68184d 100644 --- a/src/vm/debug.c +++ b/src/vm/debug.c @@ -268,6 +268,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_END_CLASS", offset); case OP_METHOD: return constantInstruction("OP_METHOD", chunk, offset); + case OP_DEFINE_CLASS_ANNOTATIONS: + return constantInstruction("OP_DEFINE_CLASS_ANNOTATIONS", chunk, offset); case OP_ENUM: return constantInstruction("OP_ENUM", chunk, offset); case OP_SET_ENUM_VALUE: diff --git a/src/vm/memory.c b/src/vm/memory.c index 7895f46a..b2370c32 100644 --- a/src/vm/memory.c +++ b/src/vm/memory.c @@ -102,6 +102,7 @@ static void blackenObject(DictuVM *vm, Obj *object) { ObjClass *klass = (ObjClass *) object; grayObject(vm, (Obj *) klass->name); grayObject(vm, (Obj *) klass->superclass); + grayObject(vm, (Obj *) klass->annotations); grayTable(vm, &klass->publicMethods); grayTable(vm, &klass->privateMethods); grayTable(vm, &klass->abstractMethods); diff --git a/src/vm/object.c b/src/vm/object.c index 6c8f0821..10d1cca9 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -69,6 +69,7 @@ ObjClass *newClass(DictuVM *vm, ObjString *name, ObjClass *superclass, ClassType initTable(&klass->privateMethods); initTable(&klass->publicMethods); initTable(&klass->publicProperties); + klass->annotations = NULL; return klass; } diff --git a/src/vm/object.h b/src/vm/object.h index a25b16a6..15f1e748 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -221,6 +221,7 @@ typedef struct sObjClass { Table privateMethods; Table abstractMethods; Table publicProperties; + ObjDict *annotations; ClassType type; } ObjClass; diff --git a/src/vm/opcodes.h b/src/vm/opcodes.h index 7bd4c986..bacde7bc 100644 --- a/src/vm/opcodes.h +++ b/src/vm/opcodes.h @@ -54,6 +54,7 @@ OPCODE(RETURN) OPCODE(EMPTY) OPCODE(CLASS) OPCODE(SUBCLASS) +OPCODE(DEFINE_CLASS_ANNOTATIONS) OPCODE(END_CLASS) OPCODE(METHOD) OPCODE(ENUM) diff --git a/src/vm/vm.c b/src/vm/vm.c index 4cbc629e..a2df420c 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1029,6 +1029,12 @@ static DictuInterpretResult run(DictuVM *vm) { klass = klass->superclass; } + if (strcmp(name->chars, "annotations") == 0) { + pop(vm); // Klass + push(vm, OBJ_VAL(klassStore->annotations)); + DISPATCH(); + } + RUNTIME_ERROR("'%s' class has no property: '%s'.", klassStore->name->chars, name->chars); } @@ -1969,6 +1975,15 @@ static DictuInterpretResult run(DictuVM *vm) { DISPATCH(); } + CASE_CODE(DEFINE_CLASS_ANNOTATIONS): { + ObjDict *dict = AS_DICT(READ_CONSTANT()); + ObjClass *klass = AS_CLASS(peek(vm, 0)); + + klass->annotations = dict; + + DISPATCH(); + } + CASE_CODE(END_CLASS): { ObjClass *klass = AS_CLASS(peek(vm, 0)); From 54cd97a24edbb9ca901b574ad1ee85e2cd8440a8 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 8 Aug 2021 22:52:58 +0100 Subject: [PATCH 03/52] Allow annotations to contain a literal value and add some tests --- src/vm/compiler.c | 115 ++++++++++++++++++++++++----------- src/vm/vm.c | 13 ---- tests/classes/annotations.du | 25 ++++++++ tests/classes/import.du | 3 +- 4 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 tests/classes/annotations.du diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 2ccc962a..3f21a925 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -920,7 +920,7 @@ static void grouping(Compiler *compiler, bool canAssign) { consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after expression."); } -static void number(Compiler *compiler, bool canAssign) { +static Value parseNumber(Compiler *compiler, bool canAssign) { UNUSED(canAssign); // We allocate the whole range for the worst case. @@ -942,10 +942,14 @@ static void number(Compiler *compiler, bool canAssign) { // Parse the string. double value = strtod(buffer, NULL); - emitConstant(compiler, NUMBER_VAL(value)); - // Free the malloc'd buffer. FREE_ARRAY(compiler->parser->vm, char, buffer, compiler->parser->previous.length + 1); + + return NUMBER_VAL(value); +} + +static void number(Compiler *compiler, bool canAssign) { + emitConstant(compiler, parseNumber(compiler, canAssign)); } static void or_(Compiler *compiler, Token previousToken, bool canAssign) { @@ -976,7 +980,7 @@ static void or_(Compiler *compiler, Token previousToken, bool canAssign) { patchJump(compiler, endJump); } -int parseString(char *string, int length) { +int parseEscapeSequences(char *string, int length) { for (int i = 0; i < length - 1; i++) { if (string[i] == '\\') { switch (string[i + 1]) { @@ -1030,7 +1034,7 @@ static void rString(Compiler *compiler, bool canAssign) { consume(compiler, TOKEN_STRING, "Expected string after r delimiter"); } -static void string(Compiler *compiler, bool canAssign) { +static Value parseString(Compiler *compiler, bool canAssign) { UNUSED(canAssign); Parser *parser = compiler->parser; @@ -1039,7 +1043,7 @@ static void string(Compiler *compiler, bool canAssign) { char *string = ALLOCATE(parser->vm, char, stringLength + 1); memcpy(string, parser->previous.start + 1, stringLength); - int length = parseString(string, stringLength); + int length = parseEscapeSequences(string, stringLength); // If there were escape chars and the string shrank, resize the buffer if (length != stringLength) { @@ -1047,7 +1051,11 @@ static void string(Compiler *compiler, bool canAssign) { } string[length] = '\0'; - emitConstant(compiler, OBJ_VAL(takeString(parser->vm, string, length))); + return OBJ_VAL(takeString(parser->vm, string, length)); +} + +static void string(Compiler *compiler, bool canAssign) { + emitConstant(compiler, parseString(compiler, canAssign)); } static void list(Compiler *compiler, bool canAssign) { @@ -1595,6 +1603,55 @@ static void endClassCompiler(Compiler *compiler, ClassCompiler *classCompiler) { } } +static bool checkLiteralToken(Compiler *compiler) { + return check(compiler, TOKEN_STRING) || check(compiler, TOKEN_NUMBER) || + check(compiler, TOKEN_TRUE) || check(compiler, TOKEN_FALSE) || check(compiler, TOKEN_NIL); +} + +static void parseClassAnnotations(Compiler *compiler) { + DictuVM *vm = compiler->parser->vm; + compiler->annotations = newDict(vm); + + do { + consume(compiler, TOKEN_IDENTIFIER, "Expected annotation identifier"); + Value annotationName = OBJ_VAL(copyString(vm, compiler->parser->previous.start, + compiler->parser->previous.length)); + push(vm, annotationName); + + if (match(compiler, TOKEN_LEFT_PAREN)) { + if (!checkLiteralToken(compiler)) { + errorAtCurrent(compiler->parser, + "Annotations can only have literal values of type string, bool, number or nil."); + return; + } + + if (match(compiler, TOKEN_STRING)) { + Value string = parseString(compiler, false); + push(vm, string); + dictSet(vm, compiler->annotations, annotationName, string); + pop(vm); + } else if (match(compiler, TOKEN_NUMBER)) { + Value number = parseNumber(compiler, false); + dictSet(vm, compiler->annotations, annotationName, number); + } else if (match(compiler, TOKEN_TRUE)) { + dictSet(vm, compiler->annotations, annotationName, TRUE_VAL); + } else if (match(compiler, TOKEN_FALSE)) { + dictSet(vm, compiler->annotations, annotationName, FALSE_VAL); + } else if (match(compiler, TOKEN_NIL)) { + dictSet(vm, compiler->annotations, annotationName, NIL_VAL); + } else { + dictSet(vm, compiler->annotations, annotationName, NIL_VAL); + } + + consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after annotation value."); + } else { + dictSet(vm, compiler->annotations, annotationName, NIL_VAL); + } + + pop(vm); + } while (match(compiler, TOKEN_AT)); +} + static void parseClassBody(Compiler *compiler) { while (!check(compiler, TOKEN_RIGHT_BRACE) && !check(compiler, TOKEN_EOF)) { if (match(compiler, TOKEN_USE)) { @@ -1608,41 +1665,29 @@ static void parseClassBody(Compiler *compiler) { emitBytes(compiler, OP_SET_CLASS_VAR, name); consume(compiler, TOKEN_SEMICOLON, "Expect ';' after variable declaration."); - } else if (match(compiler, TOKEN_PRIVATE)) { - if (match(compiler, TOKEN_IDENTIFIER)) { - if (check(compiler, TOKEN_SEMICOLON)) { - uint8_t name = identifierConstant(compiler, &compiler->parser->previous); - consume(compiler, TOKEN_SEMICOLON, "Expect ';' after private variable declaration."); - tableSet(compiler->parser->vm, &compiler->class->privateVariables, - AS_STRING(currentChunk(compiler)->constants.values[name]), EMPTY_VAL); + } else { + if (match(compiler, TOKEN_PRIVATE)) { + if (match(compiler, TOKEN_IDENTIFIER)) { + if (check(compiler, TOKEN_SEMICOLON)) { + uint8_t name = identifierConstant(compiler, &compiler->parser->previous); + consume(compiler, TOKEN_SEMICOLON, "Expect ';' after private variable declaration."); + tableSet(compiler->parser->vm, &compiler->class->privateVariables, + AS_STRING(currentChunk(compiler)->constants.values[name]), EMPTY_VAL); + continue; + } + + method(compiler, true, &compiler->parser->previous); continue; } - method(compiler, true, &compiler->parser->previous); - continue; + method(compiler, true, NULL); + } else { + method(compiler, false, NULL); } - - method(compiler, true, NULL); - } else { - method(compiler, false, NULL); } } } -static void parseAttributes(Compiler *compiler) { - DictuVM *vm = compiler->parser->vm; - compiler->annotations = newDict(vm); - - do { - consume(compiler, TOKEN_IDENTIFIER, "Expected annotation identifier"); - Value annotationName = OBJ_VAL(copyString(vm, compiler->parser->previous.start, - compiler->parser->previous.length)); - push(vm, annotationName); - dictSet(vm, compiler->annotations, annotationName, NIL_VAL); - pop(vm); - } while (match(compiler, TOKEN_AT)); -} - static void classDeclaration(Compiler *compiler) { consume(compiler, TOKEN_IDENTIFIER, "Expect class name."); uint8_t nameConstant = identifierConstant(compiler, &compiler->parser->previous); @@ -2374,7 +2419,7 @@ static void declaration(Compiler *compiler) { } else if (match(compiler, TOKEN_ENUM)) { enumDeclaration(compiler); } else if (match(compiler, TOKEN_AT)) { - parseAttributes(compiler); + parseClassAnnotations(compiler); } else { statement(compiler); } diff --git a/src/vm/vm.c b/src/vm/vm.c index a2df420c..a6a8462d 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1052,19 +1052,6 @@ static DictuInterpretResult run(DictuVM *vm) { RUNTIME_ERROR("'%s' enum has no property: '%s'.", enumObj->name->chars, name->chars); } - case OBJ_BOUND_METHOD: { - ObjBoundMethod *methodObj = AS_BOUND_METHOD(receiver); - ObjString *name = READ_STRING(); - - if (strcmp(name->chars, "annotations") == 0) { - pop(vm); // Method - push(vm, methodObj->annotations); - DISPATCH(); - } - - RUNTIME_ERROR("'%s' bound method has no property: '%s'.", methodObj->method->function->name->chars, name->chars); - } - default: { RUNTIME_ERROR_TYPE("'%s' type has no properties", 0); } diff --git a/tests/classes/annotations.du b/tests/classes/annotations.du new file mode 100644 index 00000000..00cf3d16 --- /dev/null +++ b/tests/classes/annotations.du @@ -0,0 +1,25 @@ +/** + * annotations.du + * + * Testing class annotations + */ + +@EmptyAnnotation +@TrueAnnotation(true) +@FalseAnnotation(false) +@NumberAnnotation(10) +@DecimalNumberAnnotation(10.5) +@NilAnnotation(nil) +class Test { + +} + +assert(Test.annotations.len() == 6); +assert(Test.annotations['EmptyAnnotation'] == nil); +assert(Test.annotations['TrueAnnotation'] == true); +assert(Test.annotations['FalseAnnotation'] == false); +assert(Test.annotations['NumberAnnotation'] == 10); +assert(Test.annotations['DecimalNumberAnnotation'] == 10.5); +assert(Test.annotations['NilAnnotation'] == nil); + + diff --git a/tests/classes/import.du b/tests/classes/import.du index 7149b6ab..f53ffb62 100644 --- a/tests/classes/import.du +++ b/tests/classes/import.du @@ -17,4 +17,5 @@ import "parameters.du"; import "isInstance.du"; import "constructor.du"; import "optionalChaining.du"; -import "private.du"; \ No newline at end of file +import "private.du"; +import "annotations.du"; \ No newline at end of file From 86c0e2f92b84ae007de498d948bd81a9afe89654 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 8 Aug 2021 22:56:34 +0100 Subject: [PATCH 04/52] Handle the case when no annotations are added but the attribute is accessed --- src/vm/vm.c | 2 +- tests/classes/annotations.du | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vm/vm.c b/src/vm/vm.c index a6a8462d..9f81b9fa 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -1031,7 +1031,7 @@ static DictuInterpretResult run(DictuVM *vm) { if (strcmp(name->chars, "annotations") == 0) { pop(vm); // Klass - push(vm, OBJ_VAL(klassStore->annotations)); + push(vm, klassStore->annotations == NULL ? NIL_VAL : OBJ_VAL(klassStore->annotations)); DISPATCH(); } diff --git a/tests/classes/annotations.du b/tests/classes/annotations.du index 00cf3d16..dcc32f47 100644 --- a/tests/classes/annotations.du +++ b/tests/classes/annotations.du @@ -4,6 +4,10 @@ * Testing class annotations */ +class NoAnnotations {} + +assert(NoAnnotations.annotations == nil); + @EmptyAnnotation @TrueAnnotation(true) @FalseAnnotation(false) From 63080df50cbf29b120951178622271c8c991e439 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 11 Aug 2021 17:12:23 +0100 Subject: [PATCH 05/52] Add annotation documentation --- docs/docs/classes.md | 45 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/docs/classes.md b/docs/docs/classes.md index ab5d741d..89f51b72 100644 --- a/docs/docs/classes.md +++ b/docs/docs/classes.md @@ -561,4 +561,47 @@ var anotherTestObj = AnotherTest(); testObj.isInstance(AnotherTest); // false anotherTestObj.isInstance(AnotherTest); // true anotherTestObj.isInstance(Test); // true -``` \ No newline at end of file +``` + +## Annotations + +Annotations are metadata that are applied to classes that by themselves have no impact. +They, however, can provide user defined changes at runtime to given classes. + +```cs +@Annotation +class AnnotatedClass { + +} +``` + +Annotations are accessed via the `.annotations` property available on all classes. If annotations +are preset a dictionary is returned, otherwise the `.annotations` property is `nil`. + +```cs +print(AnnotatedClass.annotations); // {"Annotation": nil} +``` + +Annotations can also be supplied a value, however, the value must be of type: nil, boolean, number or string. + +``` +@Annotation("Some extra value!") +class AnnotatedClass { + +} + +print(AnnotatedClass.annotations); // {"Annotation": "Some extra value!"} +``` + +Multiple annotations can be supplied to classes. + +```cs +@Annotation +@AnotherAnnotation(10) +@SomeOtherAnnotation +class AnnotatedClass { + +} +``` + +**Note**: Annotations are not available on methods. \ No newline at end of file From be2fd3b580773cd28aded5ce6dd8246e84b84abe Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 11 Aug 2021 17:15:29 +0100 Subject: [PATCH 06/52] Remove stray comment and mentions of annotations on methods --- src/vm/memory.c | 3 --- src/vm/object.c | 1 - src/vm/object.h | 1 - 3 files changed, 5 deletions(-) diff --git a/src/vm/memory.c b/src/vm/memory.c index b2370c32..81d47465 100644 --- a/src/vm/memory.c +++ b/src/vm/memory.c @@ -93,7 +93,6 @@ static void blackenObject(DictuVM *vm, Obj *object) { case OBJ_BOUND_METHOD: { ObjBoundMethod *bound = (ObjBoundMethod *) object; grayValue(vm, bound->receiver); - grayValue(vm, bound->annotations); grayObject(vm, (Obj *) bound->method); break; } @@ -196,8 +195,6 @@ void freeObject(DictuVM *vm, Obj *object) { } case OBJ_BOUND_METHOD: { - // if (!IS_NIL()) - FREE(vm, ObjBoundMethod, object); break; } diff --git a/src/vm/object.c b/src/vm/object.c index 10d1cca9..a0000ec2 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -56,7 +56,6 @@ ObjBoundMethod *newBoundMethod(DictuVM *vm, Value receiver, ObjClosure *method) bound->receiver = receiver; bound->method = method; - bound->annotations = NIL_VAL; return bound; } diff --git a/src/vm/object.h b/src/vm/object.h index 15f1e748..885d8723 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -242,7 +242,6 @@ typedef struct { Obj obj; Value receiver; ObjClosure *method; - Value annotations; } ObjBoundMethod; ObjModule *newModule(DictuVM *vm, ObjString *name); From bb53ed37ccf0ca6396d4607939b2de3d640d3f1d Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 11 Aug 2021 22:47:31 +0100 Subject: [PATCH 07/52] Ability to print to stderr --- src/vm/natives.c | 18 ++++++++++++++++++ src/vm/value.c | 6 ++++++ src/vm/value.h | 2 ++ 3 files changed, 26 insertions(+) diff --git a/src/vm/natives.c b/src/vm/natives.c index 925064a9..eb402575 100644 --- a/src/vm/natives.c +++ b/src/vm/natives.c @@ -98,6 +98,22 @@ static Value printNative(DictuVM *vm, int argCount, Value *args) { return NIL_VAL; } +static Value printErrorNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(vm); + + if (argCount == 0) { + fprintf(stderr, "\n"); + return NIL_VAL; + } + + for (int i = 0; i < argCount; ++i) { + printValueError(args[i]); + fprintf(stderr, "\n"); + } + + return NIL_VAL; +} + static Value assertNative(DictuVM *vm, int argCount, Value *args) { if (argCount != 1) { runtimeError(vm, "assert() takes 1 argument (%d given)", argCount); @@ -170,6 +186,7 @@ void defineAllNatives(DictuVM *vm) { "type", "set", "print", + "printError", "assert", "isDefined", "Success", @@ -181,6 +198,7 @@ void defineAllNatives(DictuVM *vm) { typeNative, setNative, printNative, + printErrorNative, assertNative, isDefinedNative, generateSuccessResult, diff --git a/src/vm/value.c b/src/vm/value.c index 039f17ab..a43ab0c9 100644 --- a/src/vm/value.c +++ b/src/vm/value.c @@ -455,6 +455,12 @@ void printValue(Value value) { free(output); } +void printValueError(Value value) { + char *output = valueToString(value); + fprintf(stderr, "%s", output); + free(output); +} + static bool listComparison(Value a, Value b) { ObjList *list = AS_LIST(a); ObjList *listB = AS_LIST(b); diff --git a/src/vm/value.h b/src/vm/value.h index 943c46de..d4d8a736 100644 --- a/src/vm/value.h +++ b/src/vm/value.h @@ -107,4 +107,6 @@ char *valueTypeToString(DictuVM *vm, Value value, int *length); void printValue(Value value); +void printValueError(Value value); + #endif From 6f1d92c3d6cf4297ca2ffaef86b0c0b483a915fa Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 13 Aug 2021 19:29:31 +0100 Subject: [PATCH 08/52] Add printError documentation --- docs/docs/built-ins.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/docs/built-ins.md b/docs/docs/built-ins.md index 5c85fb10..95009a7f 100644 --- a/docs/docs/built-ins.md +++ b/docs/docs/built-ins.md @@ -35,6 +35,16 @@ print("test"); // "test" print(10, "test", nil, true); // 10, "test", nil, true ``` +### printError(...values...) + +Prints a given list of values to stderr. + +```cs +printError(10); // 10 +printError("test"); // "test" +printError(10, "test", nil, true); // 10, "test", nil, true +``` + ### input(string: prompt -> optional) Gathers user input from stdin and returns the value as a string. `input()` has an optional prompt which will be shown to From f7b6652e72a2dd47189d293792dec5e27848ab9f Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 27 Aug 2021 20:29:07 +0100 Subject: [PATCH 09/52] Remove quotes around string when converting string object to cstring --- src/vm/object.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vm/object.c b/src/vm/object.c index 89394456..cfc9ba45 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -617,8 +617,9 @@ char *objectToString(Value value) { case OBJ_STRING: { ObjString *stringObj = AS_STRING(value); - char *string = malloc(sizeof(char) * stringObj->length + 3); - snprintf(string, stringObj->length + 3, "'%s'", stringObj->chars); + char *string = malloc(sizeof(char) * stringObj->length + 1); + memcpy(string, stringObj->chars, stringObj->length); + string[stringObj->length] = '\0'; return string; } From 0e6ee99ed4931b9d5d6223114d687a7d4bd6c4f7 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Thu, 30 Sep 2021 22:51:06 -0400 Subject: [PATCH 10/52] Update fibonacci example This update shows off a couple additional (more efficient) ways to compute the n-th fibonacci number. --- examples/fibonacci.du | 27 +++++++++++++++++++++++++++ examples/recursiveFib.du | 9 --------- 2 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 examples/fibonacci.du delete mode 100644 examples/recursiveFib.du diff --git a/examples/fibonacci.du b/examples/fibonacci.du new file mode 100644 index 00000000..f31f5071 --- /dev/null +++ b/examples/fibonacci.du @@ -0,0 +1,27 @@ +def iterative_fibonacci(n) { + var a = 0, b = 1, c = 0; + for (var i = 1; i < n; i += 1) { + c = a; + a = b; + b = b + c; + } + return b; +} + +var memoization_dict = {}; +def memoized_recursive_fibonacci(n) { + if (memoization_dict.exists(n)) return memoization_dict[n]; + if (n < 2) return n; + var result = memoized_recursive_fibonacci(n - 2) + memoized_recursive_fibonacci(n - 1); + memoization_dict[n] = result; + return result; +} + +def recursive_fibonacci(n) { + if (n < 2) return n; + return recursive_fibonacci(n - 2) + recursive_fibonacci(n - 1); +} + +print('iterative_fibonacci(100) = {}'.format(iterative_fibonacci(100))); +print('memoized_recursive_fibonacci(100) = {}'.format(memoized_recursive_fibonacci(100))); +print('recursive_fibonacci(10) = {}'.format(recursive_fibonacci(10))); diff --git a/examples/recursiveFib.du b/examples/recursiveFib.du deleted file mode 100644 index 5ad0c13c..00000000 --- a/examples/recursiveFib.du +++ /dev/null @@ -1,9 +0,0 @@ -def fibonacci(num) { - if (num < 2) { - return num; - } - - return fibonacci(num - 2) + fibonacci(num - 1); -} - -print(fibonacci(10)); \ No newline at end of file From 2ab016ec9f7d887eaf1e8ec083477a8382e897e0 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Thu, 30 Sep 2021 23:32:25 -0400 Subject: [PATCH 11/52] Add gcd to Math module (supporting 2 args) --- src/optionals/math.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/optionals/math.h | 2 ++ 2 files changed, 44 insertions(+) diff --git a/src/optionals/math.c b/src/optionals/math.c index d17fa194..66d77608 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -221,6 +221,46 @@ static Value tanNative(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(tan(AS_NUMBER(args[0]))); } +static Value gcdNative(DictuVM *vm, int argCount, Value *args) { + if (argCount != 2) { + runtimeError(vm, "gcd() takes 2 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "A non-number value passed to gcd() as the first argument"); + return EMPTY_VAL; + } + if (!IS_NUMBER(args[1])) { + runtimeError(vm, "A non-number value passed to gcd() as the second argument"); + return EMPTY_VAL; + } + + double fa = AS_NUMBER(args[0]); + double fb = AS_NUMBER(args[1]); + + if (fabs(round(fa) - fa) > FLOAT_TOLERANCE) { + runtimeError(vm, "A non-discrete number (%f) passed to gcd() as the first argument", fa); + return EMPTY_VAL; + } + if (fabs(round(fb) - fb) > FLOAT_TOLERANCE) { + runtimeError(vm, "A non-discrete number (%f) passed to gcd() as the second argument", fa); + return EMPTY_VAL; + } + + long long a = round(fa); + long long b = round(fb); + + long long r; + while (b > 0) { + r = a % b; + a = b; + b = r; + } + + return NUMBER_VAL(a); +} + ObjModule *createMathsModule(DictuVM *vm) { ObjString *name = copyString(vm, "Math", 4); push(vm, OBJ_VAL(name)); @@ -242,6 +282,8 @@ ObjModule *createMathsModule(DictuVM *vm) { defineNative(vm, &module->values, "sin", sinNative); defineNative(vm, &module->values, "cos", cosNative); defineNative(vm, &module->values, "tan", tanNative); + defineNative(vm, &module->values, "gcd", gcdNative); + // defineNative(vm, &module->values, "lcm", lcmNative); /** * Define Math properties diff --git a/src/optionals/math.h b/src/optionals/math.h index 5336d9bc..46b0aca6 100644 --- a/src/optionals/math.h +++ b/src/optionals/math.h @@ -7,6 +7,8 @@ #include "optionals.h" #include "../vm/vm.h" +#define FLOAT_TOLERANCE 0.00001 + ObjModule *createMathsModule(DictuVM *vm); #endif //dictu_math_h From 4e2cbb578c64d30c41dd38f3f893d0b630fad301 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Thu, 30 Sep 2021 23:45:03 -0400 Subject: [PATCH 12/52] Support >2 args for `Math.gcd` --- src/optionals/math.c | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index 66d77608..194609e3 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -221,44 +221,44 @@ static Value tanNative(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(tan(AS_NUMBER(args[0]))); } -static Value gcdNative(DictuVM *vm, int argCount, Value *args) { - if (argCount != 2) { - runtimeError(vm, "gcd() takes 2 argument (%d given).", argCount); - return EMPTY_VAL; +static long long gcd(long long a, long long b) { + long long r; + while (b > 0) { + r = a % b; + a = b; + b = r; } + return a; +} - if (!IS_NUMBER(args[0])) { - runtimeError(vm, "A non-number value passed to gcd() as the first argument"); - return EMPTY_VAL; - } - if (!IS_NUMBER(args[1])) { - runtimeError(vm, "A non-number value passed to gcd() as the second argument"); +static Value gcdNative(DictuVM *vm, int argCount, Value *args) { + if (argCount < 2) { + runtimeError(vm, "gcd() requires 2 or more argument (%d given).", argCount); return EMPTY_VAL; } - double fa = AS_NUMBER(args[0]); - double fb = AS_NUMBER(args[1]); + for (int i = 0; i < argCount; ++i) + if (!IS_NUMBER(args[i])) { + runtimeError(vm, "A non-number value passed to gcd() as argument #%d", i); + return EMPTY_VAL; + } - if (fabs(round(fa) - fa) > FLOAT_TOLERANCE) { - runtimeError(vm, "A non-discrete number (%f) passed to gcd() as the first argument", fa); - return EMPTY_VAL; - } - if (fabs(round(fb) - fb) > FLOAT_TOLERANCE) { - runtimeError(vm, "A non-discrete number (%f) passed to gcd() as the second argument", fa); - return EMPTY_VAL; + double as_doubles[argCount]; + for (int i = 0; i < argCount; ++i) { + as_doubles[i] = AS_NUMBER(args[i]); + if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { + runtimeError(vm, "A non-discrete number (%f) passed to gcd() as argument #%d", as_doubles[i], i); + return EMPTY_VAL; + } } - long long a = round(fa); - long long b = round(fb); + long long as_longlongs[argCount]; + for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); - long long r; - while (b > 0) { - r = a % b; - a = b; - b = r; - } + long long result = as_longlongs[0]; + for (int i = 1; i < argCount; ++i) result = gcd(result, as_longlongs[i]); - return NUMBER_VAL(a); + return NUMBER_VAL(result); } ObjModule *createMathsModule(DictuVM *vm) { From 01300485f871942f50fb602270b177444a8ff463 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Thu, 30 Sep 2021 23:54:39 -0400 Subject: [PATCH 13/52] Support list arg for `Math.gcd` --- src/optionals/math.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/optionals/math.c b/src/optionals/math.c index 194609e3..43583bd4 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -232,6 +232,12 @@ static long long gcd(long long a, long long b) { } static Value gcdNative(DictuVM *vm, int argCount, Value *args) { + if (argCount == 1 && IS_LIST(args[0])) { + ObjList *list = AS_LIST(args[0]); + argCount = list->values.count; + args = list->values.values; + } + if (argCount < 2) { runtimeError(vm, "gcd() requires 2 or more argument (%d given).", argCount); return EMPTY_VAL; From a87f136a4b574239e2af07445c9c4483d7014796 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Fri, 1 Oct 2021 00:03:17 -0400 Subject: [PATCH 14/52] Add `Math.lcm` --- src/optionals/math.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index 43583bd4..5fd5cd2e 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -267,6 +267,46 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(result); } +long long lcm(long long a, long long b) { + return (a * b) / gcd(a, b); +} + +static Value lcmNative(DictuVM *vm, int argCount, Value *args) { + if (argCount == 1 && IS_LIST(args[0])) { + ObjList *list = AS_LIST(args[0]); + argCount = list->values.count; + args = list->values.values; + } + + if (argCount < 2) { + runtimeError(vm, "lcm() requires 2 or more argument (%d given).", argCount); + return EMPTY_VAL; + } + + for (int i = 0; i < argCount; ++i) + if (!IS_NUMBER(args[i])) { + runtimeError(vm, "A non-number value passed to lcm() as argument #%d", i); + return EMPTY_VAL; + } + + double as_doubles[argCount]; + for (int i = 0; i < argCount; ++i) { + as_doubles[i] = AS_NUMBER(args[i]); + if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { + runtimeError(vm, "A non-discrete number (%f) passed to lcm() as argument #%d", as_doubles[i], i); + return EMPTY_VAL; + } + } + + long long as_longlongs[argCount]; + for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); + + long long result = as_longlongs[0]; + for (int i = 1; i < argCount; ++i) result = lcm(result, as_longlongs[i]); + + return NUMBER_VAL(result); +} + ObjModule *createMathsModule(DictuVM *vm) { ObjString *name = copyString(vm, "Math", 4); push(vm, OBJ_VAL(name)); @@ -289,7 +329,7 @@ ObjModule *createMathsModule(DictuVM *vm) { defineNative(vm, &module->values, "cos", cosNative); defineNative(vm, &module->values, "tan", tanNative); defineNative(vm, &module->values, "gcd", gcdNative); - // defineNative(vm, &module->values, "lcm", lcmNative); + defineNative(vm, &module->values, "lcm", lcmNative); /** * Define Math properties From 1874ffc6051c0b58543d447faaf0d6353ae3b0a3 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Fri, 1 Oct 2021 00:09:25 -0400 Subject: [PATCH 15/52] Add gcd and lcm tests --- tests/maths/maths.du | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/maths/maths.du b/tests/maths/maths.du index d99b448a..e8fcf388 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -34,6 +34,10 @@ 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); assert(Math.PI == 3.14159265358979); assert(Math.e == 2.71828182845905); \ No newline at end of file From 29bb7d25d8862eb8c4eddb3ba74d6712bb61f355 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Fri, 1 Oct 2021 00:12:27 -0400 Subject: [PATCH 16/52] Document gcd and lcm --- docs/docs/standard-lib/math.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/docs/standard-lib/math.md b/docs/docs/standard-lib/math.md index aade9a35..ee72baa9 100644 --- a/docs/docs/standard-lib/math.md +++ b/docs/docs/standard-lib/math.md @@ -143,3 +143,21 @@ Returns the tan value of a given number in radian Math.tan(1); // 1.5574 Math.tan(50); // -0.2719 ``` + +### Math.gcd(iterable) + +Return the greatest common divisor of the numbers within the iterable + +```cs +Math.gcd(32, 24, 12); // 4 +Math.gcd([32, 24, 12]); // 4 +``` + +### Math.lcm(iterable) + +Return the least common multiple of the numbers within the iterable + +```cs +Math.lcm(32, 24, 12); // 96 +Math.lcm([32, 24, 12]); // 96 +``` From fbda63af0f6e8aeecdce543f2f37d33782b0734d Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Fri, 1 Oct 2021 14:55:05 +0100 Subject: [PATCH 17/52] Create issue template --- .github/ISSUE_TEMPLATE/bug.yml | 74 +++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + 2 files changed, 75 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..6f4fce71 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,74 @@ +name: Bug Report +description: Create a bug-report to help us address errors and bugs. +title: "🐛 [BUG] BUG TITLE HERE" +labels: [bug] +assignees: '' +body: +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true +- type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: false +- type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: false +- type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 2. With this config... + 3. Run '...' + 4. See error... + validations: + required: false +- type: textarea + attributes: + label: Anything else? + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false + +--- +name: Bug Report +about: Create a bug-report to help us address errors and bugs. +title: '🐛 BUG: BUG TITLE HERE' +labels: "bug" +assignees: '' + +--- + +**Description** + + + +**Error Code:** + +Code Terminal is logging: + +**Working Environment** + +Operating System (eg Linux): + +**Screenshots** + +Please add a screenshot if applicable + +[Optional] **Additional Context** + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..0086358d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true From 23ae55d1c076038a24566b8550407fe2d01ff0f7 Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Fri, 1 Oct 2021 15:01:29 +0100 Subject: [PATCH 18/52] Create pull_request_template.md --- .github/pull_request_template.md | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..13dc30e5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,53 @@ + + + + + + + + +### Well detailed description of the change : + + + + I worked on the ..... + +# + +### Context of the change : + + + + - Why is this change required ? + + + +- Does it solve a problem ? (please link the issue) + +# + +### Type of change : + + + + + + + + +- [ ] Bug fix + +- [ ] New feature + +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +# + +### Preview (Screenshots) : + + + + + +

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

From 2c3c4f8d3bc43b0d1eb57e90dda106c73a1abe92 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Fri, 1 Oct 2021 14:06:01 -0400 Subject: [PATCH 19/52] Address review comments --- src/optionals/math.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index 5fd5cd2e..c9ff6f12 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -232,20 +232,27 @@ static long long gcd(long long a, long long b) { } static Value gcdNative(DictuVM *vm, int argCount, Value *args) { + char* argCountError = "gcd() requires 2 or more arguments (%d given)."; + char* nonNumberError = "gcd() argument at index %d is not a number"; + char* notWholeError = "gcd() argument (%f) at index %d is not a whole number"; + if (argCount == 1 && IS_LIST(args[0])) { + argCountError = "List passed to gcd() must have 2 or more elements (%d given)."; + nonNumberError = "The element at index %d of the list passed to gcd() is not a number"; + notWholeError = "The element (%f) at index %d of the list passed to gcd() is not a whole number"; ObjList *list = AS_LIST(args[0]); argCount = list->values.count; args = list->values.values; } if (argCount < 2) { - runtimeError(vm, "gcd() requires 2 or more argument (%d given).", argCount); + runtimeError(vm, argCountError, argCount); return EMPTY_VAL; } for (int i = 0; i < argCount; ++i) if (!IS_NUMBER(args[i])) { - runtimeError(vm, "A non-number value passed to gcd() as argument #%d", i); + runtimeError(vm, nonNumberError, i); return EMPTY_VAL; } @@ -253,7 +260,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { - runtimeError(vm, "A non-discrete number (%f) passed to gcd() as argument #%d", as_doubles[i], i); + runtimeError(vm, notWholeError, as_doubles[i], i); return EMPTY_VAL; } } @@ -272,20 +279,27 @@ long long lcm(long long a, long long b) { } static Value lcmNative(DictuVM *vm, int argCount, Value *args) { + char* argCountError = "lcm() requires 2 or more arguments (%d given)."; + char* nonNumberError = "lcm() argument at index %d is not a number"; + char* notWholeError = "lcm() argument (%f) at index %d is not a whole number"; + if (argCount == 1 && IS_LIST(args[0])) { + argCountError = "List passed to lcm() must have 2 or more elements (%d given)."; + nonNumberError = "The element at index %d of the list passed to lcm() is not a number"; + notWholeError = "The element (%f) at index %d of the list passed to lcm() is not a whole number"; ObjList *list = AS_LIST(args[0]); argCount = list->values.count; args = list->values.values; } if (argCount < 2) { - runtimeError(vm, "lcm() requires 2 or more argument (%d given).", argCount); + runtimeError(vm, argCountError, argCount); return EMPTY_VAL; } for (int i = 0; i < argCount; ++i) if (!IS_NUMBER(args[i])) { - runtimeError(vm, "A non-number value passed to lcm() as argument #%d", i); + runtimeError(vm, nonNumberError, i); return EMPTY_VAL; } @@ -293,7 +307,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { - runtimeError(vm, "A non-discrete number (%f) passed to lcm() as argument #%d", as_doubles[i], i); + runtimeError(vm, notWholeError, as_doubles[i], i); return EMPTY_VAL; } } From 03fac004b09f8b2a988d90016cf85dff4347a494 Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Fri, 1 Oct 2021 19:16:02 +0100 Subject: [PATCH 20/52] Added requested changes --- .github/ISSUE_TEMPLATE/bug.yml | 4 +++- .github/pull_request_template.md | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 6f4fce71..fe17c817 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -59,12 +59,14 @@ assignees: '' **Error Code:** -Code Terminal is logging: +Error Message Displayed: **Working Environment** Operating System (eg Linux): +Dictu Version: + **Screenshots** Please add a screenshot if applicable diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 13dc30e5..380d07e7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ - + @@ -28,11 +28,11 @@ ### Type of change : - + - + - [ ] Bug fix From 122b2f36e6de675ee951f68e8d4cbec213f43bc6 Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Fri, 1 Oct 2021 23:04:51 +0100 Subject: [PATCH 21/52] Update bug.yml --- .github/ISSUE_TEMPLATE/bug.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fe17c817..fc0088bd 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -2,7 +2,7 @@ name: Bug Report description: Create a bug-report to help us address errors and bugs. title: "🐛 [BUG] BUG TITLE HERE" labels: [bug] -assignees: '' +assignees: "" body: - type: checkboxes attributes: @@ -49,7 +49,7 @@ name: Bug Report about: Create a bug-report to help us address errors and bugs. title: '🐛 BUG: BUG TITLE HERE' labels: "bug" -assignees: '' +assignees: "" --- From 152f031eded4bf683cded255cf3f7fcff533d488 Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Fri, 1 Oct 2021 23:08:35 +0100 Subject: [PATCH 22/52] Remove assignees --- .github/ISSUE_TEMPLATE/bug.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fc0088bd..f7458ad0 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -2,7 +2,6 @@ name: Bug Report description: Create a bug-report to help us address errors and bugs. title: "🐛 [BUG] BUG TITLE HERE" labels: [bug] -assignees: "" body: - type: checkboxes attributes: @@ -49,7 +48,6 @@ name: Bug Report about: Create a bug-report to help us address errors and bugs. title: '🐛 BUG: BUG TITLE HERE' labels: "bug" -assignees: "" --- From ddb55f8fb404ee1224525e115396d4b69d8a072c Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 00:14:19 -0400 Subject: [PATCH 23/52] Remove VLAs because MSVC does not support them --- src/optionals/math.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index c9ff6f12..32a8af5c 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -256,21 +256,24 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double as_doubles[argCount]; + double* as_doubles = (double*)malloc(argCount * sizeof(double)); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); + free(as_doubles); return EMPTY_VAL; } } - long long as_longlongs[argCount]; + long long* as_longlongs = (long long*)malloc(argCount * sizeof(long long)); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = gcd(result, as_longlongs[i]); + free(as_doubles); + free(as_longlongs); return NUMBER_VAL(result); } @@ -303,21 +306,24 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double as_doubles[argCount]; + double* as_doubles = (double*)malloc(argCount * sizeof(double)); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); + free(as_doubles); return EMPTY_VAL; } } - long long as_longlongs[argCount]; + long long* as_longlongs = (long long*)malloc(argCount * sizeof(long long)); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = lcm(result, as_longlongs[i]); + free(as_doubles); + free(as_longlongs); return NUMBER_VAL(result); } From 155e14edb19c9963ba3332c78644963c49b35ca9 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 01:08:19 -0400 Subject: [PATCH 24/52] Improve malloc calls --- src/optionals/math.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index 32a8af5c..e54836fa 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -256,7 +256,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double* as_doubles = (double*)malloc(argCount * sizeof(double)); + double* as_doubles = malloc(sizeof *as_doubles * argCount); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { @@ -266,7 +266,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { } } - long long* as_longlongs = (long long*)malloc(argCount * sizeof(long long)); + long long* as_longlongs = malloc(sizeof *as_longlongs * argCount); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; @@ -306,7 +306,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double* as_doubles = (double*)malloc(argCount * sizeof(double)); + double* as_doubles = malloc(sizeof *as_doubles * argCount); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { @@ -316,7 +316,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { } } - long long* as_longlongs = (long long*)malloc(argCount * sizeof(long long)); + long long* as_longlongs = malloc(sizeof *as_longlongs * argCount); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; From ff63dea088c374e112b1607d7de2ec3d7c022fd7 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 01:47:40 -0400 Subject: [PATCH 25/52] Add `Path.join` Closes #432 --- docs/docs/standard-lib/path.md | 12 +++++++++ src/optionals/path.c | 46 ++++++++++++++++++++++++++++++++++ tests/path/join.du | 14 +++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/path/join.du diff --git a/docs/docs/standard-lib/path.md b/docs/docs/standard-lib/path.md index 684ea25b..70b9d6f0 100644 --- a/docs/docs/standard-lib/path.md +++ b/docs/docs/standard-lib/path.md @@ -102,3 +102,15 @@ Returns a list of strings containing the contents of the input path. ```js Path.listDir("/"); // ["bin", "dev", "home", "lib", ...] ``` + +### Path.join(iterable) + +Returns the provided string arguments joined using the directory separator. + +**Note:** A trailing directory separator is ignored from each argument + +```js +Path.join('/tmp', 'abcd', 'efg') == '/tmp/abcd/efg'; +Path.join(['/tmp', 'abcd', 'efg']) == '/tmp/abcd/efg'; +Path.join('/tmp/', 'abcd/', 'efg/') == '/tmp/abcd/efg'; +``` diff --git a/src/optionals/path.c b/src/optionals/path.c index 4307d309..249daba7 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -233,6 +233,51 @@ static Value listDirNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(dir_contents); } +static Value joinNative(DictuVM *vm, int argCount, Value *args) { + char* argCountError = "join() requires 1 or more arguments (%d given)."; + char* nonStringError = "join() argument at index %d is not a string"; + + if (argCount == 1 && IS_LIST(args[0])) { + argCountError = "List passed to join() must have 1 or more elements (%d given)."; + nonStringError = "The element at index %d of the list passed to join() is not a string"; + ObjList *list = AS_LIST(args[0]); + argCount = list->values.count; + args = list->values.values; + } + + if (argCount == 0) { + runtimeError(vm, argCountError, argCount); + return EMPTY_VAL; + } + + for (int i = 0; i < argCount; ++i) { + if (!IS_STRING(args[i])) { + runtimeError(vm, nonStringError, i); + return EMPTY_VAL; + } + } + + // resultSize = # of dir separators that will be used + length of each string arg + size_t resultSize = abs(argCount - 1); // abs is needed her because of a clang bug + for (int i = 0; i < argCount; ++i) resultSize += AS_STRING(args[i])->length; + // It's possible for resultSize to be too large if the strings already end with the dir + // separator, but having likely at most a few bytes of unused memory in the resulting string + // object's char array should be fine. + + char* str = calloc(resultSize, sizeof *str); + char* dest = str; + for (int i = 0; i < argCount; ++i) { + ObjString* src = AS_STRING(args[i]); + // Append the src string to the end of dest + for (int j = 0; j < src->length; ++j) *dest++ = src->chars[j]; + // Append a DIR_SEPARATOR if necessary + if (src->chars[src->length-1] == DIR_SEPARATOR) --resultSize; + else *dest++ = DIR_SEPARATOR; + } + + return OBJ_VAL(takeString(vm, str, resultSize)); +} + ObjModule *createPathModule(DictuVM *vm) { ObjString *name = copyString(vm, "Path", 4); push(vm, OBJ_VAL(name)); @@ -252,6 +297,7 @@ ObjModule *createPathModule(DictuVM *vm) { defineNative(vm, &module->values, "exists", existsNative); defineNative(vm, &module->values, "isDir", isdirNative); defineNative(vm, &module->values, "listDir", listDirNative); + defineNative(vm, &module->values, "join", joinNative); /** * Define Path properties diff --git a/tests/path/join.du b/tests/path/join.du new file mode 100644 index 00000000..d32b59e0 --- /dev/null +++ b/tests/path/join.du @@ -0,0 +1,14 @@ +/** + * join.du + * + * Testing Path.join() + * + * Joins the path component strings using the directory separator. + */ +import Path; + +assert(Path.join('/tmp', 'abcd', 'efg') == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); +assert(Path.join(['/tmp', 'abcd', 'efg']) == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); + +assert(Path.join('/tmp/', 'abcd/', 'efg/') == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); +assert(Path.join(['/tmp/', 'abcd/', 'efg/']) == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); From 18a3d0f9ffb069ca6467371ae04424bfd553f478 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Sat, 2 Oct 2021 16:25:45 +0530 Subject: [PATCH 26/52] Added string.title() method to return a title cased string --- docs/docs/strings.md | 10 ++++++++++ src/vm/datatypes/strings.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/docs/docs/strings.md b/docs/docs/strings.md index 38f627af..95131dc9 100644 --- a/docs/docs/strings.md +++ b/docs/docs/strings.md @@ -242,3 +242,13 @@ Returns the number of occurrences of a given substring within another string. "This documentation".count("Good jokes"); // 0 "Sooooooooooome characters".count("o"); // 11 ``` + +### string.title() + +Returns a title cased version of string with first letter of each word capitalized. + +```cs +"dictu language".title(); // Dictu Language +"this documentation".title(); // This Documentation +"once upon a time".title(); // Once Upon A Time +``` \ No newline at end of file diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index 8834b27f..d6e9d13e 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -465,6 +465,34 @@ static Value countString(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(count); } +static Value titleString(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0) { + runtimeError(vm, "lower() takes no arguments (%d given)", argCount); + return EMPTY_VAL; + } + + ObjString *string = AS_STRING(args[0]); + char *temp = ALLOCATE(vm, char, string->length + 1); + + bool convertNext=true; + + for (int i = 0; string->chars[i]; i++) { + if(string->chars[i]==' '){ + convertNext=true; + } + else if(convertNext){ + temp[i] = toupper(string->chars[i]); + convertNext=false; + continue; + } + temp[i] = tolower(string->chars[i]); + } + + temp[string->length] = '\0'; + + return OBJ_VAL(takeString(vm, temp, string->length)); +} + void declareStringMethods(DictuVM *vm) { defineNative(vm, &vm->stringMethods, "len", lenString); defineNative(vm, &vm->stringMethods, "toNumber", toNumberString); @@ -482,4 +510,5 @@ void declareStringMethods(DictuVM *vm) { defineNative(vm, &vm->stringMethods, "strip", stripString); defineNative(vm, &vm->stringMethods, "count", countString); defineNative(vm, &vm->stringMethods, "toBool", boolNative); // Defined in util + defineNative(vm, &vm->stringMethods, "title", titleString); } From 5b07fd498aba695e7741484951376cf7c736ba24 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Sat, 2 Oct 2021 16:28:41 +0530 Subject: [PATCH 27/52] Added string.title() method to return a title cased string --- tests/strings/title.du | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/strings/title.du diff --git a/tests/strings/title.du b/tests/strings/title.du new file mode 100644 index 00000000..22a78c58 --- /dev/null +++ b/tests/strings/title.du @@ -0,0 +1,15 @@ +/** + * title.du + * + * Testing the str.title() method + * + * .title() method returns a string with first letter of each word capitalized; a title cased string. + */ + +assert("title".title() == "Title"); +assert("dictu language".title() == "Dictu Language"); +assert("DiCtU".title() == "Dictu"); +assert("12345".title() == "12345"); +assert("12Dictu45".title() == "12dictu45"); +assert("!@£$%^&*".title() == "!@£$%^&*"); +assert("once upon a time".title()== "Once Upon A Time"); \ No newline at end of file From 72763c9de54daa3ed46e7c3cf94dc7831aefd3f0 Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Sat, 2 Oct 2021 12:14:00 +0100 Subject: [PATCH 28/52] Create feature-report.md --- .github/ISSUE_TEMPLATE/feature-report.md | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature-report.md diff --git a/.github/ISSUE_TEMPLATE/feature-report.md b/.github/ISSUE_TEMPLATE/feature-report.md new file mode 100644 index 00000000..fdb2406b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-report.md @@ -0,0 +1,28 @@ +--- +name: Feature/Enhancement request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: feature, enhancement +--- + +### Is your feature request related to a problem? Please describe. + +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +# + +### Describe the solution you'd like + +A clear and concise description of what you want to happen. + +# + +### Describe alternatives you've considered + +A clear and concise description of any alternative solutions or features you've considered. + +# + +### Additional context + +Add any other context such as screenshots, schematics, about the feature request here. \ No newline at end of file From 0862dbdde5c7ec2305f9f553d1d3b6e5bff367e6 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Sat, 2 Oct 2021 17:05:29 +0530 Subject: [PATCH 29/52] Added string.title() method to return a title cased string --- 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 d6e9d13e..604102b3 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -467,7 +467,7 @@ static Value countString(DictuVM *vm, int argCount, Value *args) { static Value titleString(DictuVM *vm, int argCount, Value *args) { if (argCount != 0) { - runtimeError(vm, "lower() takes no arguments (%d given)", argCount); + runtimeError(vm, "title() takes no arguments (%d given)", argCount); return EMPTY_VAL; } From bae50ffda66a0dec730faad3f4cfa3a67e106cdc Mon Sep 17 00:00:00 2001 From: MidouWebDev Date: Sat, 2 Oct 2021 12:36:28 +0100 Subject: [PATCH 30/52] Added requested changes Co-authored-by: Jason_000 --- .github/ISSUE_TEMPLATE/feature-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-report.md b/.github/ISSUE_TEMPLATE/feature-report.md index fdb2406b..539ab3aa 100644 --- a/.github/ISSUE_TEMPLATE/feature-report.md +++ b/.github/ISSUE_TEMPLATE/feature-report.md @@ -2,7 +2,7 @@ name: Feature/Enhancement request about: Suggest an idea for this project title: "[FEATURE]" -labels: feature, enhancement +labels: feature request, enhancement --- ### Is your feature request related to a problem? Please describe. From 1b450ddd6e8e8623d2e7b8716339cf29f813c018 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 09:34:03 -0400 Subject: [PATCH 31/52] Use ALLOCATE instead of calloc --- src/optionals/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index 249daba7..f986ca08 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -264,7 +264,7 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { // separator, but having likely at most a few bytes of unused memory in the resulting string // object's char array should be fine. - char* str = calloc(resultSize, sizeof *str); + char* str = ALLOCATE(vm, char, resultSize); char* dest = str; for (int i = 0; i < argCount; ++i) { ObjString* src = AS_STRING(args[i]); From d36e1ea1504e2c33dd4431cbb016f95b12c2afbb Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 09:38:14 -0400 Subject: [PATCH 32/52] Use ALLOCATE instead of malloc --- src/optionals/math.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index e54836fa..9386ccef 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -256,7 +256,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double* as_doubles = malloc(sizeof *as_doubles * argCount); + double* as_doubles = ALLOCATE(vm, double, argCount); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { @@ -266,7 +266,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { } } - long long* as_longlongs = malloc(sizeof *as_longlongs * argCount); + long long* as_longlongs = ALLOCATE(vm, long long, argCount); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; @@ -306,7 +306,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - double* as_doubles = malloc(sizeof *as_doubles * argCount); + double* as_doubles = ALLOCATE(vm, double, argCount); for (int i = 0; i < argCount; ++i) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { @@ -316,7 +316,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { } } - long long* as_longlongs = malloc(sizeof *as_longlongs * argCount); + long long* as_longlongs = ALLOCATE(vm, long long, argCount); for (int i = 0; i < argCount; ++i) as_longlongs[i] = round(as_doubles[i]); long long result = as_longlongs[0]; From 4590addd4750b3852ca4b7cdbb591700ab948d14 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 09:49:55 -0400 Subject: [PATCH 33/52] Use FREE instead of free --- src/optionals/math.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index 9386ccef..d03d2a5e 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -261,7 +261,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); - free(as_doubles); + FREE(vm, double, as_doubles); return EMPTY_VAL; } } @@ -272,8 +272,8 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = gcd(result, as_longlongs[i]); - free(as_doubles); - free(as_longlongs); + FREE(vm, double, as_doubles); + FREE(vm, long long, as_longlongs); return NUMBER_VAL(result); } @@ -311,7 +311,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); - free(as_doubles); + FREE(vm, double, as_doubles); return EMPTY_VAL; } } @@ -322,8 +322,8 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = lcm(result, as_longlongs[i]); - free(as_doubles); - free(as_longlongs); + FREE(vm, double, as_doubles); + FREE(vm, long long, as_longlongs); return NUMBER_VAL(result); } From 6f5f22194606603d6886e3c985955f8c01d8cb5c Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 10:11:31 -0400 Subject: [PATCH 34/52] Use FREE_ARRAY instead of FREE --- src/optionals/math.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/optionals/math.c b/src/optionals/math.c index d03d2a5e..d091566d 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -261,7 +261,7 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); - FREE(vm, double, as_doubles); + FREE_ARRAY(vm, double, as_doubles, argCount); return EMPTY_VAL; } } @@ -272,8 +272,8 @@ static Value gcdNative(DictuVM *vm, int argCount, Value *args) { long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = gcd(result, as_longlongs[i]); - FREE(vm, double, as_doubles); - FREE(vm, long long, as_longlongs); + FREE_ARRAY(vm, double, as_doubles, argCount); + FREE_ARRAY(vm, long long, as_longlongs, argCount); return NUMBER_VAL(result); } @@ -311,7 +311,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { as_doubles[i] = AS_NUMBER(args[i]); if (fabs(round(as_doubles[i]) - as_doubles[i]) > FLOAT_TOLERANCE) { runtimeError(vm, notWholeError, as_doubles[i], i); - FREE(vm, double, as_doubles); + FREE_ARRAY(vm, double, as_doubles, argCount); return EMPTY_VAL; } } @@ -322,8 +322,8 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { long long result = as_longlongs[0]; for (int i = 1; i < argCount; ++i) result = lcm(result, as_longlongs[i]); - FREE(vm, double, as_doubles); - FREE(vm, long long, as_longlongs); + FREE_ARRAY(vm, double, as_doubles, argCount); + FREE_ARRAY(vm, long long, as_longlongs, argCount); return NUMBER_VAL(result); } From 64b9cf0f88474cfa01ca1fdc54fe69e8a02998bc Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 15:48:46 +0100 Subject: [PATCH 35/52] Remove ubuntu 16 from the workflow and add macos11 --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 25af0e73..c9424e4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, ubuntu-16.04, ubuntu-20.04] + os: [ubuntu-latest, ubuntu-20.04] steps: - uses: actions/checkout@v2 @@ -36,7 +36,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-latest] + os: [macOS-latest, macOS-11] steps: - uses: actions/checkout@v2 @@ -66,4 +66,4 @@ jobs: run: | cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_SYSTEM_VERSION="10.0.18362.0" -DCICD=1 -DDISABLE_HTTP=1 -DDISABLE_LINENOISE=1 -B build cmake --build build - Debug\dictu.exe tests/runTests.du \ No newline at end of file + Debug\dictu.exe tests/runTests.du From 885f982cacce86b39a0923605207971bcc22932b Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 17:59:54 +0100 Subject: [PATCH 36/52] Update feature-report.md --- .github/ISSUE_TEMPLATE/feature-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature-report.md b/.github/ISSUE_TEMPLATE/feature-report.md index 539ab3aa..9dfd7f45 100644 --- a/.github/ISSUE_TEMPLATE/feature-report.md +++ b/.github/ISSUE_TEMPLATE/feature-report.md @@ -2,7 +2,7 @@ name: Feature/Enhancement request about: Suggest an idea for this project title: "[FEATURE]" -labels: feature request, enhancement +labels: feature request --- ### Is your feature request related to a problem? Please describe. @@ -25,4 +25,4 @@ A clear and concise description of any alternative solutions or features you've ### Additional context -Add any other context such as screenshots, schematics, about the feature request here. \ No newline at end of file +Add any other context such as screenshots, schematics, about the feature request here. From 67821797bb66f63438476642c79f8a1f49a15d7e Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 18:03:39 +0100 Subject: [PATCH 37/52] Update feature-report.md --- .github/ISSUE_TEMPLATE/feature-report.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature-report.md b/.github/ISSUE_TEMPLATE/feature-report.md index 9dfd7f45..45556015 100644 --- a/.github/ISSUE_TEMPLATE/feature-report.md +++ b/.github/ISSUE_TEMPLATE/feature-report.md @@ -5,24 +5,24 @@ title: "[FEATURE]" labels: feature request --- -### Is your feature request related to a problem? Please describe. +### Is your feature request related to a problem? -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + # ### Describe the solution you'd like -A clear and concise description of what you want to happen. + # ### Describe alternatives you've considered -A clear and concise description of any alternative solutions or features you've considered. + # ### Additional context -Add any other context such as screenshots, schematics, about the feature request here. + From 9aabd6f9670f20658b68cf89889dffa0e21de4fe Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 21:18:32 +0100 Subject: [PATCH 38/52] Make it so obj.getAttribute checks public class properties and public methods --- src/vm/datatypes/instance.c | 8 ++++++++ tests/classes/getAttributes.du | 31 +++++++++++++++++++++++++++++++ tests/classes/import.du | 3 ++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/classes/getAttributes.du diff --git a/src/vm/datatypes/instance.c b/src/vm/datatypes/instance.c index 9b4dfd75..c83322f6 100644 --- a/src/vm/datatypes/instance.c +++ b/src/vm/datatypes/instance.c @@ -65,6 +65,14 @@ static Value getAttribute(DictuVM *vm, int argCount, Value *args) { return value; } + if (tableGet(&instance->klass->publicMethods, AS_STRING(key), &value)) { + return value; + } + + if (tableGet(&instance->klass->publicProperties, AS_STRING(key), &value)) { + return value; + } + return defaultValue; } diff --git a/tests/classes/getAttributes.du b/tests/classes/getAttributes.du new file mode 100644 index 00000000..0f151937 --- /dev/null +++ b/tests/classes/getAttributes.du @@ -0,0 +1,31 @@ +/** + * getAttributes.du + * + * Testing the obj.getAttribute() method + * + */ + +class Test { + var x = 10; + + init(private priv = 10, var pub = 20) { + this.prop = 30; + } + + private privMethod() {} + + publicMethod() {} +} + +assert(Test().getAttribute("x") == 10); +assert(Test().getAttribute("x", nil) == 10); +assert(Test().getAttribute("priv") == nil); +assert(Test().getAttribute("priv", 10) == 10); +assert(Test().getAttribute("privMethod") == nil); +assert(Test().getAttribute("privMethod", 10) == 10); +assert(Test().getAttribute("pub") == 20); +assert(Test().getAttribute("pub", nil) == 20); +assert(Test().getAttribute("prop") == 30); +assert(Test().getAttribute("prop", nil) == 30); +assert(type(Test().getAttribute("publicMethod") == "function")); +assert(type(Test().getAttribute("publicMethod", nil) == "function")); \ No newline at end of file diff --git a/tests/classes/import.du b/tests/classes/import.du index f53ffb62..331911b4 100644 --- a/tests/classes/import.du +++ b/tests/classes/import.du @@ -18,4 +18,5 @@ import "isInstance.du"; import "constructor.du"; import "optionalChaining.du"; import "private.du"; -import "annotations.du"; \ No newline at end of file +import "annotations.du"; +import "getAttributes.du"; \ No newline at end of file From 71b0cc83d36f72c998ec527713848584cd2df8d4 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 21:34:53 +0100 Subject: [PATCH 39/52] Update and rename feature-report.md to feature-report.yml --- .github/ISSUE_TEMPLATE/feature-report.md | 28 ------------- .github/ISSUE_TEMPLATE/feature-report.yml | 49 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 28 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/feature-report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-report.yml diff --git a/.github/ISSUE_TEMPLATE/feature-report.md b/.github/ISSUE_TEMPLATE/feature-report.md deleted file mode 100644 index 45556015..00000000 --- a/.github/ISSUE_TEMPLATE/feature-report.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Feature/Enhancement request -about: Suggest an idea for this project -title: "[FEATURE]" -labels: feature request ---- - -### Is your feature request related to a problem? - - - -# - -### Describe the solution you'd like - - - -# - -### Describe alternatives you've considered - - - -# - -### Additional context - - diff --git a/.github/ISSUE_TEMPLATE/feature-report.yml b/.github/ISSUE_TEMPLATE/feature-report.yml new file mode 100644 index 00000000..db950f44 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-report.yml @@ -0,0 +1,49 @@ +name: Feature Request +description: Suggest an idea for this project. +title: "[FEATURE]" +labels: [feature request] +body: +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the feature you are requesting. + options: + - label: I have searched the existing issues + required: true +- type: textarea + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: false +- type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: false +- type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: false +- type: textarea + attributes: + label: Additional context + description: Add any other context such as screenshots, schematics, about the feature request here. + validations: + required: false + +--- +name: Feature Request +about: Suggest an idea for this project +title: '[FEATURE]' +labels: "feature request" + +--- +name: Feature/Enhancement request +about: Suggest an idea for this project. +title: "[FEATURE]" +labels: feature request +--- From f1d0b73ee2c54d8199903bf00eee671e2291adb9 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 21:54:28 +0100 Subject: [PATCH 40/52] Walk the inheritance chain for class properties. --- src/vm/datatypes/instance.c | 11 +++++++++-- tests/classes/getAttributes.du | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/vm/datatypes/instance.c b/src/vm/datatypes/instance.c index c83322f6..51aca3c8 100644 --- a/src/vm/datatypes/instance.c +++ b/src/vm/datatypes/instance.c @@ -69,8 +69,15 @@ static Value getAttribute(DictuVM *vm, int argCount, Value *args) { return value; } - if (tableGet(&instance->klass->publicProperties, AS_STRING(key), &value)) { - return value; + // Check class for properties + ObjClass *klass = instance->klass; + + while (klass != NULL) { + if (tableGet(&klass->publicProperties, AS_STRING(key), &value)) { + return value; + } + + klass = klass->superclass; } return defaultValue; diff --git a/tests/classes/getAttributes.du b/tests/classes/getAttributes.du index 0f151937..d7b081a4 100644 --- a/tests/classes/getAttributes.du +++ b/tests/classes/getAttributes.du @@ -28,4 +28,21 @@ assert(Test().getAttribute("pub", nil) == 20); assert(Test().getAttribute("prop") == 30); assert(Test().getAttribute("prop", nil) == 30); assert(type(Test().getAttribute("publicMethod") == "function")); -assert(type(Test().getAttribute("publicMethod", nil) == "function")); \ No newline at end of file +assert(type(Test().getAttribute("publicMethod", nil) == "function")); + +class Inherit < Test { + +} + +assert(Inherit().getAttribute("x") == 10); +assert(Inherit().getAttribute("x", nil) == 10); +assert(Inherit().getAttribute("priv") == nil); +assert(Inherit().getAttribute("priv", 10) == 10); +assert(Inherit().getAttribute("privMethod") == nil); +assert(Inherit().getAttribute("privMethod", 10) == 10); +assert(Inherit().getAttribute("pub") == 20); +assert(Inherit().getAttribute("pub", nil) == 20); +assert(Inherit().getAttribute("prop") == 30); +assert(Inherit().getAttribute("prop", nil) == 30); +assert(type(Inherit().getAttribute("publicMethod") == "function")); +assert(type(Inherit().getAttribute("publicMethod", nil) == "function")); \ No newline at end of file From ea8b74c7a4b86392115834ea52b6b9fd49b88fbf Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 2 Oct 2021 22:08:38 +0100 Subject: [PATCH 41/52] Update bug.yml --- .github/ISSUE_TEMPLATE/bug.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index f7458ad0..d59d51fe 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,6 @@ name: Bug Report description: Create a bug-report to help us address errors and bugs. -title: "🐛 [BUG] BUG TITLE HERE" +title: "[BUG]" labels: [bug] body: - type: checkboxes @@ -46,7 +46,7 @@ body: --- name: Bug Report about: Create a bug-report to help us address errors and bugs. -title: '🐛 BUG: BUG TITLE HERE' +title: 'BUG' labels: "bug" --- From 0bb2f351cec78160b948cdfbdd52d988a1e94452 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 18:21:58 -0400 Subject: [PATCH 42/52] Fix memory issue with `Path.join` --- src/optionals/path.c | 2 +- tests/path/import.du | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index f986ca08..2f3c8ed5 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -264,7 +264,7 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { // separator, but having likely at most a few bytes of unused memory in the resulting string // object's char array should be fine. - char* str = ALLOCATE(vm, char, resultSize); + char* str = ALLOCATE(vm, char, resultSize + 1); char* dest = str; for (int i = 0; i < argCount; ++i) { ObjString* src = AS_STRING(args[i]); diff --git a/tests/path/import.du b/tests/path/import.du index 8906e8f9..d58d0bc8 100644 --- a/tests/path/import.du +++ b/tests/path/import.du @@ -11,4 +11,5 @@ import "isAbsolute.du"; import "realpath.du"; import "exists.du"; import "isDir.du"; -import "listDir.du"; \ No newline at end of file +import "join.du"; +import "listDir.du"; From 57e5671325a8187b44c66a13b70d34f720eb38f8 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 18:34:42 -0400 Subject: [PATCH 43/52] Check if path part ends with dir sep upfront in `Path.join` --- src/optionals/path.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index 2f3c8ed5..d0af8fd2 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -257,22 +257,22 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { } } + ObjString* part; // resultSize = # of dir separators that will be used + length of each string arg size_t resultSize = abs(argCount - 1); // abs is needed her because of a clang bug - for (int i = 0; i < argCount; ++i) resultSize += AS_STRING(args[i])->length; - // It's possible for resultSize to be too large if the strings already end with the dir - // separator, but having likely at most a few bytes of unused memory in the resulting string - // object's char array should be fine. + for (int i = 0; i < argCount; ++i) { + part = AS_STRING(args[i]); + resultSize += part->length - (part->chars[part->length-1] == DIR_SEPARATOR); + } char* str = ALLOCATE(vm, char, resultSize + 1); char* dest = str; for (int i = 0; i < argCount; ++i) { - ObjString* src = AS_STRING(args[i]); - // Append the src string to the end of dest - for (int j = 0; j < src->length; ++j) *dest++ = src->chars[j]; + part = AS_STRING(args[i]); + // Append the part string to the end of dest + for (int j = 0; j < part->length; ++j) *dest++ = part->chars[j]; // Append a DIR_SEPARATOR if necessary - if (src->chars[src->length-1] == DIR_SEPARATOR) --resultSize; - else *dest++ = DIR_SEPARATOR; + if (part->chars[part->length-1] != DIR_SEPARATOR) *dest++ = DIR_SEPARATOR; } return OBJ_VAL(takeString(vm, str, resultSize)); From 40f46e5d272c5572cc81656d0aaf9c2fc45a4dbe Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 18:42:37 -0400 Subject: [PATCH 44/52] Attempt to fix `Path.join` for Windows It's unclear what's wrong from the CI logs, but not much has changed. Perhaps MSVC doesn't like us subtracting the result of an `==` from an int without a cast. --- src/optionals/path.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index d0af8fd2..7acd1d0c 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -259,10 +259,10 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { ObjString* part; // resultSize = # of dir separators that will be used + length of each string arg - size_t resultSize = abs(argCount - 1); // abs is needed her because of a clang bug + size_t resultSize = abs(argCount - 1); // abs is needed here because of a clang bug for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); - resultSize += part->length - (part->chars[part->length-1] == DIR_SEPARATOR); + resultSize += part->length - (int)(part->chars[part->length-1] == DIR_SEPARATOR); } char* str = ALLOCATE(vm, char, resultSize + 1); From 5684a27bf0eed99a304a90df29280d50df0896a8 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 18:48:28 -0400 Subject: [PATCH 45/52] Fix `Path.join` test on Windows --- src/optionals/path.c | 2 +- tests/path/join.du | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index 7acd1d0c..114b0b12 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -262,7 +262,7 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { size_t resultSize = abs(argCount - 1); // abs is needed here because of a clang bug for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); - resultSize += part->length - (int)(part->chars[part->length-1] == DIR_SEPARATOR); + resultSize += part->length - (part->chars[part->length-1] == DIR_SEPARATOR); } char* str = ALLOCATE(vm, char, resultSize + 1); diff --git a/tests/path/join.du b/tests/path/join.du index d32b59e0..18e181c3 100644 --- a/tests/path/join.du +++ b/tests/path/join.du @@ -10,5 +10,12 @@ import Path; assert(Path.join('/tmp', 'abcd', 'efg') == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); assert(Path.join(['/tmp', 'abcd', 'efg']) == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); -assert(Path.join('/tmp/', 'abcd/', 'efg/') == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); -assert(Path.join(['/tmp/', 'abcd/', 'efg/']) == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); +var a, b; + +a = Path.join('/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator); +b = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; +assert(a == b); + +a = Path.join(['/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator]); +b = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; +assert(a == b); From bd4e0656b01dcc9000975f0104c27ad9e0324434 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 19:32:44 -0400 Subject: [PATCH 46/52] Strip leading dir separators in `Path.join` --- src/optionals/path.c | 9 +++++++-- tests/path/join.du | 32 +++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index 114b0b12..301a2273 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -262,15 +262,20 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { size_t resultSize = abs(argCount - 1); // abs is needed here because of a clang bug for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); - resultSize += part->length - (part->chars[part->length-1] == DIR_SEPARATOR); + resultSize += part->length; + resultSize -= i && part->chars[0] == DIR_SEPARATOR; + resultSize -= part->chars[part->length-1] == DIR_SEPARATOR; } char* str = ALLOCATE(vm, char, resultSize + 1); char* dest = str; for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); + // Skip leading DIR_SEPARATOR on everything but the first part. + // e.g. `join('/tmp', '/abc')` returns '/tmp/abc' instead of '/tmp//abc' + int start = i && part->chars[0] == DIR_SEPARATOR; // Append the part string to the end of dest - for (int j = 0; j < part->length; ++j) *dest++ = part->chars[j]; + for (int j = start; j < part->length; ++j) *dest++ = part->chars[j]; // Append a DIR_SEPARATOR if necessary if (part->chars[part->length-1] != DIR_SEPARATOR) *dest++ = DIR_SEPARATOR; } diff --git a/tests/path/join.du b/tests/path/join.du index 18e181c3..f45097af 100644 --- a/tests/path/join.du +++ b/tests/path/join.du @@ -7,15 +7,29 @@ */ import Path; -assert(Path.join('/tmp', 'abcd', 'efg') == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); -assert(Path.join(['/tmp', 'abcd', 'efg']) == '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'); +var result; +var expected = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; -var a, b; +// Test basic join with varargs +result = Path.join('/tmp', 'abcd', 'efg'); +assert(result == expected); -a = Path.join('/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator); -b = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; -assert(a == b); +// Test basic join with list +result = Path.join(['/tmp', 'abcd', 'efg']); +assert(result == expected); -a = Path.join(['/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator]); -b = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; -assert(a == b); +// Test join with trailing directory separators with varargs +result = Path.join('/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator); +assert(result == expected); + +// Test join with trailing directory separators with list +result = Path.join(['/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator]); +assert(result == expected); + +// Test join with leading and trailing directory separators with varargs +result = Path.join('/tmp' + Path.dirSeparator, Path.dirSeparator + 'abcd' + Path.dirSeparator, Path.dirSeparator + 'efg' + Path.dirSeparator); +assert(result == expected); + +// Test join with leading and trailing directory separators with list +result = Path.join(['/tmp' + Path.dirSeparator, Path.dirSeparator + 'abcd' + Path.dirSeparator, Path.dirSeparator + 'efg' + Path.dirSeparator]); +assert(result == expected); From 401497b89897a812603b10a1c205d0edb60fce5e Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Sat, 2 Oct 2021 21:09:02 -0400 Subject: [PATCH 47/52] Handle multiple dir separators a beginning/end of parts in `Path.join` With special handling for the first and last parts. --- src/optionals/path.c | 43 ++++++++++++++++++++++++++++++++++++------- tests/path/join.du | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/optionals/path.c b/src/optionals/path.c index 301a2273..7833ab73 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -263,21 +263,50 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); resultSize += part->length; - resultSize -= i && part->chars[0] == DIR_SEPARATOR; - resultSize -= part->chars[part->length-1] == DIR_SEPARATOR; + // Account for leading DIR_SEPARATOR chars to be removed + for (int j = 0; j < part->length; ++j) { + if (part->chars[j] == DIR_SEPARATOR) --resultSize; + else break; + } + // Account for trailing DIR_SEPARATOR chars to be removed + for (int j = part->length - 1; j >= 0; --j) { + if (part->chars[j] == DIR_SEPARATOR) --resultSize; + else break; + } } + // Account for leading/trailing DIR_SEPARATOR on the first/last part respectively + part = AS_STRING(args[0]); + resultSize += part->chars[0] == DIR_SEPARATOR; + part = AS_STRING(args[argCount - 1]); + resultSize += part->chars[part->length - 1] == DIR_SEPARATOR; char* str = ALLOCATE(vm, char, resultSize + 1); char* dest = str; for (int i = 0; i < argCount; ++i) { part = AS_STRING(args[i]); - // Skip leading DIR_SEPARATOR on everything but the first part. - // e.g. `join('/tmp', '/abc')` returns '/tmp/abc' instead of '/tmp//abc' - int start = i && part->chars[0] == DIR_SEPARATOR; + + // Skip leading DIR_SEPARATOR characters on everything except for one on the first part, + // and trailing DIR_SEPARATOR characters on everything except for one on the last part. + // e.g. `join('///tmp///', '/abc///')` returns '/tmp/abc' instead of '///tmp////abc///' + int start = 0; + while (part->chars[start++] == DIR_SEPARATOR); + int end = part->length - 1; + while (part->chars[end--] == DIR_SEPARATOR); + + bool lastIteration = i == argCount - 1; + + // Check if the first part starts with a DIR_SEPARATOR so that we can preserve it + bool firstLeadingDirSep = !i && part->chars[0] == DIR_SEPARATOR; + + // Check if the last part ends with a DIR_SEPARATOR so that we can preserve it + bool lastTrailingDirSep = lastIteration && part->chars[part->length - 1] == DIR_SEPARATOR; + // Append the part string to the end of dest - for (int j = start; j < part->length; ++j) *dest++ = part->chars[j]; + for (int j = start - 1 - firstLeadingDirSep; j <= end + 1 + lastTrailingDirSep; ++j) + *dest++ = part->chars[j]; + // Append a DIR_SEPARATOR if necessary - if (part->chars[part->length-1] != DIR_SEPARATOR) *dest++ = DIR_SEPARATOR; + if (!lastIteration && part->chars[end + 1] != DIR_SEPARATOR) *dest++ = DIR_SEPARATOR; } return OBJ_VAL(takeString(vm, str, resultSize)); diff --git a/tests/path/join.du b/tests/path/join.du index f45097af..1d3d5a6b 100644 --- a/tests/path/join.du +++ b/tests/path/join.du @@ -8,28 +8,46 @@ import Path; var result; -var expected = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; +var expected1 = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg'; +var expected2 = '/tmp' + Path.dirSeparator + 'abcd' + Path.dirSeparator + 'efg' + Path.dirSeparator; +var manyDirSeps = Path.dirSeparator + Path.dirSeparator + Path.dirSeparator + Path.dirSeparator + Path.dirSeparator; // Test basic join with varargs result = Path.join('/tmp', 'abcd', 'efg'); -assert(result == expected); +assert(result == expected1); // Test basic join with list -result = Path.join(['/tmp', 'abcd', 'efg']); -assert(result == expected); +result = Path.join(['/tmp', 'abcd', 'efg' + Path.dirSeparator]); +assert(result == expected2); // Test join with trailing directory separators with varargs -result = Path.join('/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator); -assert(result == expected); +result = Path.join('/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg'); +assert(result == expected1); // Test join with trailing directory separators with list result = Path.join(['/tmp' + Path.dirSeparator, 'abcd' + Path.dirSeparator, 'efg' + Path.dirSeparator]); -assert(result == expected); +assert(result == expected2); // Test join with leading and trailing directory separators with varargs -result = Path.join('/tmp' + Path.dirSeparator, Path.dirSeparator + 'abcd' + Path.dirSeparator, Path.dirSeparator + 'efg' + Path.dirSeparator); -assert(result == expected); +result = Path.join('/tmp' + Path.dirSeparator, Path.dirSeparator + 'abcd' + Path.dirSeparator, Path.dirSeparator + 'efg'); +assert(result == expected1); // Test join with leading and trailing directory separators with list result = Path.join(['/tmp' + Path.dirSeparator, Path.dirSeparator + 'abcd' + Path.dirSeparator, Path.dirSeparator + 'efg' + Path.dirSeparator]); -assert(result == expected); +assert(result == expected2); + +// Test join with many leading and trailing directory separators with varargs +result = Path.join('/tmp' + manyDirSeps, manyDirSeps + 'abcd' + manyDirSeps, manyDirSeps + 'efg'); +assert(result == expected1); + +// Test join with many leading and trailing directory separators with list +result = Path.join(['/tmp' + manyDirSeps, manyDirSeps + 'abcd' + manyDirSeps, manyDirSeps + 'efg' + manyDirSeps]); +assert(result == expected2); + +// Test join with directory separators inside of part with varargs +result = Path.join('/tmp', 'abcd' + Path.dirSeparator + 'efg'); +assert(result == expected1); + +// Test join with directory separators inside of part with list +result = Path.join(['/tmp', 'abcd' + Path.dirSeparator + 'efg' + Path.dirSeparator]); +assert(result == expected2); From b4638b18fa117c0dfff5147bd2805b55a3645883 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 20:15:47 +0100 Subject: [PATCH 48/52] Update so examples run as part of the CI/CD pipeline --- .github/workflows/main.yml | 10 ++++++++++ examples/factorial.du | 12 +++++++++++- examples/guessingGame.du | 21 ++++++++++++++++++++- examples/runExamples.du | 20 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 examples/runExamples.du diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9424e4b..84d233a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,3 +67,13 @@ jobs: cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_SYSTEM_VERSION="10.0.18362.0" -DCICD=1 -DDISABLE_HTTP=1 -DDISABLE_LINENOISE=1 -B build cmake --build build Debug\dictu.exe tests/runTests.du + run-examples: + name: Test Examples + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Make dictu and run examples + run: | + cmake -DCMAKE_BUILD_TYPE=Debug -B ./build + cmake --build ./build + ./dictu examples/runExamples.du | tee /dev/stderr | grep -q 'Total memory usage: 0' diff --git a/examples/factorial.du b/examples/factorial.du index d1adf354..a3ab8052 100644 --- a/examples/factorial.du +++ b/examples/factorial.du @@ -1,4 +1,14 @@ -var amount = input("Enter a number: ").toNumber().unwrap(); +var amount; + +/** + * This is to handle the case where the script is ran headless in the CI/CD pipeline. + */ +if (System.argv[0] == __file__) { + amount = input("Enter a number: ").toNumber().unwrap(); +} else { + amount = 8; +} + var num = 1; if (amount > 0) { diff --git a/examples/guessingGame.du b/examples/guessingGame.du index 6e20c9b0..59a775e9 100644 --- a/examples/guessingGame.du +++ b/examples/guessingGame.du @@ -1,7 +1,20 @@ +import Random; + const guess = 10; +var maxGuesses = 5; while { - const userInput = input("Input your guess: ").toNumber().unwrap(); + var userInput; + + /** + * This is to handle the case where the script is ran headless in the CI/CD pipeline. + */ + if (System.argv[0] == __file__) { + userInput = input("Input your guess: ").toNumber().unwrap(); + } else { + userInput = Random.range(7, 12); + print("Automated guess: {}".format(userInput)); + } if (userInput == guess) { print("Well done!"); break; @@ -12,4 +25,10 @@ while { } System.sleep(1); + maxGuesses -= 1; + + if (maxGuesses <= 0) { + print("You ran out of guesses, the answer was {}!".format(guess)); + break; + } } \ No newline at end of file diff --git a/examples/runExamples.du b/examples/runExamples.du new file mode 100644 index 00000000..a4b2c341 --- /dev/null +++ b/examples/runExamples.du @@ -0,0 +1,20 @@ +/** + * File for the CI/CD pipeline to run all the examples. + * + * Note: Doesn't run guessing game as it requires user input + */ + +import "design-patterns/builder.du"; +import "design-patterns/chain-of-responsibility.du"; +import "design-patterns/factoryMethod.du"; +import "design-patterns/observer.du"; +import "binarySearchTree.du"; +import "bubbleSort.du"; +import "classes.du"; +import "factorial.du"; +import "fibonacci.du"; +import "fizzBuzz.du"; +import "guessingGame.du"; +import "inheritance.du"; +import "isPalindrome.du"; +import "factorial.du"; \ No newline at end of file From df77f0911f55fc5a8314e1681394194fa547057d Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 20:18:23 +0100 Subject: [PATCH 49/52] Ensure libcurl is installed --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 84d233a8..15e93f0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -74,6 +74,8 @@ jobs: - uses: actions/checkout@v2 - name: Make dictu and run examples run: | + sudo apt-get update + 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' From 9167474f096c0a1d32c73778911344550c436350 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 20:21:03 +0100 Subject: [PATCH 50/52] Remove note that not longer applies --- examples/runExamples.du | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/runExamples.du b/examples/runExamples.du index a4b2c341..a8bb52fe 100644 --- a/examples/runExamples.du +++ b/examples/runExamples.du @@ -1,7 +1,5 @@ /** * File for the CI/CD pipeline to run all the examples. - * - * Note: Doesn't run guessing game as it requires user input */ import "design-patterns/builder.du"; @@ -17,4 +15,4 @@ import "fizzBuzz.du"; import "guessingGame.du"; import "inheritance.du"; import "isPalindrome.du"; -import "factorial.du"; \ No newline at end of file +import "factorial.du"; From 2ca5e4806c077c5e26534b8b827ad8e9d0b91c97 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 20:48:22 +0100 Subject: [PATCH 51/52] Pop the value off the stack after printing and table resize --- src/vm/vm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vm/vm.c b/src/vm/vm.c index b2f4705a..561ece68 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -861,12 +861,13 @@ static DictuInterpretResult run(DictuVM *vm) { DISPATCH(); CASE_CODE(POP_REPL): { - Value v = pop(vm); + Value v = peek(vm, 0); if (!IS_NIL(v)) { setReplVar(vm, v); printValue(v); printf("\n"); } + pop(vm); DISPATCH(); } From 7c814a867f63391a14d23181191b1eb52c18414e Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 3 Oct 2021 22:50:54 +0100 Subject: [PATCH 52/52] Bump version to 0.21.0 --- docs/_config.yml | 2 +- src/include/dictu_include.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index 6871e498..2b0df27e 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.20.0" +version: "0.21.0" github_username: dictu-lang search_enabled: true diff --git a/src/include/dictu_include.h b/src/include/dictu_include.h index 67980dfc..4a51593d 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 "20" +#define DICTU_MINOR_VERSION "21" #define DICTU_PATCH_VERSION "0" #define DICTU_STRING_VERSION "Dictu Version: " DICTU_MAJOR_VERSION "." DICTU_MINOR_VERSION "." DICTU_PATCH_VERSION "\n"