From 3c91586b15ae76618b1c370604a56a09a32dc005 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 29 Sep 2020 22:34:20 +0100 Subject: [PATCH 01/45] Get from imports working for builtin and user defined modules --- c/compiler.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ c/debug.c | 14 +++++++++ c/opcodes.h | 2 ++ c/scanner.c | 2 ++ c/scanner.h | 2 +- c/vm.c | 49 +++++++++++++++++++++++++++++++ 6 files changed, 151 insertions(+), 1 deletion(-) diff --git a/c/compiler.c b/c/compiler.c index 9e733107..2eb98b28 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -1168,6 +1168,7 @@ ParseRule rules[] = { {NULL, NULL, PREC_NONE}, // TOKEN_WITH {NULL, NULL, PREC_NONE}, // TOKEN_EOF {NULL, NULL, PREC_NONE}, // TOKEN_IMPORT + {NULL, NULL, PREC_NONE}, // TOKEN_FROM {NULL, NULL, PREC_NONE}, // TOKEN_ERROR }; @@ -1653,6 +1654,86 @@ static void importStatement(Compiler *compiler) { emitByte(compiler, OP_IMPORT_END); } +static void fromImportStatement(Compiler *compiler) { + if (match(compiler, TOKEN_STRING)) { + int importConstant = makeConstant(compiler, OBJ_VAL(copyString( + compiler->parser->vm, + compiler->parser->previous.start + 1, + compiler->parser->previous.length - 2))); + + consume(compiler, TOKEN_IMPORT, "Expect 'import' after import path."); + + emitBytes(compiler, OP_IMPORT, importConstant); + + + uint8_t variables[255]; + int varCount = 0; + + do { + variables[varCount] = parseVariable(compiler, "Expect variable name.", false); + varCount++; + + if (varCount > 255) { + error(compiler->parser, "Cannot have more than 255 variables."); + } + } while (match(compiler, TOKEN_COMMA)); + + emitBytes(compiler, OP_IMPORT_FROM, varCount); + + for (int i = 0; i < varCount; ++i) { + emitByte(compiler, variables[i]); + } + + // This needs to be two separate loops as we need + // all the variables popped before defining. + for (int i = varCount - 1; i >= 0; --i) { + defineVariable(compiler, variables[i], false); + } + + emitByte(compiler, OP_IMPORT_END); + } else { + uint8_t moduleName = parseVariable(compiler, "Expect import identifier.", false); + + int index = findBuiltinModule( + (char *)compiler->parser->previous.start, + compiler->parser->previous.length + ); + + consume(compiler, TOKEN_IMPORT, "Expect 'import' after identifier."); + + if (index == -1) { + error(compiler->parser, "Unknown module1"); + } + + uint8_t variables[255]; + int varCount = 0; + + do { + variables[varCount] = parseVariable(compiler, "Expect variable name.", false); + varCount++; + + if (varCount > 255) { + error(compiler->parser, "Cannot have more than 255 variables."); + } + } while (match(compiler, TOKEN_COMMA)); + + emitBytes(compiler, OP_IMPORT_BUILTIN_VARIABLE, index); + emitBytes(compiler, moduleName, varCount); + + for (int i = 0; i < varCount; ++i) { + emitByte(compiler, variables[i]); + } + + // This needs to be two separate loops as we need + // all the variables popped before defining. + for (int i = varCount - 1; i >= 0; --i) { + defineVariable(compiler, variables[i], false); + } + } + + consume(compiler, TOKEN_SEMICOLON, "Expect ';' after import."); +} + static void whileStatement(Compiler *compiler) { Loop loop; loop.start = currentChunk(compiler)->count; @@ -1743,6 +1824,8 @@ static void statement(Compiler *compiler) { withStatement(compiler); } else if (match(compiler, TOKEN_IMPORT)) { importStatement(compiler); + } else if (match(compiler, TOKEN_FROM)) { + fromImportStatement(compiler); } else if (match(compiler, TOKEN_BREAK)) { breakStatement(compiler); } else if (match(compiler, TOKEN_WHILE)) { diff --git a/c/debug.c b/c/debug.c index aa778142..ef5aea36 100644 --- a/c/debug.c +++ b/c/debug.c @@ -41,6 +41,16 @@ static int builtinImportInstruction(const char* name, Chunk* chunk, return offset + 3; } +static int builtinFromImportInstruction(const char* name, Chunk* chunk, + int offset) { + uint8_t module = chunk->code[offset + 2]; + uint8_t argCount = chunk->code[offset + 3]; + printf("%-16s '", name); + printValue(chunk->constants.values[module]); + printf("'\n"); + return offset + 3 + argCount; +} + static int classInstruction(const char* name, Chunk* chunk, int offset) { uint8_t type = chunk->code[offset + 1]; @@ -178,8 +188,12 @@ int disassembleInstruction(Chunk *chunk, int offset) { return constantInstruction("OP_IMPORT", chunk, offset); case OP_IMPORT_BUILTIN: return builtinImportInstruction("OP_IMPORT_BUILTIN", chunk, offset); + case OP_IMPORT_BUILTIN_VARIABLE: + return builtinFromImportInstruction("OP_IMPORT_BUILTIN_VARIABLE", chunk, offset); case OP_IMPORT_VARIABLE: return simpleInstruction("OP_IMPORT_VARIABLE", offset); + case OP_IMPORT_FROM: + return constantInstruction("OP_IMPORT_FROM", chunk, offset); case OP_IMPORT_END: return simpleInstruction("OP_IMPORT_END", offset); case OP_NEW_LIST: diff --git a/c/opcodes.h b/c/opcodes.h index 4e9d1131..fbea6fe7 100644 --- a/c/opcodes.h +++ b/c/opcodes.h @@ -52,7 +52,9 @@ OPCODE(END_CLASS) OPCODE(METHOD) OPCODE(IMPORT) OPCODE(IMPORT_BUILTIN) +OPCODE(IMPORT_BUILTIN_VARIABLE) OPCODE(IMPORT_VARIABLE) +OPCODE(IMPORT_FROM) OPCODE(IMPORT_END) OPCODE(USE) OPCODE(OPEN_FILE) diff --git a/c/scanner.c b/c/scanner.c index f33bda46..55e642d8 100644 --- a/c/scanner.c +++ b/c/scanner.c @@ -189,6 +189,8 @@ static TokenType identifierType() { return checkKeyword(2, 3, "lse", TOKEN_FALSE); case 'o': return checkKeyword(2, 1, "r", TOKEN_FOR); + case 'r': + return checkKeyword(2, 2, "om", TOKEN_FROM); } } break; diff --git a/c/scanner.h b/c/scanner.h index 57500de3..7aef1197 100644 --- a/c/scanner.h +++ b/c/scanner.h @@ -37,7 +37,7 @@ typedef enum { TOKEN_VAR, TOKEN_CONST, TOKEN_TRUE, TOKEN_FALSE, TOKEN_NIL, TOKEN_FOR, TOKEN_WHILE, TOKEN_BREAK, TOKEN_RETURN, TOKEN_CONTINUE, - TOKEN_WITH, TOKEN_EOF, TOKEN_IMPORT, + TOKEN_WITH, TOKEN_EOF, TOKEN_IMPORT, TOKEN_FROM, TOKEN_ERROR } TokenType; diff --git a/c/vm.c b/c/vm.c index 7d207a2f..a9c5ca9e 100644 --- a/c/vm.c +++ b/c/vm.c @@ -1155,11 +1155,60 @@ static InterpretResult run(VM *vm) { DISPATCH(); } + CASE_CODE(IMPORT_BUILTIN_VARIABLE): { + int index = READ_BYTE(); + ObjString *fileName = READ_STRING(); + int varCount = READ_BYTE(); + Value moduleVal; + ObjModule *module; + + if (tableGet(&vm->modules, fileName, &moduleVal)) { + module = AS_MODULE(moduleVal); + } else { + module = importBuiltinModule(vm, index); + } + + for (int i = 0; i < varCount; i++) { + Value moduleVariable; + ObjString *variable = READ_STRING(); + + if (!tableGet(&module->values, variable, &moduleVariable)) { + frame->ip = ip; + runtimeError(vm, "%s can't be found in module %s", variable->chars, module->name->chars); + return INTERPRET_RUNTIME_ERROR; + } + + push(vm, moduleVariable); + } + + DISPATCH(); + } + CASE_CODE(IMPORT_VARIABLE): { push(vm, OBJ_VAL(vm->lastModule)); DISPATCH(); } + CASE_CODE(IMPORT_FROM): { + int varCount = READ_BYTE(); + + for (int i = 0; i < varCount; i++) { + Value moduleVariable; + ObjString *variable = READ_STRING(); + + if (!tableGet(&vm->lastModule->values, variable, &moduleVariable)) { + vm->scriptNameCount--; + frame->ip = ip; + runtimeError(vm, "%s can't be found in module %s", variable->chars, vm->lastModule->name->chars); + return INTERPRET_RUNTIME_ERROR; + } + + push(vm, moduleVariable); + } + + DISPATCH(); + } + CASE_CODE(IMPORT_END): { vm->scriptNameCount--; if (vm->scriptNameCount >= 0) { From 8597a9c0c0baf69e8b8534a95d826922ee63016e Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Wed, 30 Sep 2020 22:27:30 +0100 Subject: [PATCH 02/45] Add tests for from import and handle not finding a file better --- c/main.c | 6 ++++++ c/util.c | 3 +-- c/vm.c | 12 +++++++++--- tests/imports/class.du | 11 +++++++++++ tests/imports/from.du | 25 +++++++++++++++++++++++++ tests/imports/import.du | 7 +++++++ tests/imports/middle-import.du | 1 + tests/runTests.du | 1 + 8 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 tests/imports/class.du create mode 100644 tests/imports/from.du create mode 100644 tests/imports/import.du create mode 100644 tests/imports/middle-import.du diff --git a/c/main.c b/c/main.c index 58ab1dc7..5a61708a 100644 --- a/c/main.c +++ b/c/main.c @@ -111,6 +111,12 @@ static void runFile(VM *vm, int argc, const char *argv[]) { UNUSED(argc); char *source = readFile(argv[1]); + + if (source == NULL) { + fprintf(stderr, "Could not open file \"%s\".\n", argv[1]); + exit(74); + } + InterpretResult result = interpret(vm, source); free(source); // [owner] diff --git a/c/util.c b/c/util.c index f99065af..4318669c 100644 --- a/c/util.c +++ b/c/util.c @@ -7,8 +7,7 @@ char *readFile(const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { - fprintf(stderr, "Could not open file \"%s\".\n", path); - exit(74); + return NULL; } fseek(file, 0L, SEEK_END); diff --git a/c/vm.c b/c/vm.c index a9c5ca9e..a2611196 100644 --- a/c/vm.c +++ b/c/vm.c @@ -1103,7 +1103,13 @@ static InterpretResult run(VM *vm) { DISPATCH(); } - char *s = readFile(fileName->chars); + char *source = readFile(fileName->chars); + + if (source == NULL) { + frame->ip = ip; + runtimeError(vm, "Could not open file \"%s\".", fileName->chars); + return INTERPRET_RUNTIME_ERROR; + } if (vm->scriptNameCapacity < vm->scriptNameCount + 2) { int oldCapacity = vm->scriptNameCapacity; @@ -1119,9 +1125,9 @@ static InterpretResult run(VM *vm) { vm->lastModule = module; push(vm, OBJ_VAL(module)); - ObjFunction *function = compile(vm, module, s); + ObjFunction *function = compile(vm, module, source); pop(vm); - free(s); + free(source); if (function == NULL) return INTERPRET_COMPILE_ERROR; push(vm, OBJ_VAL(function)); diff --git a/tests/imports/class.du b/tests/imports/class.du new file mode 100644 index 00000000..e3e0962c --- /dev/null +++ b/tests/imports/class.du @@ -0,0 +1,11 @@ +class Test { + init() { + this.x = 10; + } +} + +class AnotherTest { + init() { + this.y = 10; + } +} \ No newline at end of file diff --git a/tests/imports/from.du b/tests/imports/from.du new file mode 100644 index 00000000..1b01ac69 --- /dev/null +++ b/tests/imports/from.du @@ -0,0 +1,25 @@ +/** + * from.du + * + * Testing the from import + * + */ + +from "tests/imports/class.du" import Test; + +assert(type(Test) == 'class'); +assert(Test().x == 10); + +from "tests/imports/class.du" import Test, AnotherTest; + +assert(type(Test) == 'class'); +assert(Test().x == 10); +assert(type(AnotherTest) == 'class'); +assert(AnotherTest().y == 10); + +import "tests/imports/middle-import.du" as MiddleImportModule; + +assert(type(MiddleImportModule.Test) == 'class'); +assert(MiddleImportModule.Test().x == 10); +assert(type(MiddleImportModule.AnotherTest) == 'class'); +assert(MiddleImportModule.AnotherTest().y == 10); \ No newline at end of file diff --git a/tests/imports/import.du b/tests/imports/import.du new file mode 100644 index 00000000..b64fd6d7 --- /dev/null +++ b/tests/imports/import.du @@ -0,0 +1,7 @@ +/** + * import.du + * + * General import file for all the import tests + */ + +import "tests/imports/from.du"; \ No newline at end of file diff --git a/tests/imports/middle-import.du b/tests/imports/middle-import.du new file mode 100644 index 00000000..4250b37a --- /dev/null +++ b/tests/imports/middle-import.du @@ -0,0 +1 @@ +from "tests/imports/class.du" import Test, AnotherTest; \ No newline at end of file diff --git a/tests/runTests.du b/tests/runTests.du index e79a0c6f..a062a1e9 100644 --- a/tests/runTests.du +++ b/tests/runTests.du @@ -25,6 +25,7 @@ if (isDefined("HTTP")) { } import "tests/modules/import.du"; +import "tests/imports/import.du"; // If we got here no runtime errors were thrown, therefore all tests passed. print("All tests passed successfully!"); From 931a28bb9e16f0fbcaa09534e6e80a46cd31b672 Mon Sep 17 00:00:00 2001 From: MANOHAR KAKUMANI <42714264+manoharkakumani@users.noreply.github.com> Date: Wed, 14 Oct 2020 16:51:58 +0530 Subject: [PATCH 03/45] Update scanner.c Now language supports hex values and exponents ex: 0xA2F , 0XA2F , 0xa2f , 0Xa2f 1e3,1e+5,1e-5,etc ![image](https://user-images.githubusercontent.com/42714264/95977586-abe70900-0e36-11eb-91c2-a646cd96552e.png) --- c/scanner.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/c/scanner.c b/c/scanner.c index d32fa613..5b2b9ddb 100644 --- a/c/scanner.c +++ b/c/scanner.c @@ -29,7 +29,9 @@ static bool isAlpha(char c) { static bool isDigit(char c) { return c >= '0' && c <= '9'; } - +static bool isHexDigit(char c) { + return ((c >= '0' && c <= '9')||(c >= 'A' && c <= 'F')||(c >= 'a' && c <= 'f')); +} static bool isAtEnd() { return *scanner.current == '\0'; } @@ -278,21 +280,48 @@ static Token identifier() { return makeToken(identifierType()); } +static Token exponent(){ + // Consume the "e" + advance(); + while(peek() == '_') advance(); + if(peek()=='+' || peek()=='-'){ + // Consume the "+ or -" + advance(); + } + if(!isDigit(peek()) && peek() != '_') return errorToken("Invalid exopnent literal"); + while (isDigit(peek()) || peek() == '_') advance(); + return makeToken(TOKEN_NUMBER); +} static Token number() { while (isDigit(peek()) || peek() == '_') advance(); - + if(peek()=='e'||peek()=='E') + return exponent(); // Look for a fractional part. - if (peek() == '.' && isDigit(peekNext())) { + if (peek() == '.'&& (isDigit(peekNext()) || (peekNext()=='e'||peekNext()=='E')) ){ // Consume the "." advance(); - - while (isDigit(peek()) || peek() == '_') advance(); + while(peek() == '_') advance(); + if(peek()=='e'||peek()=='E') + return exponent(); + while (isDigit(peek()) || peek() == '_') advance(); + if(peek()=='e'||peek()=='E') + return exponent(); } - return makeToken(TOKEN_NUMBER); } +static Token hexNumber() { +if (peek()=='0')advance(); +if((peek()=='x') || (peek()=='X')){ + advance(); + if (!isHexDigit(peek())) return errorToken("Invalid hex literal"); + while (isHexDigit(peek())) advance(); + return makeToken(TOKEN_NUMBER); +} +else return number(); +} + static Token string(char stringToken) { while (peek() != stringToken && !isAtEnd()) { @@ -303,7 +332,6 @@ static Token string(char stringToken) { } advance(); } - if (isAtEnd()) return errorToken("Unterminated string."); // The closing " or '. @@ -326,7 +354,7 @@ Token scanToken() { char c = advance(); if (isAlpha(c)) return identifier(); - if (isDigit(c)) return number(); + if (isDigit(c)) return hexNumber(); switch (c) { case '(': From 9c1e6387f45c14e49afa7d77af68c612a893f38f Mon Sep 17 00:00:00 2001 From: MANOHAR KAKUMANI <42714264+manoharkakumani@users.noreply.github.com> Date: Thu, 15 Oct 2020 10:25:05 +0530 Subject: [PATCH 04/45] Update scanner.c numbers fixed --- c/scanner.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/c/scanner.c b/c/scanner.c index 5b2b9ddb..5868d713 100644 --- a/c/scanner.c +++ b/c/scanner.c @@ -30,7 +30,7 @@ static bool isDigit(char c) { return c >= '0' && c <= '9'; } static bool isHexDigit(char c) { - return ((c >= '0' && c <= '9')||(c >= 'A' && c <= 'F')||(c >= 'a' && c <= 'f')); + return ((c >= '0' && c <= '9')||(c >= 'A' && c <= 'F')||(c >= 'a' && c <= 'f')||(c=='_')); } static bool isAtEnd() { return *scanner.current == '\0'; @@ -298,12 +298,9 @@ static Token number() { if(peek()=='e'||peek()=='E') return exponent(); // Look for a fractional part. - if (peek() == '.'&& (isDigit(peekNext()) || (peekNext()=='e'||peekNext()=='E')) ){ + if (peek() == '.'&& (isDigit(peekNext()))){ // Consume the "." advance(); - while(peek() == '_') advance(); - if(peek()=='e'||peek()=='E') - return exponent(); while (isDigit(peek()) || peek() == '_') advance(); if(peek()=='e'||peek()=='E') return exponent(); @@ -312,6 +309,7 @@ static Token number() { } static Token hexNumber() { +while(peek() == '_') advance(); if (peek()=='0')advance(); if((peek()=='x') || (peek()=='X')){ advance(); From adb786eda9a1ca5fe33676a02ff5d74a8f04f77f Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Thu, 22 Oct 2020 21:52:59 +0530 Subject: [PATCH 05/45] Added sin,cos,tan methods in Math package --- c/optionals/math.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ tests/maths/maths.du | 3 +++ 2 files changed, 48 insertions(+) diff --git a/c/optionals/math.c b/c/optionals/math.c index 91d7f8d4..fa177414 100644 --- a/c/optionals/math.c +++ b/c/optionals/math.c @@ -179,6 +179,48 @@ static Value sqrtNative(VM *vm, int argCount, Value *args) { return NUMBER_VAL(sqrt(AS_NUMBER(args[0]))); } +static Value sinNative(VM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "sin() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "A non-number value passed to sin()"); + return EMPTY_VAL; + } + + return NUMBER_VAL(sin(AS_NUMBER(args[0]))); +} + +static Value cosNative(VM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "cos() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "A non-number value passed to cos()"); + return EMPTY_VAL; + } + + return NUMBER_VAL(cos(AS_NUMBER(args[0]))); +} + +static Value tanNative(VM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "tan() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "A non-number value passed to tan()"); + return EMPTY_VAL; + } + + return NUMBER_VAL(tan(AS_NUMBER(args[0]))); +} + ObjModule *createMathsClass(VM *vm) { ObjString *name = copyString(vm, "Math", 4); push(vm, OBJ_VAL(name)); @@ -197,6 +239,9 @@ ObjModule *createMathsClass(VM *vm) { defineNative(vm, &module->values, "min", minNative); defineNative(vm, &module->values, "sum", sumNative); defineNative(vm, &module->values, "sqrt", sqrtNative); + defineNative(vm, &module->values, "sin", sinNative); + defineNative(vm, &module->values, "cos", cosNative); + defineNative(vm, &module->values, "tan", tanNative); /** * Define Math properties diff --git a/tests/maths/maths.du b/tests/maths/maths.du index c5bfe823..322fc8f9 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -29,6 +29,9 @@ assert(Math.sqrt(25) == 5); assert(Math.sqrt(100) == 10); assert(Math.sqrt(20) == 4.47213595499958); assert(Math.sqrt(100_00) == 1_00); +assert(Math.sin( 1.5707963268) == 1); +assert(Math.cos(0) == 1); +assert(Math.tan(0.7853981634) == 1); assert(Math.PI == 3.14159265358979); assert(Math.e == 2.71828182845905); \ No newline at end of file From 93dfc80a821b3f22a35d962d42ffa45ba6eb0ec1 Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Thu, 22 Oct 2020 22:17:08 +0530 Subject: [PATCH 06/45] Added sin,cos,tan in math package --- tests/maths/maths.du | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/maths/maths.du b/tests/maths/maths.du index 322fc8f9..d99b448a 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -29,9 +29,11 @@ assert(Math.sqrt(25) == 5); assert(Math.sqrt(100) == 10); assert(Math.sqrt(20) == 4.47213595499958); assert(Math.sqrt(100_00) == 1_00); -assert(Math.sin( 1.5707963268) == 1); +assert(Math.sin(1) > 0.84); +assert(Math.sin(1) < 0.845); assert(Math.cos(0) == 1); -assert(Math.tan(0.7853981634) == 1); +assert(Math.tan(1) > 1.5); +assert(Math.tan(1) < 1.6); assert(Math.PI == 3.14159265358979); assert(Math.e == 2.71828182845905); \ No newline at end of file From 94ca036313a13aac0d603fea0aae52d74705eb3f Mon Sep 17 00:00:00 2001 From: amuthanmannan Date: Fri, 23 Oct 2020 07:41:52 +0530 Subject: [PATCH 07/45] Added docs for sin,cos,tan --- docs/docs/math.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/docs/math.md b/docs/docs/math.md index 45f242ea..123307ca 100644 --- a/docs/docs/math.md +++ b/docs/docs/math.md @@ -115,3 +115,30 @@ Returns the square root of a given number Math.sqrt(25); // 5 Math.sqrt(100); // 10 ``` + +### Math.sin(number) + +Returns the sin value of a given number in radian + +```cs +Math.sin(1); // 0.8414 +Math.sin(50); // -0.2623 +``` + +### Math.cos(number) + +Returns the cos value of a given number in radian + +```cs +Math.cos(1); // 0.5403 +Math.cos(50); // 0.9649 +``` + +### Math.tan(number) + +Returns the tan value of a given number in radian + +```cs +Math.tan(1); // 1.5574 +Math.tan(50); // -0.2719 +``` From 7e7761b30623e304ff3e4e7735cf7da4a7853992 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 25 Oct 2020 15:28:12 +0000 Subject: [PATCH 08/45] Correct code formatting --- c/scanner.c | 58 +++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/c/scanner.c b/c/scanner.c index 5868d713..5fafb9dd 100644 --- a/c/scanner.c +++ b/c/scanner.c @@ -29,9 +29,11 @@ static bool isAlpha(char c) { static bool isDigit(char c) { return c >= '0' && c <= '9'; } + static bool isHexDigit(char c) { - return ((c >= '0' && c <= '9')||(c >= 'A' && c <= 'F')||(c >= 'a' && c <= 'f')||(c=='_')); + return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c == '_')); } + static bool isAtEnd() { return *scanner.current == '\0'; } @@ -280,44 +282,44 @@ static Token identifier() { return makeToken(identifierType()); } -static Token exponent(){ - // Consume the "e" - advance(); - while(peek() == '_') advance(); - if(peek()=='+' || peek()=='-'){ - // Consume the "+ or -" - advance(); - } - if(!isDigit(peek()) && peek() != '_') return errorToken("Invalid exopnent literal"); - while (isDigit(peek()) || peek() == '_') advance(); - return makeToken(TOKEN_NUMBER); + +static Token exponent() { + // Consume the "e" + advance(); + while (peek() == '_') advance(); + if (peek() == '+' || peek() == '-') { + // Consume the "+ or -" + advance(); + } + if (!isDigit(peek()) && peek() != '_') return errorToken("Invalid exopnent literal"); + while (isDigit(peek()) || peek() == '_') advance(); + return makeToken(TOKEN_NUMBER); } static Token number() { while (isDigit(peek()) || peek() == '_') advance(); - if(peek()=='e'||peek()=='E') - return exponent(); + if (peek() == 'e' || peek() == 'E') + return exponent(); // Look for a fractional part. - if (peek() == '.'&& (isDigit(peekNext()))){ + if (peek() == '.' && (isDigit(peekNext()))) { // Consume the "." advance(); - while (isDigit(peek()) || peek() == '_') advance(); - if(peek()=='e'||peek()=='E') - return exponent(); + while (isDigit(peek()) || peek() == '_') advance(); + if (peek() == 'e' || peek() == 'E') + return exponent(); } return makeToken(TOKEN_NUMBER); } static Token hexNumber() { -while(peek() == '_') advance(); -if (peek()=='0')advance(); -if((peek()=='x') || (peek()=='X')){ - advance(); - if (!isHexDigit(peek())) return errorToken("Invalid hex literal"); - while (isHexDigit(peek())) advance(); - return makeToken(TOKEN_NUMBER); -} -else return number(); + while (peek() == '_') advance(); + if (peek() == '0')advance(); + if ((peek() == 'x') || (peek() == 'X')) { + advance(); + if (!isHexDigit(peek())) return errorToken("Invalid hex literal"); + while (isHexDigit(peek())) advance(); + return makeToken(TOKEN_NUMBER); + } else return number(); } @@ -326,7 +328,7 @@ static Token string(char stringToken) { if (peek() == '\n') { scanner.line++; } else if (peek() == '\\' && !scanner.rawString) { - scanner.current++; + scanner.current++; } advance(); } From 7bee1382998d76cb56ebefda9dbb67b2630cf6f3 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 25 Oct 2020 15:34:54 +0000 Subject: [PATCH 09/45] Add some tests for hex and scientific notation numbers --- tests/number/import.du | 1 + tests/number/literals.du | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/number/literals.du diff --git a/tests/number/import.du b/tests/number/import.du index e9dc950b..dbda735f 100644 --- a/tests/number/import.du +++ b/tests/number/import.du @@ -6,3 +6,4 @@ import "tests/number/toString.du"; import "tests/number/toBool.du"; +import "tests/number/literals.du"; diff --git a/tests/number/literals.du b/tests/number/literals.du new file mode 100644 index 00000000..d4f4e5d3 --- /dev/null +++ b/tests/number/literals.du @@ -0,0 +1,13 @@ +/** + * literals.du + * + * Testing some alternate numeric literals + * + */ + +assert(0x2a == 42); +assert(0x342f == 13359); +assert(1e5 == 100000); +assert(1e+5 == 100000); +assert(1e-5 == 0.00001); +assert(2.5e3 == 2500); \ No newline at end of file From 59b662b50ac8a6f809068ef226173ae05d3777b0 Mon Sep 17 00:00:00 2001 From: Ajoe-T Date: Mon, 26 Oct 2020 13:17:48 +0530 Subject: [PATCH 10/45] Added example for inheritance --- examples/inheritance.du | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 examples/inheritance.du diff --git a/examples/inheritance.du b/examples/inheritance.du new file mode 100644 index 00000000..8cefbc7f --- /dev/null +++ b/examples/inheritance.du @@ -0,0 +1,48 @@ +/** +* inheritance.du +* +*/ + +// Parent class - Animal +class Animal { + + eating() { + print("Eating"); + } +} + +// Child class - Dog +class Dog < Animal { + + init() { + this.eating = super.eating; + } + + bark() { + print("Barking"); + } +} + +// Child class - Cat +class Cat < Animal { + + init() { + this.eating = super.eating; + } + + meow() { + print("Meowing"); + } +} + +// Initialize child class +var dog = Dog(); +var cat = Cat(); + +// Inherited methods/behaviour from parent class - Animal +dog.eating(); +cat.eating(); + +// Methods/behaviour specific to child class +dog.bark(); +cat.meow(); \ No newline at end of file From f679e5f59fac3060c2fe84e44395d16e428bf0ba Mon Sep 17 00:00:00 2001 From: tglide Date: Mon, 26 Oct 2020 18:08:57 +0000 Subject: [PATCH 11/45] feat(examples): add isPalindrome.du --- examples/isPalindrome.du | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/isPalindrome.du diff --git a/examples/isPalindrome.du b/examples/isPalindrome.du new file mode 100644 index 00000000..f71a4d30 --- /dev/null +++ b/examples/isPalindrome.du @@ -0,0 +1,13 @@ +def isPalindrome(arg) { + var len = arg.len(); + for (var i = 0; i < len/2; ++i) { + if (arg[i] != arg[len - 1 - i]) { + return false; + } + + } + return true; +} + +print(isPalindrome("aba")); // true +print(isPalindrome("abc")); // false \ No newline at end of file From 17735037d37e7d054f9f9c488a3076aae6d5a5af Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 26 Oct 2020 21:36:53 +0000 Subject: [PATCH 12/45] Fix an issue where multiple calls to the HTTP lib would segfault --- c/common.h | 2 ++ c/memory.h | 2 +- c/optionals/http.c | 42 ++++++++++++++++++++++++++++++------------ c/optionals/http.h | 3 +-- c/table.c | 8 +------- c/value.c | 4 ++-- c/vm.h | 2 -- 7 files changed, 37 insertions(+), 26 deletions(-) diff --git a/c/common.h b/c/common.h index a8d65fee..698f60c7 100644 --- a/c/common.h +++ b/c/common.h @@ -5,6 +5,8 @@ #include #include +#define UNUSED(__x__) (void) __x__ + #define NAN_TAGGING #define DEBUG_PRINT_CODE #define DEBUG_TRACE_EXECUTION diff --git a/c/memory.h b/c/memory.h index 1bb068b7..7b4aa715 100644 --- a/c/memory.h +++ b/c/memory.h @@ -14,7 +14,7 @@ ((capacity) < 8 ? 8 : (capacity) * 2) #define SHRINK_CAPACITY(capacity) \ - ((capacity) < 16 ? 7 : (capacity) / 2) + ((capacity) < 16 ? 8 : (capacity) / 2) #define GROW_ARRAY(vm, previous, type, oldCount, count) \ (type*)reallocate(vm, previous, sizeof(type) * (oldCount), \ diff --git a/c/optionals/http.c b/c/optionals/http.c index 6dfc84cb..6164beeb 100644 --- a/c/optionals/http.c +++ b/c/optionals/http.c @@ -24,12 +24,11 @@ static void createResponse(VM *vm, Response *response) { push(vm, OBJ_VAL(response->headers)); response->len = 0; - response->res = NULL; + response->res = malloc(1); + response->res[0] = '\0'; } -static size_t writeResponse(char *ptr, size_t size, size_t nmemb, void *data) -{ - Response *response = (Response *) data; +static size_t writeResponse(char *ptr, size_t size, size_t nmemb, Response *response) { size_t new_len = response->len + size * nmemb; response->res = GROW_ARRAY(response->vm, response->res, char, response->len, new_len + 1); if (response->res == NULL) { @@ -43,8 +42,7 @@ static size_t writeResponse(char *ptr, size_t size, size_t nmemb, void *data) return size * nmemb; } -static size_t writeHeaders(char *ptr, size_t size, size_t nitems, void *data) -{ +static size_t writeHeaders(char *ptr, size_t size, size_t nitems, void *data) { Response *response = (Response *) data; // if nitems equals 2 its an empty header if (nitems != 2) { @@ -117,7 +115,7 @@ static char *dictToPostArgs(ObjDict *dict) { return ret; } -static ObjDict* endRequest(VM *vm, CURL *curl, Response response) { +static ObjDict *endRequest(VM *vm, CURL *curl, Response response) { // Get status code curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.statusCode); ObjString *content = takeString(vm, response.res, response.len); @@ -125,10 +123,6 @@ static ObjDict* endRequest(VM *vm, CURL *curl, Response response) { // Push to stack to avoid GC push(vm, OBJ_VAL(content)); - /* always cleanup */ - curl_easy_cleanup(curl); - curl_global_cleanup(); - ObjDict *responseVal = initDict(vm); // Push to stack to avoid GC push(vm, OBJ_VAL(responseVal)); @@ -153,6 +147,10 @@ static ObjDict* endRequest(VM *vm, CURL *curl, Response response) { pop(vm); pop(vm); + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + return responseVal; } @@ -201,6 +199,11 @@ static Value get(VM *vm, int argCount, Value *args) { /* Check for errors */ if (curlResponse != CURLE_OK) { + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + pop(vm); + errno = curlResponse; SET_ERRNO(GET_SELF_CLASS); return NIL_VAL; @@ -209,6 +212,11 @@ static Value get(VM *vm, int argCount, Value *args) { return OBJ_VAL(endRequest(vm, curl, response)); } + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + pop(vm); + errno = CURLE_FAILED_INIT; SET_ERRNO(GET_SELF_CLASS); return NIL_VAL; @@ -234,7 +242,7 @@ static Value post(VM *vm, int argCount, Value *args) { return EMPTY_VAL; } - timeout = (long)AS_NUMBER(args[2]); + timeout = (long) AS_NUMBER(args[2]); dict = AS_DICT(args[1]); } else if (argCount == 2) { if (!IS_DICT(args[1])) { @@ -282,6 +290,11 @@ static Value post(VM *vm, int argCount, Value *args) { } if (curlResponse != CURLE_OK) { + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + pop(vm); + errno = curlResponse; SET_ERRNO(GET_SELF_CLASS); return NIL_VAL; @@ -290,6 +303,11 @@ static Value post(VM *vm, int argCount, Value *args) { return OBJ_VAL(endRequest(vm, curl, response)); } + /* always cleanup */ + curl_easy_cleanup(curl); + curl_global_cleanup(); + pop(vm); + errno = CURLE_FAILED_INIT; SET_ERRNO(GET_SELF_CLASS); return NIL_VAL; diff --git a/c/optionals/http.h b/c/optionals/http.h index 73af4304..d6b42f12 100644 --- a/c/optionals/http.h +++ b/c/optionals/http.h @@ -10,10 +10,9 @@ typedef struct response { VM *vm; - char *res; ObjList *headers; + char *res; size_t len; - size_t headerLen; long statusCode; } Response; diff --git a/c/table.c b/c/table.c index 5caf774d..31d5c181 100644 --- a/c/table.c +++ b/c/table.c @@ -113,6 +113,7 @@ bool tableSet(VM *vm, Table *table, ObjString *key, Value value) { } bool tableDelete(VM *vm, Table *table, ObjString *key) { + UNUSED(vm); if (table->count == 0) return false; int capacityMask = table->capacityMask; @@ -159,13 +160,6 @@ bool tableDelete(VM *vm, Table *table, ObjString *key) { entry = nextEntry; } - // TODO: Add constant for table load factor - if (table->count - 1 < table->capacityMask * 0.35) { - // Figure out the new table size. - capacityMask = SHRINK_CAPACITY(table->capacityMask); - adjustCapacity(vm, table, capacityMask); - } - return true; } diff --git a/c/value.c b/c/value.c index aaf96f2f..5de25d51 100644 --- a/c/value.c +++ b/c/value.c @@ -8,7 +8,7 @@ #include "vm.h" #define TABLE_MAX_LOAD 0.75 -#define TABLE_MIN_LOAD 0.35 +#define TABLE_MIN_LOAD 0.25 void initValueArray(ValueArray *array) { array->values = NULL; @@ -216,7 +216,7 @@ bool dictDelete(VM *vm, ObjDict *dict, Value key) { if (dict->count - 1 < dict->capacityMask * TABLE_MIN_LOAD) { // Figure out the new table size. - capacityMask = SHRINK_CAPACITY(dict->capacityMask); + capacityMask = SHRINK_CAPACITY(dict->capacityMask + 1) - 1; adjustDictCapacity(vm, dict, capacityMask); } diff --git a/c/vm.h b/c/vm.h index d4c7b48b..2efa5f6e 100644 --- a/c/vm.h +++ b/c/vm.h @@ -64,8 +64,6 @@ typedef enum { #define OK 0 #define NOTOK -1 -#define UNUSED(__x__) (void) __x__ - VM *initVM(bool repl, const char *scriptName, int argc, const char *argv[]); void freeVM(VM *vm); From 9c7433338c9c24f5027308c4a1016aabc15e9048 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Mon, 26 Oct 2020 22:57:28 +0000 Subject: [PATCH 13/45] Fix nested imports --- c/compiler.c | 5 ++++- c/vm.c | 2 +- tests/modules/functions.du | 23 +++++++++++++++++++++++ tests/modules/variables.du | 9 ++++++++- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/c/compiler.c b/c/compiler.c index 6d6f7db6..9c4a4977 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -1676,6 +1676,7 @@ static void importStatement(Compiler *compiler) { compiler->parser->previous.length - 2))); emitBytes(compiler, OP_IMPORT, importConstant); + emitByte(compiler, OP_POP); if (match(compiler, TOKEN_AS)) { uint8_t importName = parseVariable(compiler, "Expect import alias.", false); @@ -1683,7 +1684,9 @@ static void importStatement(Compiler *compiler) { defineVariable(compiler, importName, false); } } else { - uint8_t importName = parseVariable(compiler, "Expect import identifier.", false); + consume(compiler, TOKEN_IDENTIFIER, "Expect import identifier."); + uint8_t importName = identifierConstant(compiler, &compiler->parser->previous); + declareVariable(compiler, &compiler->parser->previous); int index = findBuiltinModule( (char *)compiler->parser->previous.start, diff --git a/c/vm.c b/c/vm.c index b160ef60..8defb7b4 100644 --- a/c/vm.c +++ b/c/vm.c @@ -1104,6 +1104,7 @@ static InterpretResult run(VM *vm) { if (tableGet(&vm->modules, fileName, &moduleVal)) { ++vm->scriptNameCount; vm->lastModule = AS_MODULE(moduleVal); + push(vm, NIL_VAL); DISPATCH(); } @@ -1131,7 +1132,6 @@ static InterpretResult run(VM *vm) { push(vm, OBJ_VAL(function)); ObjClosure *closure = newClosure(vm, function); pop(vm); - push(vm, OBJ_VAL(closure)); frame->ip = ip; diff --git a/tests/modules/functions.du b/tests/modules/functions.du index f4cb231e..d09d543c 100644 --- a/tests/modules/functions.du +++ b/tests/modules/functions.du @@ -39,3 +39,26 @@ assert(ArrowFuncModule.func9() == 30); assert(ArrowFuncModule.func9(10, 20) == 30); assert(ArrowFuncModule.func9(1.5, 2.5) == 4); assert(ArrowFuncModule.func9("Dictu ", "is great!") == "Dictu is great!"); + +/** + * Nested scope + */ +{ + { + import "tests/functions/arrow.du" as NestedArrowFuncModule; + assert(type(NestedArrowFuncModule.func) == "function"); + assert(NestedArrowFuncModule.func() == 10); + + assert(type(NestedArrowFuncModule.func9) == "function"); + assert(NestedArrowFuncModule.func9() == 30); + assert(NestedArrowFuncModule.func9(10, 20) == 30); + assert(NestedArrowFuncModule.func9(1.5, 2.5) == 4); + assert(NestedArrowFuncModule.func9("Dictu ", "is great!") == "Dictu is great!"); + { + { + import JSON; + assert(JSON.parse("10") == 10); + } + } + } +} \ No newline at end of file diff --git a/tests/modules/variables.du b/tests/modules/variables.du index f055f002..79312a68 100644 --- a/tests/modules/variables.du +++ b/tests/modules/variables.du @@ -5,6 +5,13 @@ */ import "tests/variables/scope.du" as ScopeModule; - assert(ScopeModule.x == 10); +{ + import "tests/variables/scope.du" as AnotherScopeModule; + assert(AnotherScopeModule.x == 10); + { + import "tests/variables/scope.du" as MoreScopeModule; + assert(MoreScopeModule.x == 10); + } +} From eb9609b1db7d843c55fcede7a4cc016d4eb7de86 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 27 Oct 2020 00:03:17 +0000 Subject: [PATCH 14/45] Correct capacity mask for shrinking sets --- c/value.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/value.c b/c/value.c index 5de25d51..e7433ff0 100644 --- a/c/value.c +++ b/c/value.c @@ -318,7 +318,7 @@ bool setDelete(VM *vm, ObjSet *set, Value value) { if (set->count - 1 < set->capacityMask * TABLE_MIN_LOAD) { // Figure out the new table size. - int capacityMask = SHRINK_CAPACITY(set->capacityMask); + int capacityMask = SHRINK_CAPACITY(set->capacityMask + 1) - 1; adjustSetCapacity(vm, set, capacityMask); } From aec58e42aacd28b651caa73e71109c7fff3e6ce3 Mon Sep 17 00:00:00 2001 From: Ajoe-T Date: Tue, 27 Oct 2020 12:58:06 +0530 Subject: [PATCH 15/45] Removed instance variable from child class As inheritance takes care of making the parent class methods accessible in child class, removed the instance variable from child class --- examples/inheritance.du | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/inheritance.du b/examples/inheritance.du index 8cefbc7f..86008f16 100644 --- a/examples/inheritance.du +++ b/examples/inheritance.du @@ -1,11 +1,9 @@ /** * inheritance.du -* */ // Parent class - Animal class Animal { - eating() { print("Eating"); } @@ -13,11 +11,6 @@ class Animal { // Child class - Dog class Dog < Animal { - - init() { - this.eating = super.eating; - } - bark() { print("Barking"); } @@ -25,11 +18,6 @@ class Dog < Animal { // Child class - Cat class Cat < Animal { - - init() { - this.eating = super.eating; - } - meow() { print("Meowing"); } From ea6cbc78142eaeb750d7af1f3275ae03a24b14f4 Mon Sep 17 00:00:00 2001 From: Ajoe-T Date: Tue, 27 Oct 2020 15:48:28 +0530 Subject: [PATCH 16/45] Added factory methods design pattern --- examples/design-patterns/factoryMethod.du | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 examples/design-patterns/factoryMethod.du diff --git a/examples/design-patterns/factoryMethod.du b/examples/design-patterns/factoryMethod.du new file mode 100644 index 00000000..496638d8 --- /dev/null +++ b/examples/design-patterns/factoryMethod.du @@ -0,0 +1,35 @@ +class Factory { + /** + * Factory Function that are used to create IceCream object + */ + static createIceCream() { + return IceCream(); + } + + /** + * Factory Function that are used to create Chocolate object + */ + static createChocolate() { + return Chocolate(); + } +} + +class IceCream { + order() { + print("IceCream ordered"); + } +} + +class Chocolate { + order() { + print("Chocolate ordered"); + } +} + +// Factory methods are used to create objects without having to specify the exact class +var factory = Factory(); +var icecream = factory.createIceCream(); +var chocolate = factory.createChocolate(); + +icecream.order(); +chocolate.order(); From 74db6f5b3ae02e32956bb868c49f16b311d1ff1a Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 27 Oct 2020 23:24:55 +0000 Subject: [PATCH 17/45] Fix from imports within a local scope --- c/compiler.c | 46 ++++++++++++++++++++++++++++++++-------------- c/vm.c | 1 + 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/c/compiler.c b/c/compiler.c index cd5384d2..bbd3c325 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -1716,15 +1716,17 @@ static void fromImportStatement(Compiler *compiler) { compiler->parser->previous.length - 2))); consume(compiler, TOKEN_IMPORT, "Expect 'import' after import path."); - emitBytes(compiler, OP_IMPORT, importConstant); - + emitByte(compiler, OP_POP); uint8_t variables[255]; + Token tokens[255]; int varCount = 0; do { - variables[varCount] = parseVariable(compiler, "Expect variable name.", false); + consume(compiler, TOKEN_IDENTIFIER, "Expect variable name."); + tokens[varCount] = compiler->parser->previous; + variables[varCount] = identifierConstant(compiler, &compiler->parser->previous); varCount++; if (varCount > 255) { @@ -1740,30 +1742,41 @@ static void fromImportStatement(Compiler *compiler) { // This needs to be two separate loops as we need // all the variables popped before defining. - for (int i = varCount - 1; i >= 0; --i) { - defineVariable(compiler, variables[i], false); + if (compiler->scopeDepth == 0) { + for (int i = varCount - 1; i >= 0; --i) { + defineVariable(compiler, variables[i], false); + } + } else { + for (int i = 0; i < varCount; ++i) { + declareVariable(compiler, &tokens[i]); + defineVariable(compiler, 0, false); + } } emitByte(compiler, OP_IMPORT_END); } else { - uint8_t moduleName = parseVariable(compiler, "Expect import identifier.", false); + consume(compiler, TOKEN_IDENTIFIER, "Expect import identifier."); + uint8_t importName = identifierConstant(compiler, &compiler->parser->previous); int index = findBuiltinModule( (char *)compiler->parser->previous.start, compiler->parser->previous.length ); - consume(compiler, TOKEN_IMPORT, "Expect 'import' after identifier."); + consume(compiler, TOKEN_IMPORT, "Expect 'import' after identifier"); if (index == -1) { - error(compiler->parser, "Unknown module1"); + error(compiler->parser, "Unknown module"); } uint8_t variables[255]; + Token tokens[255]; int varCount = 0; do { - variables[varCount] = parseVariable(compiler, "Expect variable name.", false); + consume(compiler, TOKEN_IDENTIFIER, "Expect variable name."); + tokens[varCount] = compiler->parser->previous; + variables[varCount] = identifierConstant(compiler, &compiler->parser->previous); varCount++; if (varCount > 255) { @@ -1772,16 +1785,21 @@ static void fromImportStatement(Compiler *compiler) { } while (match(compiler, TOKEN_COMMA)); emitBytes(compiler, OP_IMPORT_BUILTIN_VARIABLE, index); - emitBytes(compiler, moduleName, varCount); + emitBytes(compiler, importName, varCount); for (int i = 0; i < varCount; ++i) { emitByte(compiler, variables[i]); } - // This needs to be two separate loops as we need - // all the variables popped before defining. - for (int i = varCount - 1; i >= 0; --i) { - defineVariable(compiler, variables[i], false); + if (compiler->scopeDepth == 0) { + for (int i = varCount - 1; i >= 0; --i) { + defineVariable(compiler, variables[i], false); + } + } else { + for (int i = 0; i < varCount; ++i) { + declareVariable(compiler, &tokens[i]); + defineVariable(compiler, 0, false); + } } } diff --git a/c/vm.c b/c/vm.c index 2ff55a75..54d1f02a 100644 --- a/c/vm.c +++ b/c/vm.c @@ -1171,6 +1171,7 @@ static InterpretResult run(VM *vm) { int index = READ_BYTE(); ObjString *fileName = READ_STRING(); int varCount = READ_BYTE(); + Value moduleVal; ObjModule *module; From 8a88ae5990a80f9edd0bd69d49434f951c1e7564 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 27 Oct 2020 23:32:49 +0000 Subject: [PATCH 18/45] Add tests and docs for from import --- docs/docs/modules.md | 25 +++++++++++++++++++++++-- tests/modules/from.du | 31 +++++++++++++++++++++++++++++++ tests/modules/import.du | 3 ++- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 tests/modules/from.du diff --git a/docs/docs/modules.md b/docs/docs/modules.md index acb130ca..324b6ae9 100644 --- a/docs/docs/modules.md +++ b/docs/docs/modules.md @@ -33,6 +33,18 @@ import Math; Math.PI; // 3.14... ``` +If however you wish to import something specific from the module into the current scope you can use a `from` import, this +accepts a single identifier or multiple separated by a comma. + +```cs +from Math import PI; + +PI; // 3.14... + +// Import multiple things +from JSON import parse, stringify; +``` + #### User created scripts When you wish to import another Dictu script, you use the import keyword. This takes an optional identifier which will be the @@ -47,7 +59,7 @@ import "some/file.du"; import "some/file.du" as SomeModule; ``` -When importing a module with an identifer, you can access the top level module variables using the `.` operator, much +When importing a module with an identifier, you can access the top level module variables using the `.` operator, much like you would for a class. **some/file.du** @@ -69,4 +81,13 @@ print(SomeModule.test()); // "test" Once an module has been imported, it is stored within the VM, and is not executed again even if it is imported elsewhere. What happens is the module is "loaded" again, which means if it was to change in the time from import, it will not be changed -within your program even if re-importing the module. \ No newline at end of file +within your program even if re-importing the module. + +Same as importing specific items from a builtin module, `from` imports also work on user created files. + +```cs +from "some/file.du" import x, test; + +print(x); // 10 +print(test()); // "test" +``` \ No newline at end of file diff --git a/tests/modules/from.du b/tests/modules/from.du new file mode 100644 index 00000000..b61cd9ba --- /dev/null +++ b/tests/modules/from.du @@ -0,0 +1,31 @@ +/** + * from.du + * + * Testing from imports + */ + +from "tests/variables/scope.du" import x; +assert(x == 10); + +{ + from "tests/variables/scope.du" import x; + assert(x == 10); + { + from "tests/variables/scope.du" import x; + assert(x == 10); + } +} + + +from JSON import parse; +assert(parse("10") == 10); + +{ + from JSON import parse; + assert(parse("10") == 10); + + { + from Math import PI; + assert(PI == 3.14159265358979); + } +} \ No newline at end of file diff --git a/tests/modules/import.du b/tests/modules/import.du index f249b244..61e2c6b1 100644 --- a/tests/modules/import.du +++ b/tests/modules/import.du @@ -6,4 +6,5 @@ import "tests/modules/variables.du"; import "tests/modules/functions.du"; -import "tests/modules/classes.du"; \ No newline at end of file +import "tests/modules/classes.du"; +import "tests/modules/from.du"; \ No newline at end of file From 98173cdb97464cf396e20d31dd436a399873c36e Mon Sep 17 00:00:00 2001 From: tglide Date: Wed, 28 Oct 2020 14:38:09 +0000 Subject: [PATCH 19/45] feat(examples): add bubbleSort.du --- examples/bubbleSort.du | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/bubbleSort.du diff --git a/examples/bubbleSort.du b/examples/bubbleSort.du new file mode 100644 index 00000000..aad19dab --- /dev/null +++ b/examples/bubbleSort.du @@ -0,0 +1,18 @@ +def bubbleSort(list) { + var sortedList = list.copy(); + + for (var i = 0; i < sortedList.len(); ++i) { + for (var j = 0; j < sortedList.len() - 1; ++j) { + if (sortedList[j] > sortedList[j+1]) { + var temp = sortedList[j+1]; + sortedList[j+1] = sortedList[j]; + sortedList[j] = temp; + } + } + } + + return sortedList; +} + +print(bubbleSort([2,1,3,4,10, 5, 6, 5, 100, -1])); + From 0c7b39642ce88be2680c0ffb37fc922f6a1c0b04 Mon Sep 17 00:00:00 2001 From: tglide Date: Wed, 28 Oct 2020 14:39:05 +0000 Subject: [PATCH 20/45] feat(examples): add comment to bubbleSort --- examples/bubbleSort.du | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/bubbleSort.du b/examples/bubbleSort.du index aad19dab..70dd08fb 100644 --- a/examples/bubbleSort.du +++ b/examples/bubbleSort.du @@ -14,5 +14,4 @@ def bubbleSort(list) { return sortedList; } -print(bubbleSort([2,1,3,4,10, 5, 6, 5, 100, -1])); - +print(bubbleSort([2, 1, 3, 4, 10, 5, 6, 5, 100, -1])); // [-1, 1, 2, 3, 4, 5, 5, 6, 10, 100] From d37d85ba0f988563dd1c29d33f30b4bdf47cd452 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 18:56:16 +0000 Subject: [PATCH 21/45] Rename module creation functions --- c/optionals/c.c | 2 +- c/optionals/c.h | 2 +- c/optionals/datetime.c | 3 +-- c/optionals/datetime.h | 2 +- c/optionals/env.c | 2 +- c/optionals/env.h | 2 +- c/optionals/http.c | 2 +- c/optionals/http.h | 2 +- c/optionals/json.c | 2 +- c/optionals/json.h | 2 +- c/optionals/math.c | 2 +- c/optionals/math.h | 2 +- c/optionals/optionals.c | 16 ++++++++-------- c/optionals/path.c | 2 +- c/optionals/path.h | 2 +- c/optionals/random.c | 2 +- c/optionals/random.h | 2 +- c/optionals/socket.c | 2 +- c/optionals/socket.h | 2 +- c/optionals/system.c | 2 +- c/optionals/system.h | 2 +- c/vm.c | 6 +++--- 22 files changed, 31 insertions(+), 32 deletions(-) diff --git a/c/optionals/c.c b/c/optionals/c.c index f971341a..0e918334 100644 --- a/c/optionals/c.c +++ b/c/optionals/c.c @@ -49,7 +49,7 @@ Value strerrorNative(VM *vm, int argCount, Value *args) { return strerrorGeneric(vm, error); } -void createCClass(VM *vm) { +void createCModule(VM *vm) { ObjString *name = copyString(vm, "C", 1); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/c.h b/c/optionals/c.h index fa9c272c..b085d719 100644 --- a/c/optionals/c.h +++ b/c/optionals/c.h @@ -35,7 +35,7 @@ #include "../vm.h" #include "../memory.h" -void createCClass(VM *vm); +void createCModule(VM *vm); #define MAX_ERROR_LEN 256 Value strerrorGeneric(VM *, int); diff --git a/c/optionals/datetime.c b/c/optionals/datetime.c index fb0030cd..37882f76 100644 --- a/c/optionals/datetime.c +++ b/c/optionals/datetime.c @@ -1,4 +1,3 @@ -#include #include #include "datetime.h" @@ -157,7 +156,7 @@ static Value strptimeNative(VM *vm, int argCount, Value *args) { } #endif -ObjModule *createDatetimeClass(VM *vm) { +ObjModule *createDatetimeModule(VM *vm) { ObjString *name = copyString(vm, "Datetime", 8); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/datetime.h b/c/optionals/datetime.h index 20a7fc89..e27b6b3c 100644 --- a/c/optionals/datetime.h +++ b/c/optionals/datetime.h @@ -8,6 +8,6 @@ #include "optionals.h" -ObjModule *createDatetimeClass(VM *vm); +ObjModule *createDatetimeModule(VM *vm); #endif //dictu_datetime_h diff --git a/c/optionals/env.c b/c/optionals/env.c index ed85b911..e50210e0 100644 --- a/c/optionals/env.c +++ b/c/optionals/env.c @@ -68,7 +68,7 @@ static Value set(VM *vm, int argCount, Value *args) { return NUMBER_VAL(retval == 0 ? OK : NOTOK); } -ObjModule *createEnvClass(VM *vm) { +ObjModule *createEnvModule(VM *vm) { ObjString *name = copyString(vm, "Env", 3); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/env.h b/c/optionals/env.h index f1b5e329..f836ac86 100644 --- a/c/optionals/env.h +++ b/c/optionals/env.h @@ -7,6 +7,6 @@ #include "optionals.h" #include "../vm.h" -ObjModule *createEnvClass(VM *vm); +ObjModule *createEnvModule(VM *vm); #endif //dictu_env_h diff --git a/c/optionals/http.c b/c/optionals/http.c index 6164beeb..17827e20 100644 --- a/c/optionals/http.c +++ b/c/optionals/http.c @@ -313,7 +313,7 @@ static Value post(VM *vm, int argCount, Value *args) { return NIL_VAL; } -ObjModule *createHTTPClass(VM *vm) { +ObjModule *createHTTPModule(VM *vm) { ObjString *name = copyString(vm, "HTTP", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/http.h b/c/optionals/http.h index d6b42f12..26806449 100644 --- a/c/optionals/http.h +++ b/c/optionals/http.h @@ -16,6 +16,6 @@ typedef struct response { long statusCode; } Response; -ObjModule *createHTTPClass(VM *vm); +ObjModule *createHTTPModule(VM *vm); #endif //dictu_http_h \ No newline at end of file diff --git a/c/optionals/json.c b/c/optionals/json.c index c1954e53..b0135a45 100644 --- a/c/optionals/json.c +++ b/c/optionals/json.c @@ -263,7 +263,7 @@ static Value stringify(VM *vm, int argCount, Value *args) { return OBJ_VAL(string); } -ObjModule *createJSONClass(VM *vm) { +ObjModule *createJSONModule(VM *vm) { ObjString *name = copyString(vm, "JSON", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/json.h b/c/optionals/json.h index 87b6424f..667316be 100644 --- a/c/optionals/json.h +++ b/c/optionals/json.h @@ -6,6 +6,6 @@ #include "optionals.h" #include "../vm.h" -ObjModule *createJSONClass(VM *vm); +ObjModule *createJSONModule(VM *vm); #endif //dictu_json_h diff --git a/c/optionals/math.c b/c/optionals/math.c index fa177414..91556370 100644 --- a/c/optionals/math.c +++ b/c/optionals/math.c @@ -221,7 +221,7 @@ static Value tanNative(VM *vm, int argCount, Value *args) { return NUMBER_VAL(tan(AS_NUMBER(args[0]))); } -ObjModule *createMathsClass(VM *vm) { +ObjModule *createMathsModule(VM *vm) { ObjString *name = copyString(vm, "Math", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/math.h b/c/optionals/math.h index ececd1a0..0c7dc08a 100644 --- a/c/optionals/math.h +++ b/c/optionals/math.h @@ -6,6 +6,6 @@ #include "optionals.h" #include "../vm.h" -ObjModule *createMathsClass(VM *vm); +ObjModule *createMathsModule(VM *vm); #endif //dictu_math_h diff --git a/c/optionals/optionals.c b/c/optionals/optionals.c index 9f30f994..7c152ba6 100644 --- a/c/optionals/optionals.c +++ b/c/optionals/optionals.c @@ -1,15 +1,15 @@ #include "optionals.h" BuiltinModules modules[] = { - {"Math", &createMathsClass}, - {"Env", &createEnvClass}, - {"JSON", &createJSONClass}, - {"Path", &createPathClass}, - {"Datetime", &createDatetimeClass}, - {"Socket", &createSocketClass}, - {"Random", &createRandomClass}, + {"Math", &createMathsModule}, + {"Env", &createEnvModule}, + {"JSON", &createJSONModule}, + {"Path", &createPathModule}, + {"Datetime", &createDatetimeModule}, + {"Socket", &createSocketModule}, + {"Random", &createRandomModule}, #ifndef DISABLE_HTTP - {"HTTP", &createHTTPClass}, + {"HTTP", &createHTTPModule}, #endif {NULL, NULL} }; diff --git a/c/optionals/path.c b/c/optionals/path.c index a92bb0c4..12a80504 100644 --- a/c/optionals/path.c +++ b/c/optionals/path.c @@ -269,7 +269,7 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { return OBJ_VAL(dir_contents); } -ObjModule *createPathClass(VM *vm) { +ObjModule *createPathModule(VM *vm) { ObjString *name = copyString(vm, "Path", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/path.h b/c/optionals/path.h index a879ee62..07eb3e0a 100644 --- a/c/optionals/path.h +++ b/c/optionals/path.h @@ -41,6 +41,6 @@ #include "../vm.h" #include "../memory.h" -ObjModule *createPathClass(VM *vm); +ObjModule *createPathModule(VM *vm); #endif //dictu_path_h diff --git a/c/optionals/random.c b/c/optionals/random.c index ae9da8ad..99b9a263 100644 --- a/c/optionals/random.c +++ b/c/optionals/random.c @@ -67,7 +67,7 @@ static Value randomSelect(VM *vm, int argCount, Value *args) return args[index]; } -ObjModule *createRandomClass(VM *vm) +ObjModule *createRandomModule(VM *vm) { ObjString *name = copyString(vm, "Random", 6); push(vm, OBJ_VAL(name)); diff --git a/c/optionals/random.h b/c/optionals/random.h index be86105e..176adf15 100644 --- a/c/optionals/random.h +++ b/c/optionals/random.h @@ -7,6 +7,6 @@ #include "optionals.h" #include "../vm.h" -ObjModule *createRandomClass(VM *vm); +ObjModule *createRandomModule(VM *vm); #endif //dictu_random_h diff --git a/c/optionals/socket.c b/c/optionals/socket.c index 54910588..706a0ee0 100644 --- a/c/optionals/socket.c +++ b/c/optionals/socket.c @@ -223,7 +223,7 @@ static Value setSocketOpt(VM *vm, int argCount, Value *args) { return TRUE_VAL; } -ObjModule *createSocketClass(VM *vm) { +ObjModule *createSocketModule(VM *vm) { ObjString *name = copyString(vm, "Socket", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/socket.h b/c/optionals/socket.h index 4efd2164..062cc135 100644 --- a/c/optionals/socket.h +++ b/c/optionals/socket.h @@ -10,6 +10,6 @@ #include #endif -ObjModule *createSocketClass(VM *vm); +ObjModule *createSocketModule(VM *vm); #endif //dictu_socket_h diff --git a/c/optionals/system.c b/c/optionals/system.c index 3a2cc342..e61ea046 100644 --- a/c/optionals/system.c +++ b/c/optionals/system.c @@ -307,7 +307,7 @@ void initPlatform(VM *vm, Table *table) { #endif } -void createSystemClass(VM *vm, int argc, const char *argv[]) { +void createSystemModule(VM *vm, int argc, const char *argv[]) { ObjString *name = copyString(vm, "System", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); diff --git a/c/optionals/system.h b/c/optionals/system.h index 47d0f30b..54ddce20 100644 --- a/c/optionals/system.h +++ b/c/optionals/system.h @@ -24,6 +24,6 @@ #include "../vm.h" #include "../memory.h" -void createSystemClass(VM *vm, int argc, const char *argv[]); +void createSystemModule(VM *vm, int argc, const char *argv[]); #endif //dictu_system_h diff --git a/c/vm.c b/c/vm.c index 54d1f02a..43fc4bac 100644 --- a/c/vm.c +++ b/c/vm.c @@ -145,10 +145,10 @@ VM *initVM(bool repl, const char *scriptName, int argc, const char *argv[]) { /** * Native classes which are not required to be - * imported. For imported natives see optionals.c + * imported. For imported modules see optionals.c */ - createSystemClass(vm, argc, argv); - createCClass(vm); + createSystemModule(vm, argc, argv); + createCModule(vm); return vm; } From e75d396cb570938df5c3add6ff6c4a1744c89425 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 20:57:18 +0000 Subject: [PATCH 22/45] Remove uses of malloc in strings.c Note there is still a use of malloc + free since valueToString hasn't been updated --- c/datatypes/strings.c | 70 ++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/c/datatypes/strings.c b/c/datatypes/strings.c index 0fe69f33..e99c507a 100644 --- a/c/datatypes/strings.c +++ b/c/datatypes/strings.c @@ -1,5 +1,4 @@ #include "strings.h" -#include "../vm.h" #include "../memory.h" @@ -40,7 +39,7 @@ static Value formatString(VM *vm, int argCount, Value *args) { } int length = 0; - char **replaceStrings = malloc(argCount * sizeof(char *)); + char **replaceStrings = ALLOCATE(vm, char *, argCount); for (int j = 1; j < argCount + 1; j++) { Value value = args[j]; @@ -59,7 +58,7 @@ static Value formatString(VM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int stringLen = string->length + 1; - char *tmp = malloc(stringLen); + char *tmp = ALLOCATE(vm, char, length); char *tmpFree = tmp; memcpy(tmp, string->chars, stringLen); @@ -78,14 +77,14 @@ static Value formatString(VM *vm, int argCount, Value *args) { free(replaceStrings[i]); } - free(tmp); - free(replaceStrings); + FREE_ARRAY(vm, char, tmp, length); + FREE_ARRAY(vm, char *, replaceStrings, argCount); return EMPTY_VAL; } int fullLength = string->length - count * 2 + length + 1; char *pos; - char *newStr = malloc(sizeof(char) * fullLength); + char *newStr = ALLOCATE(vm, char, fullLength); int stringLength = 0; for (int i = 0; i < argCount; ++i) { @@ -102,12 +101,12 @@ static Value formatString(VM *vm, int argCount, Value *args) { free(replaceStrings[i]); } - free(replaceStrings); + FREE_ARRAY(vm, char *, replaceStrings, argCount); memcpy(newStr + stringLength, tmp, strlen(tmp)); - ObjString *newString = copyString(vm, newStr, fullLength - 1); + newStr[fullLength] = '\0'; + ObjString *newString = takeString(vm, newStr, fullLength - 1); - free(newStr); - free(tmpFree); + FREE_ARRAY(vm, char, tmpFree, length); return OBJ_VAL(newString); } @@ -125,9 +124,9 @@ static Value splitString(VM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); char *delimiter = AS_CSTRING(args[1]); - char *tmp = malloc(string->length + 1); + char *tmp = ALLOCATE(vm, char, string->length + 1); char *tmpFree = tmp; - memcpy(tmp, string->chars, string->length + 1); + memcpy(tmp, string->chars, string->length); tmp[string->length] = '\0'; int delimiterLength = strlen(delimiter); char *token; @@ -162,7 +161,7 @@ static Value splitString(VM *vm, int argCount, Value *args) { } pop(vm); - free(tmpFree); + FREE_ARRAY(vm, char, tmpFree, string->length + 1); return OBJ_VAL(list); } @@ -251,9 +250,10 @@ static Value replaceString(VM *vm, int argCount, Value *args) { int stringLen = strlen(string) + 1; // Make a copy of the string so we do not modify the original - char *tmp = malloc(stringLen); + char *tmp = ALLOCATE(vm, char, stringLen + 1); char *tmpFree = tmp; memcpy(tmp, string, stringLen); + tmp[stringLen] = '\0'; // Count the occurrences of the needle so we can determine the size // of the string we need to allocate @@ -266,13 +266,13 @@ static Value replaceString(VM *vm, int argCount, Value *args) { tmp = tmpFree; if (count == 0) { - free(tmpFree); + FREE_ARRAY(vm, char, tmpFree, stringLen + 1); return stringValue; } int length = strlen(tmp) - count * (len - replaceLen) + 1; char *pos; - char *newStr = malloc(sizeof(char) * length); + char *newStr = ALLOCATE(vm, char, length); int stringLength = 0; for (int i = 0; i < count; ++i) { @@ -288,10 +288,9 @@ static Value replaceString(VM *vm, int argCount, Value *args) { } memcpy(newStr + stringLength, tmp, strlen(tmp)); - ObjString *newString = copyString(vm, newStr, length - 1); + ObjString *newString = takeString(vm, newStr, length - 1); - free(newStr); - free(tmpFree); + FREE_ARRAY(vm, char, tmpFree, stringLen + 1); return OBJ_VAL(newString); } @@ -302,17 +301,14 @@ static Value lowerString(VM *vm, int argCount, Value *args) { } ObjString *string = AS_STRING(args[0]); - - char *temp = malloc(sizeof(char) * (string->length + 1)); + char *temp = ALLOCATE(vm, char, string->length + 1); for (int i = 0; string->chars[i]; i++) { temp[i] = tolower(string->chars[i]); } temp[string->length] = '\0'; - Value ret = OBJ_VAL(copyString(vm, temp, string->length)); - free(temp); - return ret; + return OBJ_VAL(takeString(vm, temp, string->length)); } static Value upperString(VM *vm, int argCount, Value *args) { @@ -322,17 +318,14 @@ static Value upperString(VM *vm, int argCount, Value *args) { } ObjString *string = AS_STRING(args[0]); - - char *temp = malloc(sizeof(char) * (string->length + 1)); + char *temp = ALLOCATE(vm, char, string->length + 1); for (int i = 0; string->chars[i]; i++) { temp[i] = toupper(string->chars[i]); } temp[string->length] = '\0'; - Value ret = OBJ_VAL(copyString(vm, temp, string->length)); - free(temp); - return ret; + return OBJ_VAL(takeString(vm, temp, string->length)); } static Value startsWithString(VM *vm, int argCount, Value *args) { @@ -381,7 +374,7 @@ static Value leftStripString(VM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int i, count = 0; - char *temp = malloc(sizeof(char) * (string->length + 1)); + char *temp = ALLOCATE(vm, char, string->length + 1); for (i = 0; i < string->length; ++i) { if (!isspace(string->chars[i])) { @@ -390,10 +383,11 @@ static Value leftStripString(VM *vm, int argCount, Value *args) { count++; } + // We need to remove the stripped chars from the count + // Will be free'd at the call of takeString + vm->bytesAllocated -= count; memcpy(temp, string->chars + count, string->length - count); - Value ret = OBJ_VAL(copyString(vm, temp, string->length - count)); - free(temp); - return ret; + return OBJ_VAL(takeString(vm, temp, string->length - count)); } static Value rightStripString(VM *vm, int argCount, Value *args) { @@ -404,7 +398,7 @@ static Value rightStripString(VM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int length; - char *temp = malloc(sizeof(char) * (string->length + 1)); + char *temp = ALLOCATE(vm, char, string->length + 1); for (length = string->length - 1; length > 0; --length) { if (!isspace(string->chars[length])) { @@ -412,10 +406,12 @@ static Value rightStripString(VM *vm, int argCount, Value *args) { } } + // We need to remove the stripped chars from the count + // Will be free'd at the call of takeString + vm->bytesAllocated -= string->length - length - 1; memcpy(temp, string->chars, length + 1); - Value ret = OBJ_VAL(copyString(vm, temp, length + 1)); - free(temp); - return ret; + temp[length + 1] = '\0'; + return OBJ_VAL(takeString(vm, temp, length + 1)); } static Value stripString(VM *vm, int argCount, Value *args) { From 33d42375d1d40576a21871f18a23c22dcbf1a43f Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 20:59:00 +0000 Subject: [PATCH 23/45] Remove use of malloc in number.c --- c/datatypes/number.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/c/datatypes/number.c b/c/datatypes/number.c index c45ecc94..2e47b2cd 100644 --- a/c/datatypes/number.c +++ b/c/datatypes/number.c @@ -1,7 +1,5 @@ #include "number.h" -#include "../vm.h" - -#include +#include "../memory.h" static Value toStringNumber(VM *vm, int argCount, Value *args) { if (argCount != 0) { @@ -12,17 +10,15 @@ static Value toStringNumber(VM *vm, int argCount, Value *args) { double number = AS_NUMBER(args[0]); int numberStringLength = snprintf(NULL, 0, "%.15g", number) + 1; - char *numberString = malloc(numberStringLength); + char *numberString = ALLOCATE(vm, char, numberStringLength); + if (numberString == NULL) { runtimeError(vm, "Memory error on toString()!"); return EMPTY_VAL; } snprintf(numberString, numberStringLength, "%.15g", number); - Value value = OBJ_VAL(copyString(vm, numberString, numberStringLength - 1)); - - free(numberString); - return value; + return OBJ_VAL(takeString(vm, numberString, numberStringLength - 1)); } void declareNumberMethods(VM *vm) { From a797d1029a5844174fa5492253b72d58d63265c7 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 21:08:49 +0000 Subject: [PATCH 24/45] Remove use of malloc in lists.c Note still a call to free since valueToString hasn't been updated --- c/datatypes/lists.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/c/datatypes/lists.c b/c/datatypes/lists.c index 04229ea5..3ec65d8b 100644 --- a/c/datatypes/lists.c +++ b/c/datatypes/lists.c @@ -223,7 +223,7 @@ static Value joinListItem(VM *vm, int argCount, Value *args) { char *output; char *fullString = NULL; - int index = 0; + int length = 0; int delimiterLength = strlen(delimiter); for (int j = 0; j < list->values.count - 1; ++j) { @@ -233,15 +233,16 @@ static Value joinListItem(VM *vm, int argCount, Value *args) { output = valueToString(list->values.values[j]); } int elementLength = strlen(output); - fullString = realloc(fullString, index + elementLength + delimiterLength + 1); - memcpy(fullString + index, output, elementLength); + fullString = GROW_ARRAY(vm, fullString, char, length, length + elementLength + delimiterLength); + + memcpy(fullString + length, output, elementLength); if (!IS_STRING(list->values.values[j])) { free(output); } - index += elementLength; - memcpy(fullString + index, delimiter, delimiterLength); - index += delimiterLength; + length += elementLength; + memcpy(fullString + length, delimiter, delimiterLength); + length += delimiterLength; } // Outside the loop as we do not want the append the delimiter on the last element @@ -252,19 +253,17 @@ static Value joinListItem(VM *vm, int argCount, Value *args) { } int elementLength = strlen(output); - fullString = realloc(fullString, index + elementLength + 1); - memcpy(fullString + index, output, elementLength); - index += elementLength; + fullString = GROW_ARRAY(vm, fullString, char, length, length + elementLength + 1); + memcpy(fullString + length, output, elementLength); + length += elementLength; - fullString[index] = '\0'; + fullString[length] = '\0'; if (!IS_STRING(list->values.values[list->values.count - 1])) { free(output); } - Value ret = OBJ_VAL(copyString(vm, fullString, index)); - free(fullString); - return ret; + return OBJ_VAL(takeString(vm, fullString, length)); } static Value copyListShallow(VM *vm, int argCount, Value *args) { From 6dd40fff9f9573ffb2074cffd9ca203cdbe6d725 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 21:11:47 +0000 Subject: [PATCH 25/45] Remove use of malloc in files.c --- c/datatypes/files.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/c/datatypes/files.c b/c/datatypes/files.c index 98454800..81bd49d9 100644 --- a/c/datatypes/files.c +++ b/c/datatypes/files.c @@ -1,5 +1,4 @@ #include "files.h" -#include "../vm.h" #include "../memory.h" static Value writeFile(VM *vm, int argCount, Value *args) { @@ -72,7 +71,7 @@ static Value readFullFile(VM *vm, int argCount, Value *args) { fseek(file->file, currentPosition, SEEK_SET); } - char *buffer = (char *) malloc(fileSize + 1); + char *buffer = ALLOCATE(vm, char, fileSize + 1); if (buffer == NULL) { runtimeError(vm, "Not enough memory to read \"%s\".\n", file->path); return EMPTY_VAL; @@ -80,16 +79,13 @@ static Value readFullFile(VM *vm, int argCount, Value *args) { size_t bytesRead = fread(buffer, sizeof(char), fileSize, file->file); if (bytesRead < fileSize && !feof(file->file)) { - free(buffer); + FREE_ARRAY(vm, char, buffer, fileSize + 1); runtimeError(vm, "Could not read file \"%s\".\n", file->path); return EMPTY_VAL; } buffer[bytesRead] = '\0'; - Value ret = OBJ_VAL(copyString(vm, buffer, bytesRead)); - - free(buffer); - return ret; + return OBJ_VAL(takeString(vm, buffer, bytesRead)); } static Value readLineFile(VM *vm, int argCount, Value *args) { From 32f38454823760af24ae0aff1ab5ef25696b2072 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 21:21:42 +0000 Subject: [PATCH 26/45] Remove use of malloc in path.c and close directory after use --- c/optionals/path.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/c/optionals/path.c b/c/optionals/path.c index 12a80504..44871c7c 100644 --- a/c/optionals/path.c +++ b/c/optionals/path.c @@ -215,7 +215,7 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { push(vm, OBJ_VAL(dir_contents)); #ifdef _WIN32 - char *searchPath = malloc(strlen(path) + 4); + char *searchPath = ALLOCATE(vm, char, strlen(path) + 4); if (searchPath == NULL) { runtimeError(vm, "Memory error on listdir()!"); return EMPTY_VAL; @@ -243,7 +243,7 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { } while (FindNextFile(dir, &file) != 0); FindClose(dir); - free(searchPath); + FREE_ARRAY(vm, char, searchPath, strlen(path) + 4); #else struct dirent *dir; DIR *d; @@ -265,6 +265,7 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { #endif pop(vm); + closedir(d); return OBJ_VAL(dir_contents); } From 2392314a5f40820a49161a8c0fec13b71f4599a8 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 21:31:55 +0000 Subject: [PATCH 27/45] Remove the use of malloc in json.c There is still a use of malloc / free in here due to valueToString not being updated yet --- c/optionals/json.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/c/optionals/json.c b/c/optionals/json.c index b0135a45..f8f126a7 100644 --- a/c/optionals/json.c +++ b/c/optionals/json.c @@ -146,7 +146,7 @@ static Value parse(VM *vm, int argCount, Value *args) { return val; } -json_value* stringifyJson(Value value) { +json_value* stringifyJson(VM *vm, Value value) { if (IS_NIL(value)) { return json_null_new(); } else if (IS_BOOL(value)) { @@ -170,7 +170,7 @@ json_value* stringifyJson(Value value) { json_value *json = json_array_new(list->values.count); for (int i = 0; i < list->values.count; i++) { - json_array_push(json, stringifyJson(list->values.values[i])); + json_array_push(json, stringifyJson(vm, list->values.values[i])); } return json; @@ -202,7 +202,7 @@ json_value* stringifyJson(Value value) { json_object_push( json, key, - stringifyJson(entry->value) + stringifyJson(vm, entry->value) ); if (!IS_STRING(entry->key)) { @@ -240,7 +240,7 @@ static Value stringify(VM *vm, int argCount, Value *args) { indent = AS_NUMBER(args[1]); } - json_value *json = stringifyJson(args[0]); + json_value *json = stringifyJson(vm, args[0]); if (json == NULL) { errno = JSON_ENOSERIAL; @@ -255,10 +255,17 @@ static Value stringify(VM *vm, int argCount, Value *args) { indent }; - char *buf = malloc(json_measure_ex(json, default_opts)); + + int length = json_measure_ex(json, default_opts); + char *buf = ALLOCATE(vm, char, length); json_serialize_ex(buf, json, default_opts); - ObjString *string = copyString(vm, buf, strlen(buf)); - free(buf); + int actualLength = strlen(buf); + ObjString *string = takeString(vm, buf, actualLength); + + // json_measure_ex can produce a length larger than the actual string returned + // so we need to cater for this case + vm->bytesAllocated -= length - actualLength - 1; + json_builder_free(json); return OBJ_VAL(string); } From 1cbae11fb4e54dc7c8f3c48b0c36faacc51c35b2 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 22:20:44 +0000 Subject: [PATCH 28/45] Remove the use of malloc in datetime.c --- c/optionals/datetime.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/c/optionals/datetime.c b/c/optionals/datetime.c index 37882f76..90e752e5 100644 --- a/c/optionals/datetime.c +++ b/c/optionals/datetime.c @@ -84,7 +84,7 @@ static Value strftimeNative(VM *vm, int argCount, Value *args) { struct tm tictoc; int len = (format->length > 128 ? format->length * 4 : 128); - char *point = malloc(len); + char *point = ALLOCATE(vm, char, len); if (point == NULL) { runtimeError(vm, "Memory error on strftime()!"); return EMPTY_VAL; @@ -98,36 +98,33 @@ static Value strftimeNative(VM *vm, int argCount, Value *args) { * there is a big enough buffer. */ - /** however is not guaranteed that 0 indicates a failure (`man strftime' says so). + /** however is not guaranteed that 0 indicates a failure (`man strftime' says so). * So we might want to catch up the eternal loop, by using a maximum iterator. */ - - ObjString *res; - int max_iterations = 8; // maximum 65536 bytes with the default 128 len, // more if the given string is > 128 int iterator = 0; while (strftime(point, sizeof(char) * len, fmt, &tictoc) == 0) { if (++iterator > max_iterations) { - res = copyString(vm, "", 0); - goto theend; + FREE_ARRAY(vm, char, point, len); + return OBJ_VAL(copyString(vm, "", 0)); } len *= 2; - point = realloc(point, len); + point = GROW_ARRAY(vm, point, char, len / 2, len); if (point == NULL) { runtimeError(vm, "Memory error on strftime()!"); return EMPTY_VAL; } } - res = copyString(vm, point, strlen(point)); + int length = strlen(point); -theend: - free(point); + // Account for the buffer created at the start + vm->bytesAllocated -= len - length - 1; - return OBJ_VAL(res); + return OBJ_VAL(takeString(vm, point, length)); } #ifdef HAS_STRPTIME From e814e39617a16e7791fb1d67aff99e35441fa038 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 22:40:32 +0000 Subject: [PATCH 29/45] Move closedir into the preprocessor if statement --- c/optionals/path.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/c/optionals/path.c b/c/optionals/path.c index 44871c7c..480b316b 100644 --- a/c/optionals/path.c +++ b/c/optionals/path.c @@ -262,10 +262,11 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { runtimeError(vm, "%s is not a path!", path); return EMPTY_VAL; } + + closedir(d); #endif pop(vm); - closedir(d); return OBJ_VAL(dir_contents); } From 04d8dbc9e17d056f199e6a199b05624b337d46bc Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 30 Oct 2020 22:56:32 +0000 Subject: [PATCH 30/45] Update format in strings.c --- c/datatypes/strings.c | 13 ++++++------- c/optionals/path.c | 5 +++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/c/datatypes/strings.c b/c/datatypes/strings.c index e99c507a..7bad623e 100644 --- a/c/datatypes/strings.c +++ b/c/datatypes/strings.c @@ -39,7 +39,7 @@ static Value formatString(VM *vm, int argCount, Value *args) { } int length = 0; - char **replaceStrings = ALLOCATE(vm, char *, argCount); + char **replaceStrings = ALLOCATE(vm, char*, argCount); for (int j = 1; j < argCount + 1; j++) { Value value = args[j]; @@ -58,7 +58,7 @@ static Value formatString(VM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); int stringLen = string->length + 1; - char *tmp = ALLOCATE(vm, char, length); + char *tmp = ALLOCATE(vm, char, stringLen); char *tmpFree = tmp; memcpy(tmp, string->chars, stringLen); @@ -77,8 +77,8 @@ static Value formatString(VM *vm, int argCount, Value *args) { free(replaceStrings[i]); } - FREE_ARRAY(vm, char, tmp, length); - FREE_ARRAY(vm, char *, replaceStrings, argCount); + FREE_ARRAY(vm, char, tmp , stringLen); + FREE_ARRAY(vm, char*, replaceStrings, argCount); return EMPTY_VAL; } @@ -101,12 +101,11 @@ static Value formatString(VM *vm, int argCount, Value *args) { free(replaceStrings[i]); } - FREE_ARRAY(vm, char *, replaceStrings, argCount); + FREE_ARRAY(vm, char*, replaceStrings, argCount); memcpy(newStr + stringLength, tmp, strlen(tmp)); - newStr[fullLength] = '\0'; ObjString *newString = takeString(vm, newStr, fullLength - 1); - FREE_ARRAY(vm, char, tmpFree, length); + FREE_ARRAY(vm, char, tmpFree, stringLen); return OBJ_VAL(newString); } diff --git a/c/optionals/path.c b/c/optionals/path.c index 480b316b..deedc9f6 100644 --- a/c/optionals/path.c +++ b/c/optionals/path.c @@ -215,7 +215,8 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { push(vm, OBJ_VAL(dir_contents)); #ifdef _WIN32 - char *searchPath = ALLOCATE(vm, char, strlen(path) + 4); + int length = strlen(path) + 4; + char *searchPath = ALLOCATE(vm, char, length); if (searchPath == NULL) { runtimeError(vm, "Memory error on listdir()!"); return EMPTY_VAL; @@ -243,7 +244,7 @@ static Value listdirNative(VM *vm, int argCount, Value *args) { } while (FindNextFile(dir, &file) != 0); FindClose(dir); - FREE_ARRAY(vm, char, searchPath, strlen(path) + 4); + FREE_ARRAY(vm, char, searchPath, length); #else struct dirent *dir; DIR *d; From 50b52eb89501a262462081a69e769fc25e061456 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 31 Oct 2020 01:10:54 +0000 Subject: [PATCH 31/45] Remove malloc in a number of places throughout --- c/compiler.c | 11 ++++++----- c/main.c | 6 +++--- c/natives.c | 17 +++++++++-------- c/optionals/http.c | 3 +-- c/util.c | 5 +++-- c/util.h | 2 +- c/vm.c | 5 +++-- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/c/compiler.c b/c/compiler.c index bbd3c325..a7ac3c58 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -672,7 +672,7 @@ static void number(Compiler *compiler, bool canAssign) { // We allocate the whole range for the worst case. // Also account for the null-byte. - char* buffer = (char *)malloc((compiler->parser->previous.length + 1) * sizeof(char)); + char* buffer = ALLOCATE(compiler->parser->vm, char, compiler->parser->previous.length + 1); char* current = buffer; // Strip it of any underscores. @@ -692,7 +692,7 @@ static void number(Compiler *compiler, bool canAssign) { emitConstant(compiler, NUMBER_VAL(value)); // Free the malloc'd buffer. - free(buffer); + FREE_ARRAY(compiler->parser->vm, char, buffer, compiler->parser->previous.length + 1); } static void or_(Compiler *compiler, bool canAssign) { @@ -781,13 +781,14 @@ static void string(Compiler *compiler, bool canAssign) { Parser *parser = compiler->parser; - char *string = malloc(sizeof(char) * parser->previous.length - 1); + char *string = ALLOCATE(parser->vm, char, parser->previous.length - 1); + memcpy(string, parser->previous.start + 1, parser->previous.length - 2); int length = parseString(string, parser->previous.length - 2); string[length] = '\0'; - emitConstant(compiler, OBJ_VAL(copyString(parser->vm, string, length))); - free(string); + emitConstant(compiler, OBJ_VAL(takeString(parser->vm, string, length))); + parser->vm->bytesAllocated -= parser->previous.length - 2 - length; } static void list(Compiler *compiler, bool canAssign) { diff --git a/c/main.c b/c/main.c index 5e9a3a0b..ee241a59 100644 --- a/c/main.c +++ b/c/main.c @@ -15,7 +15,7 @@ void cleanupSockets(void) { #endif #include "common.h" -#include "vm.h" +#include "memory.h" #include "util.h" #define VERSION "Dictu Version: 0.11.0\n" @@ -159,7 +159,7 @@ static void repl(VM *vm, int argc, const char *argv[]) { static void runFile(VM *vm, int argc, const char *argv[]) { UNUSED(argc); - char *source = readFile(argv[1]); + char *source = readFile(vm, argv[1]); if (source == NULL) { fprintf(stderr, "Could not open file \"%s\".\n", argv[1]); @@ -167,7 +167,7 @@ static void runFile(VM *vm, int argc, const char *argv[]) { } InterpretResult result = interpret(vm, source); - free(source); // [owner] + FREE_ARRAY(vm, char, source, strlen(source) + 1); if (result == INTERPRET_COMPILE_ERROR) exit(65); if (result == INTERPRET_RUNTIME_ERROR) exit(70); diff --git a/c/natives.c b/c/natives.c index c875adf6..0561b80d 100644 --- a/c/natives.c +++ b/c/natives.c @@ -97,7 +97,7 @@ static Value inputNative(VM *vm, int argCount, Value *args) { } uint64_t currentSize = 128; - char *line = malloc(currentSize); + char *line = ALLOCATE(vm, char, currentSize); if (line == NULL) { runtimeError(vm, "Memory error on input()!"); @@ -105,13 +105,14 @@ static Value inputNative(VM *vm, int argCount, Value *args) { } int c = EOF; - uint64_t i = 0; + uint64_t length = 0; while ((c = getchar()) != '\n' && c != EOF) { - line[i++] = (char) c; + line[length++] = (char) c; - if (i + 1 == currentSize) { + if (length + 1 == currentSize) { + int oldSize = currentSize; currentSize = GROW_CAPACITY(currentSize); - line = realloc(line, currentSize); + line = GROW_ARRAY(vm, line, char, oldSize, currentSize); if (line == NULL) { printf("Unable to allocate memory\n"); @@ -120,10 +121,10 @@ static Value inputNative(VM *vm, int argCount, Value *args) { } } - line[i] = '\0'; + line[length] = '\0'; - Value l = OBJ_VAL(copyString(vm, line, strlen(line))); - free(line); + Value l = OBJ_VAL(takeString(vm, line, length)); + vm->bytesAllocated -= currentSize - length - 1; return l; } diff --git a/c/optionals/http.c b/c/optionals/http.c index 17827e20..6682f5fd 100644 --- a/c/optionals/http.c +++ b/c/optionals/http.c @@ -24,8 +24,7 @@ static void createResponse(VM *vm, Response *response) { push(vm, OBJ_VAL(response->headers)); response->len = 0; - response->res = malloc(1); - response->res[0] = '\0'; + response->res = NULL; } static size_t writeResponse(char *ptr, size_t size, size_t nmemb, Response *response) { diff --git a/c/util.c b/c/util.c index 4318669c..64821066 100644 --- a/c/util.c +++ b/c/util.c @@ -3,8 +3,9 @@ #include #include "vm.h" +#include "memory.h" -char *readFile(const char *path) { +char *readFile(VM *vm, const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { return NULL; @@ -14,7 +15,7 @@ char *readFile(const char *path) { size_t fileSize = ftell(file); rewind(file); - char *buffer = (char *) malloc(fileSize + 1); + char *buffer = ALLOCATE(vm, char, fileSize + 1); if (buffer == NULL) { fprintf(stderr, "Not enough memory to read \"%s\".\n", path); exit(74); diff --git a/c/util.h b/c/util.h index 571d4b51..bd559e88 100644 --- a/c/util.h +++ b/c/util.h @@ -3,7 +3,7 @@ #include "vm.h" -char *readFile(const char *path); +char *readFile(VM *vm, const char *path); void defineNative(VM *vm, Table *table, const char *name, NativeFn function); diff --git a/c/vm.c b/c/vm.c index 43fc4bac..cef00917 100644 --- a/c/vm.c +++ b/c/vm.c @@ -1108,7 +1108,7 @@ static InterpretResult run(VM *vm) { DISPATCH(); } - char *source = readFile(fileName->chars); + char *source = readFile(vm, fileName->chars); if (source == NULL) { frame->ip = ip; @@ -1132,7 +1132,8 @@ static InterpretResult run(VM *vm) { push(vm, OBJ_VAL(module)); ObjFunction *function = compile(vm, module, source); pop(vm); - free(source); + + FREE_ARRAY(vm, char, source, strlen(source) + 1); if (function == NULL) return INTERPRET_COMPILE_ERROR; push(vm, OBJ_VAL(function)); From e5d0ac36ec98dd96e14519bf21e599540fbdf9df Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sat, 31 Oct 2020 01:25:35 +0000 Subject: [PATCH 32/45] Redo linenoise REPL in main to remove malloc calls --- c/main.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/c/main.c b/c/main.c index ee241a59..e59fa195 100644 --- a/c/main.c +++ b/c/main.c @@ -83,8 +83,9 @@ static void repl(VM *vm, int argc, const char *argv[]) { linenoiseHistoryLoad("history.txt"); while((line = linenoise(">>> ")) != NULL) { - char *fullLine = malloc(sizeof(char) * (strlen(line) + 1)); - snprintf(fullLine, strlen(line) + 1, "%s", line); + int fullLineLength = strlen(line); + char *fullLine = ALLOCATE(vm, char, fullLineLength + 1); + snprintf(fullLine, fullLineLength + 1, "%s", line); linenoiseHistoryAdd(line); linenoiseHistorySave("history.txt"); @@ -92,12 +93,14 @@ static void repl(VM *vm, int argc, const char *argv[]) { while (!replCountBraces(fullLine) || !replCountQuotes(fullLine)) { free(line); line = linenoise("... "); + int lineLength = strlen(line); if (line == NULL) { return; } - char *temp = realloc(fullLine, strlen(fullLine) + strlen(line) + 1); + + char *temp = GROW_ARRAY(vm, fullLine, char, fullLineLength, fullLineLength + lineLength); if (temp == NULL) { printf("Unable to allocate memory\n"); @@ -105,7 +108,8 @@ static void repl(VM *vm, int argc, const char *argv[]) { } fullLine = temp; - memcpy(fullLine + strlen(fullLine), line, strlen(line) + 1); + memcpy(fullLine + fullLineLength, line, lineLength); + fullLineLength += lineLength; linenoiseHistoryAdd(line); linenoiseHistorySave("history.txt"); @@ -114,7 +118,7 @@ static void repl(VM *vm, int argc, const char *argv[]) { interpret(vm, fullLine); free(line); - free(fullLine); + FREE_ARRAY(vm, char, fullLine, fullLineLength + 1); } #else #define BUFFER_SIZE 8 From f17ea4132a19ce35a73e72f179ef06757a90ba9b Mon Sep 17 00:00:00 2001 From: Sid Srinivas Date: Sat, 31 Oct 2020 06:37:44 -0700 Subject: [PATCH 33/45] feat(examples): add binarySearchTree.du --- examples/binarySearchTree.du | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/binarySearchTree.du diff --git a/examples/binarySearchTree.du b/examples/binarySearchTree.du new file mode 100644 index 00000000..9cc91975 --- /dev/null +++ b/examples/binarySearchTree.du @@ -0,0 +1,69 @@ +class treeNode { + init(key) { + this.key = key; + this.left = nil; + this.right = nil; + } + getKey() { + return this.key; + } +} + +class binarySearchTree { + init(key) { + this.root = treeNode(key); + } + insert(key) { + if (this.root == nil) { + this.root = treeNode(key); + } else { + var cur = this.root; + var prev = cur; + while (cur != nil) { + prev = cur; + if (cur.key < key) { + cur = cur.left; + } else if (cur.key > key){ + cur = cur.right; + } + } + if (prev != nil) { + if (prev.getKey() < key) { + prev.right = treeNode(key); + } else { + prev.left = treeNode(key); + } + } + } + } + search(key) { + if (this.root == nil) { + return false; + } + if (this.root.key == key) { + return true; + } + var cur = this.root; + var prev = cur; + while (cur != nil and cur.key != key) { + prev = cur; + if (cur.getKey() < key) { + cur = cur.right; + } else { + cur = cur.left; + } + } + if ( cur != nil and cur.getKey() == key) { + return true; + } + return false; + } +} + +var tree = binarySearchTree(10); +tree.insert(6); +tree.insert(9); +tree.insert(13); +tree.insert(15); +assert(tree.search(9)); +assert(not tree.search(1)); \ No newline at end of file From 8665f375cf62dcd2dbf20df83f795d969d41d8db Mon Sep 17 00:00:00 2001 From: Avinash Upadhyaya K R Date: Sat, 31 Oct 2020 22:34:53 +0530 Subject: [PATCH 34/45] Added docker installation of Dictu --- .dockerignore | 5 +++++ Docker/DictuUbuntuDockerfile | 19 +++++++++++++++++++ Docker/README.md | 27 +++++++++++++++++++++++++++ README.md | 5 +++++ 4 files changed, 56 insertions(+) create mode 100644 .dockerignore create mode 100644 Docker/DictuUbuntuDockerfile create mode 100644 Docker/README.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3ba7a43e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +examples +.github +docs +README.md +LICENSE diff --git a/Docker/DictuUbuntuDockerfile b/Docker/DictuUbuntuDockerfile new file mode 100644 index 00000000..fd1bb5b5 --- /dev/null +++ b/Docker/DictuUbuntuDockerfile @@ -0,0 +1,19 @@ +FROM ubuntu + +WORKDIR Dictu + +RUN apt update \ + && apt install -y --no-install-recommends build-essential \ + && apt-get update \ + && apt-get install -y --no-install-recommends libcurl4-gnutls-dev\ + && rm -rf /var/lib/apt/lists/* + +COPY . . + +RUN make dictu DISABLE_HTTP=1 + +RUN cp dictu /usr/bin/ \ + && rm -rf * + +CMD ["dictu"] + diff --git a/Docker/README.md b/Docker/README.md new file mode 100644 index 00000000..2893ac67 --- /dev/null +++ b/Docker/README.md @@ -0,0 +1,27 @@ +## Dictu Docker + +### Building the Docker image + +Make sure you have Docker installed on your system. Refer to [Docker installation](https://docs.docker.com/engine/install/). + +To build the Docker image of Dictu, clone the repository and change directory into `Dictu`. + +Run the following command from the **root of the project** i.e, the `Dictu` folder by default. + +```bash +$ docker build -t dictu:ubuntu -f Docker/DictuUbuntuDockerfile . +``` + +To start a REPL from this image, run - + +```bash +$ docker run -it dictu:ubuntu +``` + +> The image built can be used in other docker images as a base image for running dictu files. Make sure you have the image built locally. + +```Dockerfile +FROM dictu:ubuntu +COPY example.du . +RUN dictu example.du +``` diff --git a/README.md b/README.md index 1aac2aec..96c716e8 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,11 @@ $ make dictu DISABLE_HTTP=1 $ ./dictu examples/guessingGame.du ``` +### Docker Installation + +Refer to [Dictu Docker](https://github.com/dictu-lang/Dictu/blob/develop/Docker/README.md) + + ## Example program ```js var userInput; From d6125a18928b96c1acde5472a535fb14680b0d00 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 1 Nov 2020 02:56:55 +0000 Subject: [PATCH 35/45] Add obj.isInstance method --- c/datatypes/instance.c | 28 ++++++++++++++++++++++++++++ docs/docs/classes.md | 29 +++++++++++++++++++++++++++++ tests/classes/import.du | 3 ++- tests/classes/isInstance.du | 30 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/classes/isInstance.du diff --git a/c/datatypes/instance.c b/c/datatypes/instance.c index 5a7ce047..dc3e4100 100644 --- a/c/datatypes/instance.c +++ b/c/datatypes/instance.c @@ -88,6 +88,33 @@ static Value setAttribute(VM *vm, int argCount, Value *args) { return NIL_VAL; } +static Value isInstance(VM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "isInstance() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_CLASS(args[1])) { + runtimeError(vm, "Argument passed to isInstance() must be a class"); + return EMPTY_VAL; + } + + ObjInstance *object = AS_INSTANCE(args[0]); + + ObjClass *klass = AS_CLASS(args[1]); + ObjClass *klassToFind = object->klass; + + while (klassToFind != NULL) { + if (klass == klassToFind) { + return BOOL_VAL(true); + } + + klassToFind = klassToFind->superclass; + } + + return BOOL_VAL(false); +} + static Value copyShallow(VM *vm, int argCount, Value *args) { if (argCount != 0) { runtimeError(vm, "copy() takes no arguments (%d given)", argCount); @@ -117,6 +144,7 @@ void declareInstanceMethods(VM *vm) { defineNative(vm, &vm->instanceMethods, "hasAttribute", hasAttribute); defineNative(vm, &vm->instanceMethods, "getAttribute", getAttribute); defineNative(vm, &vm->instanceMethods, "setAttribute", setAttribute); + defineNative(vm, &vm->instanceMethods, "isInstance", isInstance); defineNative(vm, &vm->instanceMethods, "copy", copyShallow); defineNative(vm, &vm->instanceMethods, "deepCopy", copyDeep); } diff --git a/docs/docs/classes.md b/docs/docs/classes.md index 55ca4170..a9080700 100644 --- a/docs/docs/classes.md +++ b/docs/docs/classes.md @@ -440,4 +440,33 @@ myObject.obj = Test(); // Reference to a mutable datatype myNewObject = myObject.deepCopy(); myNewObject.obj.x = 100; print(myObject.obj.x); // 10 +``` + +## Checking instance types + +### instance.isInstance(class) + +Checking if an instance is of a given class is made very simple with the `isInstance` method. This method takes in a class as an +argument and returns a boolean based on whether or not the object was instantiated from the given class. Since classes can inherit other +classes, and we know subclasses have the type of their parent class, the same holds true for `isInstance()`. If the instance being checked +is passed it's parent class as an argument `isInstance()` will evaluate to true. + +```cs +class Test {} + +var obj = Test(); + +print(obj.isInstance(Test)); // true + +// Inheritance + +class Test {} +class AnotherTest < Test {} + +var testObj = Test(); +var anotherTestObj = AnotherTest(); + +testObj.isInstance(AnotherTest); // false +anotherTestObj.isInstance(AnotherTest); // true +anotherTestObj.isInstance(Test); // true ``` \ No newline at end of file diff --git a/tests/classes/import.du b/tests/classes/import.du index 2dd590c5..c99f12ca 100644 --- a/tests/classes/import.du +++ b/tests/classes/import.du @@ -13,4 +13,5 @@ import "tests/classes/copy.du"; import "tests/classes/toString.du"; import "tests/classes/abstract.du"; import "tests/classes/classVariables.du"; -import "tests/classes/parameters.du"; \ No newline at end of file +import "tests/classes/parameters.du"; +import "tests/classes/isInstance.du"; \ No newline at end of file diff --git a/tests/classes/isInstance.du b/tests/classes/isInstance.du new file mode 100644 index 00000000..82835125 --- /dev/null +++ b/tests/classes/isInstance.du @@ -0,0 +1,30 @@ +/** + * isInstance.du + * + * Testing instance.isInstance() method + * + * .isInstance() returns a boolean based on whether the object is created from a given class / parent superclass + */ + +class Test {} + +assert(Test().isInstance(Test)); + +class AnotherTest < Test {} + +assert(AnotherTest().isInstance(Test)); +assert(AnotherTest().isInstance(AnotherTest)); +assert(!Test().isInstance(AnotherTest)); + +class Unrelated {} + +assert(!Test().isInstance(Unrelated)); +assert(!AnotherTest().isInstance(Unrelated)); + + +abstract class AbstractBase {} + +class ConcreteClass < AbstractBase {} + +assert(ConcreteClass().isInstance(AbstractBase)); +assert(ConcreteClass().isInstance(ConcreteClass)); \ No newline at end of file From 0a3793081c69179585cfba676987152e464f8466 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 1 Nov 2020 02:59:38 +0000 Subject: [PATCH 36/45] Add some more tests to check for a longer inheritance chain for isInstance --- tests/classes/isInstance.du | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/classes/isInstance.du b/tests/classes/isInstance.du index 82835125..1d62551e 100644 --- a/tests/classes/isInstance.du +++ b/tests/classes/isInstance.du @@ -27,4 +27,16 @@ abstract class AbstractBase {} class ConcreteClass < AbstractBase {} assert(ConcreteClass().isInstance(AbstractBase)); -assert(ConcreteClass().isInstance(ConcreteClass)); \ No newline at end of file +assert(ConcreteClass().isInstance(ConcreteClass)); + + +class FirstClass {} +class SecondClass < FirstClass {} +class ThirdClass < SecondClass {} + +assert(FirstClass().isInstance(FirstClass)); +assert(SecondClass().isInstance(FirstClass)); +assert(SecondClass().isInstance(SecondClass)); +assert(ThirdClass().isInstance(FirstClass)); +assert(ThirdClass().isInstance(SecondClass)); +assert(ThirdClass().isInstance(ThirdClass)); \ No newline at end of file From 1a803f85eb3c39a52349aa01e0dca9e6e249782c Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Sun, 1 Nov 2020 13:13:09 +0000 Subject: [PATCH 37/45] Allow dictu to compile on alpine linux --- c/optionals/datetime.h | 4 ++++ c/optionals/http.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/c/optionals/datetime.h b/c/optionals/datetime.h index 20a7fc89..5790b28f 100644 --- a/c/optionals/datetime.h +++ b/c/optionals/datetime.h @@ -1,8 +1,12 @@ #ifndef dictu_datetime_h #define dictu_datetime_h +#ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE +#endif +#ifndef __USE_XOPEN #define __USE_XOPEN +#endif #include diff --git a/c/optionals/http.c b/c/optionals/http.c index 6164beeb..ec08a28a 100644 --- a/c/optionals/http.c +++ b/c/optionals/http.c @@ -28,7 +28,8 @@ static void createResponse(VM *vm, Response *response) { response->res[0] = '\0'; } -static size_t writeResponse(char *ptr, size_t size, size_t nmemb, Response *response) { +static size_t writeResponse(char *ptr, size_t size, size_t nmemb, void *data) { + Response *response = (Response *) data; size_t new_len = response->len + size * nmemb; response->res = GROW_ARRAY(response->vm, response->res, char, response->len, new_len + 1); if (response->res == NULL) { From 96be54952e992550160cbc3f2da90543ce591f76 Mon Sep 17 00:00:00 2001 From: Avinash Upadhyaya K R Date: Sun, 1 Nov 2020 19:40:39 +0530 Subject: [PATCH 38/45] Added test run in Docker build --- Docker/DictuUbuntuDockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Docker/DictuUbuntuDockerfile b/Docker/DictuUbuntuDockerfile index fd1bb5b5..a23f47ea 100644 --- a/Docker/DictuUbuntuDockerfile +++ b/Docker/DictuUbuntuDockerfile @@ -10,10 +10,9 @@ RUN apt update \ COPY . . -RUN make dictu DISABLE_HTTP=1 - -RUN cp dictu /usr/bin/ \ - && rm -rf * +RUN make dictu DISABLE_HTTP=1 \ + && cp dictu /usr/bin/ \ + && dictu tests/runTests.du \ + && rm -rf * CMD ["dictu"] - From df3f8d1a483d75e480b15c4c581ac7de8fa2b034 Mon Sep 17 00:00:00 2001 From: Avinash Upadhyaya K R Date: Sun, 1 Nov 2020 20:35:31 +0530 Subject: [PATCH 39/45] Added alpine docker image of dictu --- Docker/DictuAlpineDockerfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Docker/DictuAlpineDockerfile diff --git a/Docker/DictuAlpineDockerfile b/Docker/DictuAlpineDockerfile new file mode 100644 index 00000000..ca693fc9 --- /dev/null +++ b/Docker/DictuAlpineDockerfile @@ -0,0 +1,14 @@ +FROM alpine + +WORKDIR Dictu + +RUN apk add make curl-dev gcc libc-dev --no-cache + +COPY . . + +RUN make dictu \ + && cp dictu /usr/bin/ \ + && dictu tests/runTests.du \ + && rm -rf * + +CMD ["dictu"] From ef70c6d4ce169abe64dca677998265335e6aac50 Mon Sep 17 00:00:00 2001 From: Avinash Upadhyaya K R <52544819+avinashupadhya99@users.noreply.github.com> Date: Sun, 1 Nov 2020 20:38:56 +0530 Subject: [PATCH 40/45] Updated Docker README for alpine changes --- Docker/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Docker/README.md b/Docker/README.md index 2893ac67..141ba5fb 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -8,11 +8,19 @@ To build the Docker image of Dictu, clone the repository and change directory in Run the following command from the **root of the project** i.e, the `Dictu` folder by default. +To build the *Alpine* version of Dictu - + +```bash +$ docker build -t dictu:alpine -f Docker/DictuAlpineDockerfile . +``` + +To build the *Ubuntu* version of Dictu - + ```bash $ docker build -t dictu:ubuntu -f Docker/DictuUbuntuDockerfile . ``` -To start a REPL from this image, run - +To start a REPL from this image, run - (Replace the tag with the appropriate version, i.e, alpine or ubuntu) ```bash $ docker run -it dictu:ubuntu From aab3881534c8165594ed95e9fb61cc2b49ea6d24 Mon Sep 17 00:00:00 2001 From: Avinash Upadhyaya K R Date: Sun, 1 Nov 2020 22:50:09 +0530 Subject: [PATCH 41/45] Removed HTTP disabling while building ubuntu docker image --- Docker/DictuUbuntuDockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docker/DictuUbuntuDockerfile b/Docker/DictuUbuntuDockerfile index a23f47ea..9059b711 100644 --- a/Docker/DictuUbuntuDockerfile +++ b/Docker/DictuUbuntuDockerfile @@ -10,7 +10,7 @@ RUN apt update \ COPY . . -RUN make dictu DISABLE_HTTP=1 \ +RUN make dictu \ && cp dictu /usr/bin/ \ && dictu tests/runTests.du \ && rm -rf * From 5e8e082be4e19731059780f203836cfd23c9a16c Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 3 Nov 2020 23:02:04 +0000 Subject: [PATCH 42/45] Ensure all modules have errno and strerror --- c/optionals/datetime.c | 7 +++++++ c/optionals/env.c | 6 ++++++ c/optionals/math.c | 4 +++- c/optionals/path.c | 7 +++++-- c/optionals/random.c | 6 ++++++ c/optionals/socket.c | 2 +- docs/docs/datetime.md | 6 ++++++ docs/docs/json.md | 10 ++++++++++ docs/docs/math.md | 9 +++++---- docs/docs/path.md | 2 +- docs/docs/random.md | 6 ++++++ docs/docs/system.md | 2 +- 12 files changed, 57 insertions(+), 10 deletions(-) diff --git a/c/optionals/datetime.c b/c/optionals/datetime.c index 90e752e5..a01d17ef 100644 --- a/c/optionals/datetime.c +++ b/c/optionals/datetime.c @@ -162,12 +162,19 @@ ObjModule *createDatetimeModule(VM *vm) { /** * Define Datetime methods */ + defineNative(vm, &module->values, "strerror", strerrorNative); defineNative(vm, &module->values, "now", nowNative); defineNative(vm, &module->values, "nowUTC", nowUTCNative); defineNative(vm, &module->values, "strftime", strftimeNative); #ifdef HAS_STRPTIME defineNative(vm, &module->values, "strptime", strptimeNative); #endif + + /** + * Define Datetime properties + */ + defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); + pop(vm); pop(vm); diff --git a/c/optionals/env.c b/c/optionals/env.c index e50210e0..3e47f0a4 100644 --- a/c/optionals/env.c +++ b/c/optionals/env.c @@ -80,6 +80,12 @@ ObjModule *createEnvModule(VM *vm) { defineNative(vm, &module->values, "strerror", strerrorNative); defineNative(vm, &module->values, "get", get); defineNative(vm, &module->values, "set", set); + + /** + * Define Env properties + */ + defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); + pop(vm); pop(vm); diff --git a/c/optionals/math.c b/c/optionals/math.c index 91556370..468f4ae0 100644 --- a/c/optionals/math.c +++ b/c/optionals/math.c @@ -228,8 +228,9 @@ ObjModule *createMathsModule(VM *vm) { push(vm, OBJ_VAL(module)); /** - * Define Math values + * Define Math methods */ + defineNative(vm, &module->values, "strerror", strerrorNative); defineNative(vm, &module->values, "average", averageNative); defineNative(vm, &module->values, "floor", floorNative); defineNative(vm, &module->values, "round", roundNative); @@ -246,6 +247,7 @@ ObjModule *createMathsModule(VM *vm) { /** * Define Math properties */ + defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); defineNativeProperty(vm, &module->values, "PI", NUMBER_VAL(3.14159265358979)); defineNativeProperty(vm, &module->values, "e", NUMBER_VAL(2.71828182845905)); pop(vm); diff --git a/c/optionals/path.c b/c/optionals/path.c index deedc9f6..a27dc43a 100644 --- a/c/optionals/path.c +++ b/c/optionals/path.c @@ -283,9 +283,8 @@ ObjModule *createPathModule(VM *vm) { */ #ifdef HAS_REALPATH defineNative(vm, &module->values, "realpath", realpathNative); - defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); - defineNative(vm, &module->values, "strerror", strerrorNative); // only realpath uses errno #endif + defineNative(vm, &module->values, "strerror", strerrorNative); // only realpath uses errno defineNative(vm, &module->values, "isAbsolute", isAbsoluteNative); defineNative(vm, &module->values, "basename", basenameNative); defineNative(vm, &module->values, "extname", extnameNative); @@ -294,6 +293,10 @@ ObjModule *createPathModule(VM *vm) { defineNative(vm, &module->values, "isdir", isdirNative); defineNative(vm, &module->values, "listdir", listdirNative); + /** + * Define Path properties + */ + defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); defineNativeProperty(vm, &module->values, "delimiter", OBJ_VAL( copyString(vm, PATH_DELIMITER_AS_STRING, PATH_DELIMITER_STRLEN))); defineNativeProperty(vm, &module->values, "dirSeparator", OBJ_VAL( diff --git a/c/optionals/random.c b/c/optionals/random.c index 99b9a263..0d79ffcb 100644 --- a/c/optionals/random.c +++ b/c/optionals/random.c @@ -79,10 +79,16 @@ ObjModule *createRandomModule(VM *vm) /** * Define Random methods */ + defineNative(vm, &module->values, "strerror", strerrorNative); defineNative(vm, &module->values, "random", randomRandom); defineNative(vm, &module->values, "range", randomRange); defineNative(vm, &module->values, "select", randomSelect); + /** + * Define Random properties + */ + defineNativeProperty(vm, &module->values, "errno", NUMBER_VAL(0)); + pop(vm); pop(vm); diff --git a/c/optionals/socket.c b/c/optionals/socket.c index 706a0ee0..ed90a48d 100644 --- a/c/optionals/socket.c +++ b/c/optionals/socket.c @@ -245,7 +245,7 @@ ObjModule *createSocketModule(VM *vm) { defineNativeProperty(vm, &module->values, "SO_REUSEADDR", NUMBER_VAL(SO_REUSEADDR)); /** - * Setup socket object methods + * Setup Socket object methods */ defineNative(vm, &vm->socketMethods, "bind", bindSocket); defineNative(vm, &vm->socketMethods, "listen", listenSocket); diff --git a/docs/docs/datetime.md b/docs/docs/datetime.md index 93a65349..d55fe8d2 100644 --- a/docs/docs/datetime.md +++ b/docs/docs/datetime.md @@ -23,6 +23,12 @@ To make use of the Datetime module an import is required. import Datetime; ``` +### Constants + +| Constant | Description | +|----------------------|---------------------------------| +| Datetime.errno | Number of the last error | + ### Datetime.now() Returns a human readable locale datetime string. diff --git a/docs/docs/json.md b/docs/docs/json.md index ded55cfd..1dc2fd99 100644 --- a/docs/docs/json.md +++ b/docs/docs/json.md @@ -23,6 +23,16 @@ To make use of the JSON module an import is required. import JSON; ``` +### Constants + +| Constant | Description | +|----------------------|------------------------------------------------------------| +| JSON.errno | Number of the last error | +| JSON.ENULL | Error value when JSON object is nil | +| JSON.ENOTYPE | Error value when there's no corresponding data type | +| JSON.EINVAL | Error value when it's an invalid JSON Object | +| JSON.ENOSERIAL | Error value when the object is not serializable | + ### JSON.parse(string) Parses a JSON string and turns it into a valid Dictu datatype. diff --git a/docs/docs/math.md b/docs/docs/math.md index 123307ca..53aa09b4 100644 --- a/docs/docs/math.md +++ b/docs/docs/math.md @@ -26,10 +26,11 @@ import Math; ### Constants -| Constant | Description | -|-----------|--------------------------------------------------------| -| Math.PI | The mathematical constant: 3.14159265358979 | -| Math.e | The mathematical constant: 2.71828182845905 | +| Constant | Description | +|--------------|--------------------------------------------------------| +| Math.errno | Number of the last error | +| Math.PI | The mathematical constant: 3.14159265358979 | +| Math.e | The mathematical constant: 2.71828182845905 | ### Math.min(iterable) diff --git a/docs/docs/path.md b/docs/docs/path.md index b0f08ed5..38ac2900 100644 --- a/docs/docs/path.md +++ b/docs/docs/path.md @@ -27,9 +27,9 @@ import Path; | Constant | Description | |--------------------|--------------------------------------| +| Path.errno | Number of the last error | | Path.delimiter | System dependent path delimiter | | Path.dirSeparator | System dependent directory separator | -| Path.errno | Number of the last error (UNIX only) | ### Path.basename(string) diff --git a/docs/docs/random.md b/docs/docs/random.md index 34ea0a35..9b0c826d 100644 --- a/docs/docs/random.md +++ b/docs/docs/random.md @@ -24,6 +24,12 @@ To make use of the Random module an import is required. import Random; ``` +### Constants + +| Constant | Description | +|----------------------|---------------------------------| +| Random.errno | Number of the last error | + ### Random.random() Return a random float between 0 and 1. diff --git a/docs/docs/system.md b/docs/docs/system.md index 2ec78869..cdfbb289 100644 --- a/docs/docs/system.md +++ b/docs/docs/system.md @@ -21,8 +21,8 @@ nav_order: 12 | Constant | Description | |-----------------|---------------------------------------------------------------------------------------------------| -| System.argv | The list of command line arguments. The first element of the argv list is always the script name. | | System.errno | Number of the last error. | +| System.argv | The list of command line arguments. The first element of the argv list is always the script name. | | System.platform | This string identifies the underlying system platform. | | System.S_IRWXU | Read, write, and execute by owner. | | System.S_IRUSR | Read by owner. | From 0e34e6b3f79b86847f77bab2d52433561d25a6fd Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Thu, 5 Nov 2020 18:50:38 +0000 Subject: [PATCH 43/45] Produce a more coherent error message when creating an abstract method with a body --- c/compiler.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/c/compiler.c b/c/compiler.c index a7ac3c58..ca2cfba4 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -1278,6 +1278,11 @@ static void method(Compiler *compiler) { // Setup function and parse parameters beginFunction(compiler, &fnCompiler, TYPE_ABSTRACT); endCompiler(&fnCompiler); + + if (check(compiler, TOKEN_LEFT_BRACE)) { + error(compiler->parser, "Abstract methods can not have an implementation."); + return; + } } emitBytes(compiler, OP_METHOD, constant); From 72ef517d52911371e29539bc81517a4928360ac2 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Fri, 6 Nov 2020 23:57:14 +0000 Subject: [PATCH 44/45] Make scanner variable a local variable --- c/compiler.c | 15 +- c/compiler.h | 1 + c/scanner.c | 394 +++++++++++++++++++++++++-------------------------- c/scanner.h | 13 +- 4 files changed, 212 insertions(+), 211 deletions(-) diff --git a/c/compiler.c b/c/compiler.c index a7ac3c58..df5f3baa 100644 --- a/c/compiler.c +++ b/c/compiler.c @@ -48,7 +48,7 @@ static void advance(Parser *parser) { parser->previous = parser->current; for (;;) { - parser->current = scanToken(); + parser->current = scanToken(&parser->scanner); if (parser->current.type != TOKEN_ERROR) break; errorAtCurrent(parser, parser->current.start); @@ -1913,8 +1913,8 @@ static void statement(Compiler *compiler) { if (check(compiler, TOKEN_RIGHT_BRACE)) { if (check(compiler, TOKEN_SEMICOLON)) { - backTrack(); - backTrack(); + backTrack(&parser->scanner); + backTrack(&parser->scanner); parser->current = previous; expressionStatement(compiler); return; @@ -1923,7 +1923,7 @@ static void statement(Compiler *compiler) { if (check(compiler, TOKEN_COLON)) { for (int i = 0; i < parser->current.length + parser->previous.length; ++i) { - backTrack(); + backTrack(&parser->scanner); } parser->current = previous; @@ -1933,7 +1933,7 @@ static void statement(Compiler *compiler) { // Reset the scanner to the previous position for (int i = 0; i < parser->current.length; ++i) { - backTrack(); + backTrack(&parser->scanner); } // Reset the parser @@ -1957,7 +1957,10 @@ ObjFunction *compile(VM *vm, ObjModule *module, const char *source) { parser.panicMode = false; parser.module = module; - initScanner(source); + Scanner scanner; + initScanner(&scanner, source); + parser.scanner = scanner; + Compiler compiler; initCompiler(&parser, &compiler, NULL, TYPE_TOP_LEVEL); diff --git a/c/compiler.h b/c/compiler.h index 9f1185eb..45bc8314 100644 --- a/c/compiler.h +++ b/c/compiler.h @@ -68,6 +68,7 @@ typedef struct Loop { typedef struct { VM *vm; + Scanner scanner; Token current; Token previous; bool hadError; diff --git a/c/scanner.c b/c/scanner.c index 054b400b..2138669b 100644 --- a/c/scanner.c +++ b/c/scanner.c @@ -1,23 +1,13 @@ -#include #include #include "common.h" #include "scanner.h" -typedef struct { - const char *start; - const char *current; - int line; - bool rawString; -} Scanner; - -Scanner scanner; - -void initScanner(const char *source) { - scanner.start = source; - scanner.current = source; - scanner.line = 1; - scanner.rawString = false; +void initScanner(Scanner *scanner, const char *source) { + scanner->start = source; + scanner->current = source; + scanner->line = 1; + scanner->rawString = false; } static bool isAlpha(char c) { @@ -34,92 +24,92 @@ static bool isHexDigit(char c) { return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c == '_')); } -static bool isAtEnd() { - return *scanner.current == '\0'; +static bool isAtEnd(Scanner *scanner) { + return *scanner->current == '\0'; } -static char advance() { - scanner.current++; - return scanner.current[-1]; +static char advance(Scanner *scanner) { + scanner->current++; + return scanner->current[-1]; } -static char peek() { - return *scanner.current; +static char peek(Scanner *scanner) { + return *scanner->current; } -static char peekNext() { - if (isAtEnd()) return '\0'; - return scanner.current[1]; +static char peekNext(Scanner *scanner) { + if (isAtEnd(scanner)) return '\0'; + return scanner->current[1]; } -static bool match(char expected) { - if (isAtEnd()) return false; - if (*scanner.current != expected) return false; +static bool match(Scanner *scanner, char expected) { + if (isAtEnd(scanner)) return false; + if (*scanner->current != expected) return false; - scanner.current++; + scanner->current++; return true; } -static Token makeToken(TokenType type) { +static Token makeToken(Scanner *scanner, TokenType type) { Token token; token.type = type; - token.start = scanner.start; - token.length = (int) (scanner.current - scanner.start); - token.line = scanner.line; + token.start = scanner->start; + token.length = (int) (scanner->current - scanner->start); + token.line = scanner->line; return token; } -static Token errorToken(const char *message) { +static Token errorToken(Scanner *scanner, const char *message) { Token token; token.type = TOKEN_ERROR; token.start = message; token.length = (int) strlen(message); - token.line = scanner.line; + token.line = scanner->line; return token; } -static void skipWhitespace() { +static void skipWhitespace(Scanner *scanner) { for (;;) { - char c = peek(); + char c = peek(scanner); switch (c) { case ' ': case '\r': case '\t': - advance(); + advance(scanner); break; case '\n': - scanner.line++; - advance(); + scanner->line++; + advance(scanner); break; case '/': - if (peekNext() == '*') { + if (peekNext(scanner) == '*') { // Multiline comments - advance(); - advance(); + advance(scanner); + advance(scanner); while (true) { - while (peek() != '*' && !isAtEnd()) { - if ((c = advance()) == '\n') { - scanner.line++; + while (peek(scanner) != '*' && !isAtEnd(scanner)) { + if ((c = advance(scanner)) == '\n') { + scanner->line++; } } - if (isAtEnd()) + if (isAtEnd(scanner)) return; - if (peekNext() == '/') { + if (peekNext(scanner) == '/') { break; } - advance(); + advance(scanner); } - advance(); - advance(); - } else if (peekNext() == '/') { + advance(scanner); + advance(scanner); + } else if (peekNext(scanner) == '/') { // A comment goes until the end of the line. - while (peek() != '\n' && !isAtEnd()) advance(); + while (peek(scanner) != '\n' && !isAtEnd(scanner)) advance(scanner); } else { return; } @@ -131,51 +121,51 @@ static void skipWhitespace() { } } -static TokenType checkKeyword(int start, int length, +static TokenType checkKeyword(Scanner *scanner, int start, int length, const char *rest, TokenType type) { - if (scanner.current - scanner.start == start + length && - memcmp(scanner.start + start, rest, length) == 0) { + if (scanner->current - scanner->start == start + length && + memcmp(scanner->start + start, rest, length) == 0) { return type; } return TOKEN_IDENTIFIER; } -static TokenType identifierType() { - switch (scanner.start[0]) { +static TokenType identifierType(Scanner *scanner) { + switch (scanner->start[0]) { case 'a': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'b': { - return checkKeyword(2, 6, "stract", TOKEN_ABSTRACT); + return checkKeyword(scanner, 2, 6, "stract", TOKEN_ABSTRACT); } case 'n': { - return checkKeyword(2, 1, "d", TOKEN_AND); + return checkKeyword(scanner, 2, 1, "d", TOKEN_AND); } case 's': { - return checkKeyword(2, 0, "", TOKEN_AS); + return checkKeyword(scanner, 2, 0, "", TOKEN_AS); } } } break; case 'b': - return checkKeyword(1, 4, "reak", TOKEN_BREAK); + return checkKeyword(scanner, 1, 4, "reak", TOKEN_BREAK); case 'c': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'l': - return checkKeyword(2, 3, "ass", TOKEN_CLASS); + return checkKeyword(scanner, 2, 3, "ass", TOKEN_CLASS); case 'o': { // Skip second char // Skip third char - if (scanner.current - scanner.start > 3) { - switch (scanner.start[3]) { + if (scanner->current - scanner->start > 3) { + switch (scanner->start[3]) { case 't': - return checkKeyword(4, 4, "inue", TOKEN_CONTINUE); + return checkKeyword(scanner, 4, 4, "inue", TOKEN_CONTINUE); case 's': - return checkKeyword(4, 1, "t", TOKEN_CONST); + return checkKeyword(scanner, 4, 1, "t", TOKEN_CONST); } } } @@ -183,94 +173,94 @@ static TokenType identifierType() { } break; case 'd': - return checkKeyword(1, 2, "ef", TOKEN_DEF); + return checkKeyword(scanner, 1, 2, "ef", TOKEN_DEF); case 'e': - return checkKeyword(1, 3, "lse", TOKEN_ELSE); + return checkKeyword(scanner, 1, 3, "lse", TOKEN_ELSE); case 'f': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'a': - return checkKeyword(2, 3, "lse", TOKEN_FALSE); + return checkKeyword(scanner, 2, 3, "lse", TOKEN_FALSE); case 'o': - return checkKeyword(2, 1, "r", TOKEN_FOR); + return checkKeyword(scanner, 2, 1, "r", TOKEN_FOR); case 'r': - return checkKeyword(2, 2, "om", TOKEN_FROM); + return checkKeyword(scanner, 2, 2, "om", TOKEN_FROM); } } break; case 'i': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'f': - return checkKeyword(2, 0, "", TOKEN_IF); + return checkKeyword(scanner, 2, 0, "", TOKEN_IF); case 'm': - return checkKeyword(2, 4, "port", TOKEN_IMPORT); + return checkKeyword(scanner, 2, 4, "port", TOKEN_IMPORT); } } break; case 'n': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'o': - return checkKeyword(2, 1, "t", TOKEN_BANG); + return checkKeyword(scanner, 2, 1, "t", TOKEN_BANG); case 'i': - return checkKeyword(2, 1, "l", TOKEN_NIL); + return checkKeyword(scanner, 2, 1, "l", TOKEN_NIL); } } break; case 'o': - return checkKeyword(1, 1, "r", TOKEN_OR); + return checkKeyword(scanner, 1, 1, "r", TOKEN_OR); case 'r': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'e': - return checkKeyword(2, 4, "turn", TOKEN_RETURN); + return checkKeyword(scanner, 2, 4, "turn", TOKEN_RETURN); } } else { - if (scanner.start[1] == '"' || scanner.start[1] == '\'') { - scanner.rawString = true; + if (scanner->start[1] == '"' || scanner->start[1] == '\'') { + scanner->rawString = true; return TOKEN_R; } } break; case 's': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'u': - return checkKeyword(2, 3, "per", TOKEN_SUPER); + return checkKeyword(scanner, 2, 3, "per", TOKEN_SUPER); case 't': - return checkKeyword(2, 4, "atic", TOKEN_STATIC); + return checkKeyword(scanner, 2, 4, "atic", TOKEN_STATIC); } } break; case 't': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'h': - return checkKeyword(2, 2, "is", TOKEN_THIS); + return checkKeyword(scanner, 2, 2, "is", TOKEN_THIS); case 'r': - if (scanner.current - scanner.start > 2) { - switch (scanner.start[2]) { + if (scanner->current - scanner->start > 2) { + switch (scanner->start[2]) { case 'u': - return checkKeyword(3, 1, "e", TOKEN_TRUE); + return checkKeyword(scanner, 3, 1, "e", TOKEN_TRUE); case 'a': - return checkKeyword(3, 2, "it", TOKEN_TRAIT); + return checkKeyword(scanner, 3, 2, "it", TOKEN_TRAIT); } } } } break; case 'u': - return checkKeyword(1, 2, "se", TOKEN_USE); + return checkKeyword(scanner, 1, 2, "se", TOKEN_USE); case 'v': - return checkKeyword(1, 2, "ar", TOKEN_VAR); + return checkKeyword(scanner, 1, 2, "ar", TOKEN_VAR); case 'w': - if (scanner.current - scanner.start > 1) { - switch (scanner.start[1]) { + if (scanner->current - scanner->start > 1) { + switch (scanner->start[1]) { case 'h': - return checkKeyword(2, 3, "ile", TOKEN_WHILE); + return checkKeyword(scanner, 2, 3, "ile", TOKEN_WHILE); case 'i': - return checkKeyword(2, 2, "th", TOKEN_WITH); + return checkKeyword(scanner, 2, 2, "th", TOKEN_WITH); } } break; @@ -279,168 +269,168 @@ static TokenType identifierType() { return TOKEN_IDENTIFIER; } -static Token identifier() { - while (isAlpha(peek()) || isDigit(peek())) advance(); +static Token identifier(Scanner *scanner) { + while (isAlpha(peek(scanner)) || isDigit(peek(scanner))) advance(scanner); - return makeToken(identifierType()); + return makeToken(scanner,identifierType(scanner)); } -static Token exponent() { +static Token exponent(Scanner *scanner) { // Consume the "e" - advance(); - while (peek() == '_') advance(); - if (peek() == '+' || peek() == '-') { + advance(scanner); + while (peek(scanner) == '_') advance(scanner); + if (peek(scanner) == '+' || peek(scanner) == '-') { // Consume the "+ or -" - advance(); + advance(scanner); } - if (!isDigit(peek()) && peek() != '_') return errorToken("Invalid exopnent literal"); - while (isDigit(peek()) || peek() == '_') advance(); - return makeToken(TOKEN_NUMBER); + if (!isDigit(peek(scanner)) && peek(scanner) != '_') return errorToken(scanner, "Invalid exopnent literal"); + while (isDigit(peek(scanner)) || peek(scanner) == '_') advance(scanner); + return makeToken(scanner,TOKEN_NUMBER); } -static Token number() { - while (isDigit(peek()) || peek() == '_') advance(); - if (peek() == 'e' || peek() == 'E') - return exponent(); +static Token number(Scanner *scanner) { + while (isDigit(peek(scanner)) || peek(scanner) == '_') advance(scanner); + if (peek(scanner) == 'e' || peek(scanner) == 'E') + return exponent(scanner); // Look for a fractional part. - if (peek() == '.' && (isDigit(peekNext()))) { + if (peek(scanner) == '.' && (isDigit(peekNext(scanner)))) { // Consume the "." - advance(); - while (isDigit(peek()) || peek() == '_') advance(); - if (peek() == 'e' || peek() == 'E') - return exponent(); + advance(scanner); + while (isDigit(peek(scanner)) || peek(scanner) == '_') advance(scanner); + if (peek(scanner) == 'e' || peek(scanner) == 'E') + return exponent(scanner); } - return makeToken(TOKEN_NUMBER); + return makeToken(scanner,TOKEN_NUMBER); } -static Token hexNumber() { - while (peek() == '_') advance(); - if (peek() == '0')advance(); - if ((peek() == 'x') || (peek() == 'X')) { - advance(); - if (!isHexDigit(peek())) return errorToken("Invalid hex literal"); - while (isHexDigit(peek())) advance(); - return makeToken(TOKEN_NUMBER); - } else return number(); +static Token hexNumber(Scanner *scanner) { + while (peek(scanner) == '_') advance(scanner); + if (peek(scanner) == '0')advance(scanner); + if ((peek(scanner) == 'x') || (peek(scanner) == 'X')) { + advance(scanner); + if (!isHexDigit(peek(scanner))) return errorToken(scanner, "Invalid hex literal"); + while (isHexDigit(peek(scanner))) advance(scanner); + return makeToken(scanner,TOKEN_NUMBER); + } else return number(scanner); } -static Token string(char stringToken) { - while (peek() != stringToken && !isAtEnd()) { - if (peek() == '\n') { - scanner.line++; - } else if (peek() == '\\' && !scanner.rawString) { - scanner.current++; +static Token string(Scanner *scanner, char stringToken) { + while (peek(scanner) != stringToken && !isAtEnd(scanner)) { + if (peek(scanner) == '\n') { + scanner->line++; + } else if (peek(scanner) == '\\' && !scanner->rawString) { + scanner->current++; } - advance(); + advance(scanner); } - if (isAtEnd()) return errorToken("Unterminated string."); + if (isAtEnd(scanner)) return errorToken(scanner, "Unterminated string."); // The closing " or '. - advance(); - scanner.rawString = false; - return makeToken(TOKEN_STRING); + advance(scanner); + scanner->rawString = false; + return makeToken(scanner,TOKEN_STRING); } -void backTrack() { - scanner.current--; +void backTrack(Scanner *scanner) { + scanner->current--; } -Token scanToken() { - skipWhitespace(); +Token scanToken(Scanner *scanner) { + skipWhitespace(scanner); - scanner.start = scanner.current; + scanner->start = scanner->current; - if (isAtEnd()) return makeToken(TOKEN_EOF); + if (isAtEnd(scanner)) return makeToken(scanner, TOKEN_EOF); - char c = advance(); + char c = advance(scanner); - if (isAlpha(c)) return identifier(); - if (isDigit(c)) return hexNumber(); + if (isAlpha(c)) return identifier(scanner); + if (isDigit(c)) return hexNumber(scanner); switch (c) { case '(': - return makeToken(TOKEN_LEFT_PAREN); + return makeToken(scanner, TOKEN_LEFT_PAREN); case ')': - return makeToken(TOKEN_RIGHT_PAREN); + return makeToken(scanner, TOKEN_RIGHT_PAREN); case '{': - return makeToken(TOKEN_LEFT_BRACE); + return makeToken(scanner, TOKEN_LEFT_BRACE); case '}': - return makeToken(TOKEN_RIGHT_BRACE); + return makeToken(scanner, TOKEN_RIGHT_BRACE); case '[': - return makeToken(TOKEN_LEFT_BRACKET); + return makeToken(scanner, TOKEN_LEFT_BRACKET); case ']': - return makeToken(TOKEN_RIGHT_BRACKET); + return makeToken(scanner, TOKEN_RIGHT_BRACKET); case ';': - return makeToken(TOKEN_SEMICOLON); + return makeToken(scanner, TOKEN_SEMICOLON); case ':': - return makeToken(TOKEN_COLON); + return makeToken(scanner, TOKEN_COLON); case ',': - return makeToken(TOKEN_COMMA); + return makeToken(scanner, TOKEN_COMMA); case '.': - return makeToken(TOKEN_DOT); + return makeToken(scanner, TOKEN_DOT); case '/': { - if (match('=')) { - return makeToken(TOKEN_DIVIDE_EQUALS); + if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_DIVIDE_EQUALS); } else { - return makeToken(TOKEN_SLASH); + return makeToken(scanner, TOKEN_SLASH); } } case '*': { - if (match('=')) { - return makeToken(TOKEN_MULTIPLY_EQUALS); - } else if (match('*')) { - return makeToken(TOKEN_STAR_STAR); + if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_MULTIPLY_EQUALS); + } else if (match(scanner, '*')) { + return makeToken(scanner, TOKEN_STAR_STAR); } else { - return makeToken(TOKEN_STAR); + return makeToken(scanner, TOKEN_STAR); } } case '%': - return makeToken(TOKEN_PERCENT); + return makeToken(scanner, TOKEN_PERCENT); case '-': { - if (match('-')) { - return makeToken(TOKEN_MINUS_MINUS); - } else if (match('=')) { - return makeToken(TOKEN_MINUS_EQUALS); + if (match(scanner, '-')) { + return makeToken(scanner, TOKEN_MINUS_MINUS); + } else if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_MINUS_EQUALS); } else { - return makeToken(TOKEN_MINUS); + return makeToken(scanner, TOKEN_MINUS); } } case '+': { - if (match('+')) { - return makeToken(TOKEN_PLUS_PLUS); - } else if (match('=')) { - return makeToken(TOKEN_PLUS_EQUALS); + if (match(scanner, '+')) { + return makeToken(scanner, TOKEN_PLUS_PLUS); + } else if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_PLUS_EQUALS); } else { - return makeToken(TOKEN_PLUS); + return makeToken(scanner, TOKEN_PLUS); } } case '&': - return makeToken(match('=') ? TOKEN_AMPERSAND_EQUALS : TOKEN_AMPERSAND); + return makeToken(scanner, match(scanner, '=') ? TOKEN_AMPERSAND_EQUALS : TOKEN_AMPERSAND); case '^': - return makeToken(match('=') ? TOKEN_CARET_EQUALS : TOKEN_CARET); + return makeToken(scanner, match(scanner, '=') ? TOKEN_CARET_EQUALS : TOKEN_CARET); case '|': - return makeToken(match('=') ? TOKEN_PIPE_EQUALS : TOKEN_PIPE); + return makeToken(scanner, match(scanner, '=') ? TOKEN_PIPE_EQUALS : TOKEN_PIPE); case '!': - return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); + return makeToken(scanner, match(scanner, '=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); case '=': - if (match('=')) { - return makeToken(TOKEN_EQUAL_EQUAL); - } else if (match('>')) { - return makeToken(TOKEN_ARROW); + if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_EQUAL_EQUAL); + } else if (match(scanner, '>')) { + return makeToken(scanner, TOKEN_ARROW); } else { - return makeToken(TOKEN_EQUAL); + return makeToken(scanner, TOKEN_EQUAL); } case '<': - return makeToken(match('=') ? TOKEN_LESS_EQUAL : TOKEN_LESS); + return makeToken(scanner, match(scanner, '=') ? TOKEN_LESS_EQUAL : TOKEN_LESS); case '>': - return makeToken(match('=') ? + return makeToken(scanner, match(scanner, '=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER); case '"': - return string('"'); + return string(scanner, '"'); case '\'': - return string('\''); + return string(scanner, '\''); } - return errorToken("Unexpected character."); + return errorToken(scanner, "Unexpected character."); } diff --git a/c/scanner.h b/c/scanner.h index 7aef1197..4722f9b7 100644 --- a/c/scanner.h +++ b/c/scanner.h @@ -49,10 +49,17 @@ typedef struct { int line; } Token; -void initScanner(const char *source); +typedef struct { + const char *start; + const char *current; + int line; + bool rawString; +} Scanner; + +void initScanner(Scanner *scanner, const char *source); -void backTrack(); +void backTrack(Scanner *scanner); -Token scanToken(); +Token scanToken(Scanner *scanner); #endif From 44785c7cb8a32f913ea15196966ed06c9135bf50 Mon Sep 17 00:00:00 2001 From: Jason_000 Date: Tue, 10 Nov 2020 22:58:43 +0000 Subject: [PATCH 45/45] Bump version to 0.12.0 --- c/main.c | 2 +- docs/_config.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/c/main.c b/c/main.c index e59fa195..d3bc55e7 100644 --- a/c/main.c +++ b/c/main.c @@ -18,7 +18,7 @@ void cleanupSockets(void) { #include "memory.h" #include "util.h" -#define VERSION "Dictu Version: 0.11.0\n" +#define VERSION "Dictu Version: 0.12.0\n" #ifndef DISABLE_LINENOISE #include "linenoise.h" diff --git a/docs/_config.yml b/docs/_config.yml index 7eeb72cb..c597151a 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.11.0" +version: "0.12.0" github_username: dictu-lang search_enabled: true