From cdcfb63f4132a1fa52273184eb4c355b3c720035 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sat, 4 Jun 2022 15:59:24 +0900 Subject: [PATCH] New '__options__' psuedo-module with compile-time changes --- src/compiler.c | 76 +++++++++++++++++++ test/testOptionsCompileTimeBuiltins.krk | 5 ++ .../testOptionsCompileTimeBuiltins.krk.expect | 2 + 3 files changed, 83 insertions(+) create mode 100644 test/testOptionsCompileTimeBuiltins.krk create mode 100644 test/testOptionsCompileTimeBuiltins.krk.expect diff --git a/src/compiler.c b/src/compiler.c index 136c6ab..765a4b8 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -221,8 +221,12 @@ typedef struct Compiler { size_t annotationCount; /**< Number of type annotations found while compiling function signature. */ int delSatisfied; /**< Flag indicating if a 'del' target has been completed. */ + + size_t optionsFlags; /**< Special __options__ imports; similar to __future__ in Python */ } Compiler; +#define OPTIONS_FLAG_COMPILE_TIME_BUILTINS (1 << 0) + /** * @brief Class compilation context. * @@ -346,6 +350,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) { compiler->properties = NULL; compiler->annotationCount = 0; compiler->delSatisfied = 0; + compiler->optionsFlags = compiler->enclosing ? compiler->enclosing->optionsFlags : 0; if (type != TYPE_MODULE) { current->codeobject->name = krk_copyString(parser.previous.start, parser.previous.length); @@ -662,6 +667,15 @@ static ssize_t parseVariable(const char * errorMessage) { declareVariable(); if (current->scopeDepth > 0) return 0; + if ((current->optionsFlags & OPTIONS_FLAG_COMPILE_TIME_BUILTINS) && *parser.previous.start != '_') { + KrkValue value; + if (krk_tableGet_fast(&vm.builtins->fields, krk_copyString(parser.previous.start, parser.previous.length), &value)) { + error("Conflicting declaration of global '%.*s' is invalid when 'compile_time_builtins' is enabled.", + (int)parser.previous.length, parser.previous.start); + return 0; + } + } + return identifierConstant(&parser.previous); } @@ -2260,11 +2274,56 @@ static void importStatement(void) { } while (match(TOKEN_COMMA)); } +static void optionsImport(void) { + int expectCloseParen = 0; + + KrkToken compile_time_builtins = syntheticToken("compile_time_builtins"); + + advance(); + consume(TOKEN_IMPORT, "__options__ is not a package\n"); + + if (match(TOKEN_LEFT_PAREN)) { + expectCloseParen = 1; + startEatingWhitespace(); + } + + do { + consume(TOKEN_IDENTIFIER, "Expected member name"); + + /* Okay, what is it? */ + if (identifiersEqual(&parser.previous, &compile_time_builtins)) { + current->optionsFlags |= OPTIONS_FLAG_COMPILE_TIME_BUILTINS; + } else { + error("'%.*s' is not a recognized __options__ import", + (int)parser.previous.length, parser.previous.start); + break; + } + + if (check(TOKEN_AS)) { + errorAtCurrent("__options__ imports can not be given names"); + break; + } + + } while (match(TOKEN_COMMA) && !check(TOKEN_RIGHT_PAREN)); + + if (expectCloseParen) { + stopEatingWhitespace(); + consume(TOKEN_RIGHT_PAREN, "Expected ')' after import list started with '('"); + } +} + static void fromImportStatement(void) { int expectCloseParen = 0; KrkToken startOfName = {0}; int leadingDots = 0; + KrkToken options = syntheticToken("__options__"); + if (check(TOKEN_IDENTIFIER) && identifiersEqual(&parser.current, &options)) { + /* from __options__ import ... */ + optionsImport(); + return; + } + while (match(TOKEN_DOT)) { leadingDots++; } @@ -2769,6 +2828,23 @@ static void namedVariable(KrkToken name, int exprType) { } else if ((arg = resolveUpvalue(current, &name)) != -1) { DO_VARIABLE(OP_SET_UPVALUE, OP_GET_UPVALUE, OP_NONE); } else { + if ((current->optionsFlags & OPTIONS_FLAG_COMPILE_TIME_BUILTINS) && *name.start != '_') { + KrkValue value; + if (krk_tableGet_fast(&vm.builtins->fields, krk_copyString(name.start, name.length), &value)) { + if ((exprType == EXPR_ASSIGN_TARGET && matchComplexEnd()) || + (exprType == EXPR_CAN_ASSIGN && match(TOKEN_EQUAL)) || + (exprType == EXPR_CAN_ASSIGN && matchAssignment())) { + error("Can not assign to '%.*s' when 'compile_time_builtins' is enabled.", + (int)name.length, name.start); + } else if (exprType == EXPR_DEL_TARGET && checkEndOfDel()) { + error("Can not delete '%.*s' when 'compile_time_builtins' is enabled.", + (int)name.length, name.start); + } else { + emitConstant(value); + } + return; + } + } arg = identifierConstant(&name); DO_VARIABLE(OP_SET_GLOBAL, OP_GET_GLOBAL, OP_DEL_GLOBAL); } diff --git a/test/testOptionsCompileTimeBuiltins.krk b/test/testOptionsCompileTimeBuiltins.krk new file mode 100644 index 0000000..45e324a --- /dev/null +++ b/test/testOptionsCompileTimeBuiltins.krk @@ -0,0 +1,5 @@ +from __options__ import compile_time_builtins + +print('hello world') + +print(sum(abs(x) for x in range(-4,4))) diff --git a/test/testOptionsCompileTimeBuiltins.krk.expect b/test/testOptionsCompileTimeBuiltins.krk.expect new file mode 100644 index 0000000..5d30574 --- /dev/null +++ b/test/testOptionsCompileTimeBuiltins.krk.expect @@ -0,0 +1,2 @@ +hello world +16