diff --git a/.gitignore b/.gitignore index 28ce6b820..c22ce1cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ TEMP* /*.so* /TEST.md /*.deb +/lib/package diff --git a/ChangeLog.md b/ChangeLog.md index 02452399e..ea20205e5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -16,17 +16,20 @@ * New Features * Supported SAT solver by picosat. * Supported SemanticVersion class. -* Improvements +* Improvements/Enhancements * Supported putting a comma at the end of an argument's list for both declaration and calling. * Supported the operator of `==` and `!=` in Boolean class. * Supported `String#isIntegerString`. * Shared an implementation of conversion for a string, an integer, and a double. * Improved type analysis for the language server. * SpecTest: Made a space wider of Test case number. + * #255: Improved the `operator[]` in `Range`. * Bug Fixed * #235: Crash when using `_` outside a function. * #236: Can't specify the class as a return type of function. * #237: Comparing between variables having a string is failed. + * #256: Comparison operator will be failed with an integer on LHS and a variable(double) on RHS. + * #257: Fails a destructuring assignment when declaration with const. ## V1.0.0 (Official Release) - 2021/03/16 This is a 1st official release version. diff --git a/ChangeLog_v1.0.x.md b/ChangeLog_v1.0.x.md index 1d2266225..1defd64cb 100644 --- a/ChangeLog_v1.0.x.md +++ b/ChangeLog_v1.0.x.md @@ -6,6 +6,8 @@ * #235: Crash when using `_` outside a function. * #236: Can't specify the class as a return type of function. * #237: Comparing between variables having a string is failed. + * #256: Comparison operator will be failed with an integer on LHS and a variable(double) on RHS. + * #257: Fails a destructuring assignment when declaration with const. ## V1.0.0 (Official Release) - 2021/03/16 diff --git a/build/Makefile b/build/Makefile index 9d1a95953..a29c6fabf 100644 --- a/build/Makefile +++ b/build/Makefile @@ -160,7 +160,7 @@ all: timex kinx $(SOFILES) main_kxcmd install: mkdir -p /usr/bin/kinxlib/include mkdir -p /usr/bin/kinxlib/docs/licenses - mkdir -p /usr/bin/kinxlib/exec/3rdparty + mkdir -p /usr/bin/kinxlib/package cp -f ./kinx /usr/bin/kinx cp -f ./kxrepl /usr/bin/kxrepl cp -f ./kxtest /usr/bin/kxtest diff --git a/build/template/install.nsi b/build/template/install.nsi index d841d4d69..1f9a8079d 100644 --- a/build/template/install.nsi +++ b/build/template/install.nsi @@ -76,10 +76,11 @@ Section File /r /x ".gitignore" "lib\webview" SetOutPath "$INSTDIR\docs" - File /r /x "typesetting" "docs\licenses" + File /r "docs\licenses" # Uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" + CreateDirectory "$INSTDIR\bin\lib\package" # Shortcut to start menu CreateDirectory "$SMPROGRAMS\Kinx" diff --git a/docs/spec/lib/basic/range.md b/docs/spec/lib/basic/range.md index d92cbf3db..6edc8cc71 100644 --- a/docs/spec/lib/basic/range.md +++ b/docs/spec/lib/basic/range.md @@ -181,7 +181,7 @@ out of range (9) out of range (10) ``` -### Example 5. Range for Switch-Case (2) +### Example 6. Range for Switch-Case (2) #### Code @@ -224,7 +224,7 @@ okay 1 (ae) out of range (af) ``` -### Example 6. Range for String +### Example 7. Range for String Range for String means to return a part of string between the start and the end of `Range`. It is like `String#subString()` but note that `String#subString()` requires a length. @@ -246,7 +246,7 @@ cdefghijklmnopqrstuvwxy cdefghijklmnopqrstuvwxy ``` -### Example 7. Range for Array +### Example 8. Range for Array Range for Array means to return a part of array between the start and the end of `Range`. It is like `Array#subArray()` but note that `Array#subArray()` requires a length. @@ -268,7 +268,7 @@ System.println(ary.subArray(2, 10)); // [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] ``` -### Example 8. Range for Binary +### Example 9. Range for Binary Range for Binary means to return a part of binary between the start and the end of `Range`. It is like `Binary#subBinary()` but note that `Binary#subBinary()` requires a length. @@ -290,7 +290,7 @@ System.println(bin.subBinary(2, 10)); // <0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x <0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b> ``` -### Example 9. Range for Range +### Example 10. Range for Range Range for Range means to return parts between the start and the end by `Range` at the index. @@ -308,3 +308,88 @@ System.println(range[2...12]); // [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] ``` + +### Example 11. Infinite Range (Range Index) + +Range for Range means to return parts between the start and the end by `Range` at the index. + +#### Code + +```javascript +var range = 0..; +System.println(range[2..12]); // [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +System.println(range[2...12]); // [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] +``` + +#### Result + +``` +[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +[2, 3, 4, 5, 6, 7, 8, 9, 10, 11] +``` + +### Example 12. Infinite Range (Integer Index) + +Range for Range means to return parts between the start and the end by `Range` at the index. + +#### Code + +```javascript +var range = 1..; +System.println(range[2]); // 3 +System.println(range[22]); // 23 +System.println(range[1050]); // 1051 +``` + +#### Result + +``` +3 +23 +1051 +``` + +### Example 13. Range for Range (Infinite Range Index 1) + +Range for Range means to return parts between the start and the end by `Range` at the index. + +#### Code + +```javascript +System.println((1..10)[2..]); // 3..10 +System.println((1...10)[2..]); // 3...10 +System.println((100..)[2..]); // 102.. +``` + +#### Result + +``` +Range(3, 10, false) +Range(3, 10, true) +Range(102, null, false) +``` + +### Example 14. Range for Range (Infinite Range Index 2) + +Range for Range means to return parts between the start and the end by `Range` at the index. + +#### Code + +```javascript +var range = (1...10)[2..]; +for (e in range) { + System.println(e); +} +``` + +#### Result + +``` +3 +4 +5 +6 +7 +8 +9 +``` diff --git a/docs/spec/others/bugfixes.md b/docs/spec/others/bugfixes.md index b9cd539fd..cb21b947c 100644 --- a/docs/spec/others/bugfixes.md +++ b/docs/spec/others/bugfixes.md @@ -95,7 +95,7 @@ for (ypix in 0...24) { : ......------------------------::::::::::;;;;+==x& &x=+;;;::::::-------. ``` -### Example 3. Comparing between variables of a string. +### Example 3. Comparing between variables of a string This bug's was caused by missing implementation. @@ -127,7 +127,7 @@ Successful Successful ``` -### Example 4. Can't specify a return type of function. +### Example 4. Can't specify a return type of function This bug's was caused by lack of consideration of a part of type propagation. @@ -155,3 +155,62 @@ f().xxx(); ``` Successful ``` + +### Example 5. Comparison Failure & Crash + +This bug's was caused by lack of the code which moves to the next opcode. + +* Issue: [#256](https://github.com/Kray-G/kinx/issues/256) +* Fixed: [bf1b5ba926db08a69a5c6786d7557f9f6d7e420f](https://github.com/Kray-G/kinx/commit/bf1b5ba926db08a69a5c6786d7557f9f6d7e420f) + +#### Code + +```javascript +function test1(a) { return 10 >= a; } +function test2(a) { return -1 <= a; } +function test3(a) { return 100 < a; } + +System.println(test1(10.5)); +System.println(test2(10.5)); +System.println(test3(10.5)); +``` + +#### Result + +``` +0 +1 +0 +``` + +### Example 6. Fails a Destructuring Assignment in Const + +This bug's was caused by an incorrect bytecode. + +* Issue: [#257](https://github.com/Kray-G/kinx/issues/257) +* Fixed: [43d82765b577221c820575b7f5e7323cc0171be1](https://github.com/Kray-G/kinx/commit/43d82765b577221c820575b7f5e7323cc0171be1) + +#### Code + +```javascript +function test1(data) { + var [a, b, ...c] = data.split(','); +} +function test2(data) { + [a, b, ...c] = data.split(','); +} +function test3(data) { + const [a, b, ...c] = data.split(','); +} +test1("1,2,3,4,5,6,7,8,9"); +test2("1,2,3,4,5,6,7,8,9"); +test3("1,2,3,4,5,6,7,8,9"); + +System.println("Successful"); +``` + +#### Result + +``` +Successful +``` diff --git a/examples/sat2.kx b/examples/sat2.kx new file mode 100644 index 000000000..1b1ec5f93 --- /dev/null +++ b/examples/sat2.kx @@ -0,0 +1,55 @@ +using SatisfiablitySolver; + +var vs = new VersionSatisfiablity(); + +var X = vs.addProduct("X") + .addVersion("0.0.1", true); +var A = vs.addProduct("A") + .addVersion("0.0.1") + .addVersion("0.0.2"); +var B = vs.addProduct("B") + .addVersion("0.0.1") + .addVersion("0.0.2"); +var Z = vs.addProduct("Z") + .addVersion("0.0.1") + .addVersion("0.0.2"); + +function msg(a) { + if (a.not) { + return ("%1% is NOT v%2%" % a.value.name % a.value.version).format(); + } else { + return ("%1% is v%2%" % a.value.name % a.value.version).format(); + } +} +function error(item) { + switch (item.length()) { + when 1: + System.println("- ", msg(item[0])); + when 2: + item[0].not = !item[0].not; // (!A || B) means (A => B) + System.println("- ", msg(item[0]), " => ", msg(item[1])); + else: + System.println("- ", item.map { => msg(_1) }.join(', or\n ')); + } +} + +function tryit() { + var count = 0; + for (var e in vs) { + System.println("%d: " % ++count, e.toJsonString(true)); + } + if (count == 0) { + System.println("Unsatisfiable - Conflict here"); + vs.getConflict().each { => error(_1) }; + } +} + +A("0.0.1").dependsOn(Z("0.0.1")); +A("0.0.2").dependsOn(Z("0.0.2")); + +B("0.0.1").dependsOn(Z("0.0.1")); +B("0.0.2").dependsOn(Z("0.0.2")); + +X("0.0.1").dependsOn(A("0.0.2")); +X("0.0.1").dependsOn(B("0.0.1")); +tryit(); diff --git a/include/ir.h b/include/ir.h index 11374a1ea..1baac7db3 100644 --- a/include/ir.h +++ b/include/ir.h @@ -883,7 +883,7 @@ typedef struct kx_options_ { int output_location:1; int src_stdin:1; int utf8inout:1; - int native_verbose:1; + int verbose:1; int with_native:1; /* dump with native */ int exception_detail_info:1; int debug_mode:1; diff --git a/include/kinx.h b/include/kinx.h index 3021d6010..827370b72 100644 --- a/include/kinx.h +++ b/include/kinx.h @@ -31,8 +31,9 @@ typedef struct kx_lexinfo_ { int is_trim; kx_lexinner_t inner; kx_yyin_t in; - int tempbuf[16]; + const char *pkgkey; const int *restart; + int tempbuf[16]; } kx_lexinfo_t; kvec_init_t(kx_lexinfo_t); diff --git a/include/kxastobject.h b/include/kxastobject.h index 9588932a2..06effb634 100644 --- a/include/kxastobject.h +++ b/include/kxastobject.h @@ -34,6 +34,7 @@ extern kx_object_t *kx_gen_str_object_pos(name_t name); extern const char *kx_gen_constant_string(const char *name); extern const char *kx_check_the_name(kx_object_t *obj); extern kx_object_t *kx_gen_stmtlist(kx_object_t *lhs, kx_object_t *rhs); +extern kx_object_t *kx_gen_exprlist(kx_object_t *lhs, kx_object_t *rhs); extern kx_object_t *kx_gen_range_object(kx_object_t *start, kx_object_t *end, int include_end); extern kx_object_t *kx_gen_case_when_object(kx_object_t *decl, kx_object_t *expr, kx_object_t *modifier); extern kx_object_t *kx_gen_forin_object(kx_object_t *var, kx_object_t *range, kx_object_t *stmt, int is_decl); diff --git a/include/parser.h b/include/parser.h index cef129b9d..bf0eed2e6 100644 --- a/include/parser.h +++ b/include/parser.h @@ -4,6 +4,13 @@ #include #include +#include + +typedef struct package_t_ { + const char *vers; + struct package_t_ *next; +} package_t; +KHASH_MAP_INIT_STR(package, package_t *) typedef struct name_t_ { const char *name; @@ -11,11 +18,13 @@ typedef struct name_t_ { int pos1; int pos2; } name_t; + typedef struct arytype_t_ { int type; int depth; const char *name; /* class name */ } arytype_t; + typedef struct named_stmt_ { const char *name; /* class name */ kx_object_t *stmt; @@ -25,4 +34,12 @@ typedef struct named_stmt_ { #include #endif /* KX_PARSER */ +extern int kx_trace_fmt(kx_context_t *ctx, int nested, const char *fmt, ...); +#define kx_trace(ctx, nested, ...) do {\ + if (ctx->options.verbose) { \ + kx_trace_fmt(ctx, nested, __VA_ARGS__);\ + } \ +} while (0); \ +/**/ + #endif /* KX_PARSER_H */ diff --git a/install.nsi b/install.nsi index c712cb994..995134126 100644 --- a/install.nsi +++ b/install.nsi @@ -76,10 +76,11 @@ Section File /r /x ".gitignore" "lib\webview" SetOutPath "$INSTDIR\docs" - File /r /x "typesetting" "docs\licenses" + File /r "docs\licenses" # Uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" + CreateDirectory "$INSTDIR\bin\lib\package" # Shortcut to start menu CreateDirectory "$SMPROGRAMS\Kinx" diff --git a/lib/exec/3rdparty/.gitignore b/lib/exec/3rdparty/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/lib/exec/3rdparty/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/lib/std/MarkdownParser.kx b/lib/std/MarkdownParser.kx index a16f11876..a608dae9c 100644 --- a/lib/std/MarkdownParser.kx +++ b/lib/std/MarkdownParser.kx @@ -51,7 +51,7 @@ namespace Markdown { } public toObject() { var obj = {}; - @keySet().each { &(key): + @keySet().each { &(key) if (key[0] != '_'[0] && !this[key].isFunction) { if (this[key].isNode) { obj[key] = this[key].toObject(); @@ -213,9 +213,9 @@ namespace Markdown { } } - _class Table(_rows) : Node('table', 'block') { + class Table(_rows) : Node('table', 'block') { private initialize() { - const [heading, separator, ...rows] = _rows.map(&(line) => line.replace(/^\||\|$/, '').split('|')); + var [heading, separator, ...rows] = _rows.map({ &(line) => line.replace(/^\||\|$/, '').split('|') }); @headings = heading.map(&(cell) => inlineParser(cell.trim())); @aligns = separator.map(&(cell) => { cell = cell.trim(); diff --git a/lib/std/kxenumerable.kx b/lib/std/kxenumerable.kx index 7bdb598f1..67ab81891 100644 --- a/lib/std/kxenumerable.kx +++ b/lib/std/kxenumerable.kx @@ -820,6 +820,8 @@ _class _Range(start_, end_, excludeEnd_) { mixin _Enumerable; @Enumerable.sum = @sum; @isRange = true; + var arrayCache_ = []; + var arrayCacheLastIndex_ = 0; var cur_; private initialize() { cur_ = start_; @@ -853,6 +855,9 @@ _class _Range(start_, end_, excludeEnd_) { return a <=> b; } private eachByFunction(func) { + if (!@array && arrayCacheLastIndex_ > 0) { + @array = @toArray(); + } if (@array) { var i = 0; var sz = @array.length(); @@ -897,6 +902,9 @@ _class _Range(start_, end_, excludeEnd_) { if (func.isFunction) { return eachByFunction(func); } + if (!@array && arrayCacheLastIndex_ > 0) { + @array = @toArray(); + } if (@array) { @array.each(&(e) => { yield e; @@ -945,9 +953,53 @@ _class _Range(start_, end_, excludeEnd_) { } return true; } + public toString() { + return "Range(%{start_}, %{end_.isDefined ? end_ : 'null'}, %{excludeEnd_ ? 'true' : 'false'})"; + } + public toArray() { + if (end_.isUndefined) { + throw FiberException("Cannot complete the iteration"); + } + while (true) { + if (end_.isDefined && excludeEnd_ && comp(cur_, end_) >= 0) { + break; + } + arrayCache_.push(cur_); + if (end_.isDefined && !excludeEnd_ && comp(cur_, end_) >= 0) { + break; + } + cur_ = cur_.next(); + } + return arrayCache_; + } public [](rhs) { + if (rhs.isInteger) { + var n = rhs - arrayCacheLastIndex_ + 1; + if (n > 0) { + arrayCache_ += @take(n); + arrayCacheLastIndex_ += n; + } + return arrayCache_[rhs]; + } + if (rhs.isRange) { + var s = rhs.begin(); + var e = rhs.end(); + var ex = rhs.isEndExcluded(); + if (e.isUndefined) { + return new Range(start_ + s, end_, excludeEnd_); + } + if (end_.isUndefined) { + var n = e + (ex ? 0 : 1) - arrayCacheLastIndex_; + if (n > 0) { + arrayCache_ += @take(n); + arrayCacheLastIndex_ += n; + } + return arrayCache_[rhs]; + } + /* will do toArray. */ + } if (!@array) { - @array = @toArray(); + @array = toArray(); } return @array[rhs]; } diff --git a/lib/std/kxstartup.kx b/lib/std/kxstartup.kx index 43881cdaa..79450d88a 100644 --- a/lib/std/kxstartup.kx +++ b/lib/std/kxstartup.kx @@ -82,7 +82,7 @@ var Xml, Net; } } $libpath = libpath; - $pkgpath = (libpath / "../kinxpkg").replace(/\/[^\/]+\/\.\./, ""); + $pkgpath = (libpath / "package").replace(/\/[^\/]+\/\.\./, ""); })(); const KX_KEY_ESC = 0x1b; diff --git a/src/ast_analyzer.c b/src/ast_analyzer.c index 00f6bbb6b..79e621a35 100644 --- a/src/ast_analyzer.c +++ b/src/ast_analyzer.c @@ -1477,7 +1477,8 @@ LOOP_HEAD:; case KXST_EXPRSEQ: /* lhs: expr1: rhs: expr2 */ case KXST_EXPRLIST: /* lhs: expr1: rhs: expr2 */ analyze_ast(ctx, node->lhs, actx); - analyze_ast(ctx, node->rhs, actx); + node = node->rhs; + if (node) goto LOOP_HEAD; break; case KXST_STMTLIST: /* lhs: stmt2: rhs: stmt1 */ analyze_ast(ctx, node->lhs, actx); diff --git a/src/ast_defdisp.c b/src/ast_defdisp.c index 2b3c99781..7acde0bd4 100644 --- a/src/ast_defdisp.c +++ b/src/ast_defdisp.c @@ -555,7 +555,8 @@ LOOP_HEAD:; break; case KXST_EXPRLIST: /* lhs: expr1: rhs: expr2 */ display_def_ast(dctx, node->lhs, lvalue); - display_def_ast(dctx, node->rhs, lvalue); + node = node->rhs; + if (node) goto LOOP_HEAD; break; case KXST_STMTLIST: /* lhs: stmt1: rhs: stmt2 */ display_def_ast(dctx, node->lhs, lvalue); diff --git a/src/ast_display.c b/src/ast_display.c index e97eecfe6..0c53ed89d 100644 --- a/src/ast_display.c +++ b/src/ast_display.c @@ -399,7 +399,8 @@ LOOP_HEAD:; break; case KXST_EXPRLIST: /* lhs: expr1: rhs: expr2 */ display_ast(node->lhs, indent, lvalue); - display_ast(node->rhs, indent, lvalue); + node = node->rhs; + if (node) goto LOOP_HEAD; break; case KXST_STMTLIST: /* lhs: stmt1: rhs: stmt2 */ display_ast(node->lhs, indent, 0); diff --git a/src/ast_gencode.c b/src/ast_gencode.c index 87482de67..476266bf4 100644 --- a/src/ast_gencode.c +++ b/src/ast_gencode.c @@ -124,7 +124,7 @@ static const kx_block_t kx_empty_block = {0}; static const kx_function_t kx_empty_func = {0}; -static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int jmp, int nested); +static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int decl, int jmp, int nested); static void gencode_ast(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int lvalue); static int new_function(kx_analyze_t *ana, kx_object_t *node) @@ -376,19 +376,19 @@ if (code_size(module, ana) > 0) { \ } \ /**/ -static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int jmp, int index, int nested) +static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int decl, int jmp, int index, int nested) { kx_module_t *module = ana->module; if (!node) { return index; } if (node->type == KXST_EXPRLIST) { - index = apply_getval(ctx, node->lhs, ana, jmp, index, nested); - index = apply_getval(ctx, node->rhs, ana, jmp, index, nested); + index = apply_getval(ctx, node->lhs, ana, decl, jmp, index, nested); + index = apply_getval(ctx, node->rhs, ana, decl, jmp, index, nested); return index; } if (node->type == KXOP_CAST) { - return apply_getval(ctx, node->lhs, ana, jmp, index, nested); + return apply_getval(ctx, node->lhs, ana, decl, jmp, index, nested); } if (node->type == KXOP_KEYVALUE) { kx_yyerror_line("Cannot use a key-value pair in array item", node->file, node->line); @@ -440,8 +440,8 @@ static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_TYPEOF, .value1 = { .i = KX_ARY_T } })); KX_CHECK_PATTERN_JMP(jmpblk, nested+1); - int idx = apply_getval(ctx, node->lhs, ana, jmp, 0, nested + 1); - (void)apply_getval(ctx, node->rhs, ana, jmp, idx, nested + 1); + int idx = apply_getval(ctx, node->lhs, ana, decl, jmp, 0, nested + 1); + (void)apply_getval(ctx, node->rhs, ana, decl, jmp, idx, nested + 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_POP })); break; case KXOP_MKOBJ: @@ -450,7 +450,7 @@ static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_TYPEOF, .value1 = { .i = KX_OBJ_T } })); KX_CHECK_PATTERN_JMP(jmpblk, nested+1); - apply_getvals(ctx, node->lhs, ana, jmp, nested + 1); + apply_getvals(ctx, node->lhs, ana, decl, jmp, nested + 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_POP })); break; case KXOP_MKRANGE: @@ -463,7 +463,7 @@ static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, default: if (!(node->type == KXOP_VAR && node->var_type == KX_UND_T)) { // KXDC_CONST means with a pin operator. - if (node->optional == KXDC_CONST) { + if (!decl && node->optional == KXDC_CONST) { kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_APPLYVI, .value1.i = index })); gencode_ast_hook(ctx, node, ana, 0); @@ -483,15 +483,15 @@ static int apply_getval(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, return index + 1; } -static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int jmp, int nested) +static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, int decl, int jmp, int nested) { kx_module_t *module = ana->module; if (!node) { return; } if (node->type == KXST_EXPRLIST) { - apply_getvals(ctx, node->lhs, ana, jmp, nested); - apply_getvals(ctx, node->rhs, ana, jmp, nested); + apply_getvals(ctx, node->lhs, ana, decl, jmp, nested); + apply_getvals(ctx, node->rhs, ana, decl, jmp, nested); return; } @@ -544,8 +544,8 @@ static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *an kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_TYPEOF, .value1 = { .i = KX_ARY_T } })); KX_CHECK_PATTERN_JMP(jmpblk, nested+1); - int idx = apply_getval(ctx, value->lhs, ana, jmp, 0, nested + 1); - (void)apply_getval(ctx, value->rhs, ana, jmp, idx, nested + 1); + int idx = apply_getval(ctx, value->lhs, ana, decl, jmp, 0, nested + 1); + (void)apply_getval(ctx, value->rhs, ana, decl, jmp, idx, nested + 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_POP })); break; } @@ -555,7 +555,7 @@ static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *an kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_TYPEOF, .value1 = { .i = KX_OBJ_T } })); KX_CHECK_PATTERN_JMP(jmpblk, nested+1); - apply_getvals(ctx, value->lhs, ana, jmp, nested + 1); + apply_getvals(ctx, value->lhs, ana, decl, jmp, nested + 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_POP })); break; case KXOP_MKRANGE: @@ -567,7 +567,7 @@ static void apply_getvals(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *an break; default: // KXDC_CONST means with a pin operator. - if (value->optional == KXDC_CONST) { + if (!decl && value->optional == KXDC_CONST) { kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_DUP })); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE(ana), .op = KX_APPLYVS, .value1.s = key })); gencode_ast_hook(ctx, value, ana, 0); @@ -1099,6 +1099,7 @@ static void gencode_ast(kx_context_t *ctx, kx_object_t *node, kx_analyze_t *ana, } LOOP_HEAD:; +// printf("%s:%d, node->type = %d (%s:%d)\n", __FILE__, __LINE__, node->type, node->file, node->line); kx_module_t *module = ana->module; switch (node->type) { case KXVL_UNKNOWN: @@ -1335,9 +1336,9 @@ LOOP_HEAD:; } } if (node->lhs->type == KXOP_MKARY) { - apply_getval(ctx, node->lhs->lhs, ana, -1, 0, 0); + apply_getval(ctx, node->lhs->lhs, ana, 1, -1, 0, 0); } else if (node->lhs->type == KXOP_MKOBJ) { - apply_getvals(ctx, node->lhs->lhs, ana, -1, 0); + apply_getvals(ctx, node->lhs->lhs, ana, 1, -1, 0); } else { gencode_ast_hook(ctx, node->lhs, ana, 1); if ((code_size(module, ana) > 0) && last_op(ana) == KX_PUSHLV) { @@ -1413,9 +1414,9 @@ LOOP_HEAD:; } } if (node->lhs->type == KXOP_MKARY) { - apply_getval(ctx, node->lhs->lhs, ana, -1, 0, 0); + apply_getval(ctx, node->lhs->lhs, ana, 0, -1, 0, 0); } else if (node->lhs->type == KXOP_MKOBJ) { - apply_getvals(ctx, node->lhs->lhs, ana, -1, 0); + apply_getvals(ctx, node->lhs->lhs, ana, 0, -1, 0); } else { gencode_ast_hook(ctx, node->lhs, ana, 1); if ((code_size(module, ana) > 0) && last_op(ana) == KX_PUSHLV) { @@ -1756,7 +1757,7 @@ LOOP_HEAD:; get_block(module, chklen_block)->tf[0] = ana->block; } if (last || exblk < 0) { - apply_getval(ctx, cond->lhs, ana, next, 0, 1); + apply_getval(ctx, cond->lhs, ana, 0, next, 0, 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE_OF(cond, ana), .op = KX_POP })); get_block(module, curblk)->tf[1] = next; if (chklen_block > 0) { @@ -1764,7 +1765,7 @@ LOOP_HEAD:; } } else { int nc = new_block_hook(ana); - apply_getval(ctx, cond->lhs, ana, nc, 0, 1); + apply_getval(ctx, cond->lhs, ana, 0, nc, 0, 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE_OF(cond, ana), .op = KX_POP })); get_block(module, ana->block)->tf[0] = exblk; get_block(module, curblk)->tf[1] = nc; @@ -1783,12 +1784,12 @@ LOOP_HEAD:; get_block(module, curblk)->tf[0] = ana->block; if (last || exblk < 0) { - apply_getvals(ctx, cond->lhs, ana, next, 1); + apply_getvals(ctx, cond->lhs, ana, 0, next, 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE_OF(cond, ana), .op = KX_POP })); get_block(module, curblk)->tf[1] = next; } else { int nc = new_block_hook(ana); - apply_getvals(ctx, cond->lhs, ana, nc, 1); + apply_getvals(ctx, cond->lhs, ana, 0, nc, 1); kv_push(kx_code_t, get_block(module, ana->block)->code, ((kx_code_t){ FILELINE_OF(cond, ana), .op = KX_POP })); get_block(module, ana->block)->tf[0] = exblk; get_block(module, curblk)->tf[1] = nc; @@ -1994,9 +1995,8 @@ LOOP_HEAD:; } case KXST_EXPRLIST: { /* lhs: expr1: rhs: expr2 */ gencode_ast_hook(ctx, node->lhs, ana, 0); - if (node->rhs) { - gencode_ast_hook(ctx, node->rhs, ana, 0); - } + node = node->rhs; + if (node) goto LOOP_HEAD; break; } case KXST_STMTLIST: { /* lhs: stmt1: rhs: stmt2 */ diff --git a/src/ast_object.c b/src/ast_object.c index ceaaabba0..8415c1aee 100644 --- a/src/ast_object.c +++ b/src/ast_object.c @@ -257,6 +257,22 @@ kx_object_t *kx_gen_stmtlist(kx_object_t *lhs, kx_object_t *rhs) return lhs; } +kx_object_t *kx_gen_exprlist(kx_object_t *lhs, kx_object_t *rhs) +{ + if (!lhs) { + return rhs; + } + if (lhs->type != KXST_EXPRLIST) { + return kx_gen_bexpr_object(KXST_EXPRLIST, lhs, rhs); + } + kx_object_t *node = lhs->ex ? lhs->ex : lhs; + while (node->rhs && node->rhs->type == KXST_EXPRLIST) { + node = node->rhs; + } + lhs->ex = node->rhs = kx_gen_bexpr_object(KXST_EXPRLIST, node->rhs, rhs); + return lhs; +} + kx_object_t *kx_gen_range_object(kx_object_t *lhs, kx_object_t *end, int exclude_end) { return kx_gen_obj(KXOP_MKRANGE, exclude_end, lhs, end, NULL); @@ -690,9 +706,6 @@ static kx_object_t *kx_gen_func_object_impl(int type, int optional, arytype_t *r } } kx_object_t *obj = kx_gen_obj(type, (type != KXST_NATIVE) ? optional : KXFT_ANONYMOUS, lhs, rhs, ex); - if (inherit) { - obj->typename = inherit; - } obj->line2 = obj->line; if (line > 0) { obj->line = line; diff --git a/src/exec/code/ge.inc b/src/exec/code/ge.inc index 854fd56ba..6ead4bed9 100644 --- a/src/exec/code/ge.inc +++ b/src/exec/code/ge.inc @@ -118,6 +118,7 @@ int exc = 0; \ (void)kx_try_le_i(ctx, cur, v1, &exc); /* comparing by opposite operation */ \ KX_EXCEPTION_CHECK("SystemException", exc); \ + cur = cur->next; \ } \ } \ /**/ diff --git a/src/exec/code/le.inc b/src/exec/code/le.inc index b9bdbf575..f9dfd0aa2 100644 --- a/src/exec/code/le.inc +++ b/src/exec/code/le.inc @@ -118,6 +118,7 @@ int exc = 0; \ (void)kx_try_ge_i(ctx, cur, v1, &exc); /* comparing by opposite operation */ \ KX_EXCEPTION_CHECK("SystemException", exc); \ + cur = cur->next; \ } \ } \ /**/ diff --git a/src/exec/code/lt.inc b/src/exec/code/lt.inc index 62346e988..e7f950913 100644 --- a/src/exec/code/lt.inc +++ b/src/exec/code/lt.inc @@ -118,6 +118,7 @@ int exc = 0; \ (void)kx_try_gt_i(ctx, cur, v1, &exc); /* comparing by opposite operation */ \ KX_EXCEPTION_CHECK("SystemException", exc); \ + cur = cur->next; \ } \ } \ /**/ diff --git a/src/global.c b/src/global.c index 7a0072dae..7b56ab8d8 100644 --- a/src/global.c +++ b/src/global.c @@ -1,6 +1,9 @@ #include #include +#include #include +#define KX_NO_INCLUDE_PARSER_TAB_H +#include /* used in parsing, parsing phase is not reentrant. */ kx_lexinfo_t kx_lexinfo = {0}; @@ -10,6 +13,7 @@ kx_object_t *kx_ast_root = NULL; int g_yyerror = 0; int g_yywarning = 0; kx_context_t *g_parse_ctx = NULL; +khash_t(package) *g_packages = NULL; /* used in runtime. */ volatile int g_terminated = 0; diff --git a/src/kinx.y b/src/kinx.y index 72af82c8b..f87cd2f73 100644 --- a/src/kinx.y +++ b/src/kinx.y @@ -821,7 +821,7 @@ AssignExpressionObjList KeyValueList : KeyValue - | KeyValueList ',' KeyValue { $$ = kx_gen_bexpr_object(KXST_EXPRLIST, $1, $3); } + | KeyValueList ',' KeyValue { $$ = kx_gen_exprlist($1, $3); } ; KeyValue @@ -1034,9 +1034,9 @@ Inherit_Opt .name = kx_check_the_name($3), .stmt = kx_gen_bexpr_object(KXST_STMTLIST, - kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("this", KX_UNKNOWN_T, $2), + kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("this", KX_OBJ_T, $2), kx_gen_bexpr_object(KXOP_CALL, kx_gen_bexpr_object(KXOP_IDX, $3, kx_gen_str_object("create")), $4)), - kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("super", KX_UNKNOWN_T, $2), + kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("super", KX_OBJ_T, $2), kx_gen_bexpr_object(KXOP_CALL, kx_gen_bexpr_object(KXOP_IDX, kx_gen_var_object("System", KX_UNKNOWN_T), kx_gen_str_object("makeSuper")), kx_gen_var_object("this", KX_UNKNOWN_T))) ), }; diff --git a/src/lexer.c b/src/lexer.c index cbe0d48d6..e2467a7a5 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -15,6 +15,19 @@ static int g_binmode = 0; static int g_regexmode = 0; static const char *varname = NULL; static const char *modulename = NULL; +extern khash_t(package) *g_packages; + +static int get_nested_level(void) +{ + int l, r = kv_size(kx_lex_stack); + for (l = r - 1; 0 <= l; --l) { + kx_lexinfo_t *lexinfo = &(kv_A(kx_lex_stack, l)); + if (lexinfo->in.fp || lexinfo->in.str) { + break; + } + } + return l + 1; +} static const char *parent_path(const char *str) { @@ -36,6 +49,9 @@ static inline const char* make_path(const char* base, const char* name) void setup_lexinfo(kx_context_t *ctx, const char *file, kx_yyin_t *yyin) { + if (yyin->fp || yyin->str) { + kx_trace(g_parse_ctx, get_nested_level(), "[>>] %s\n", file); + } kx_lexinfo.ch = 0; kx_lexinfo.restart = NULL; kx_lexinfo.file = const_str(ctx, file); @@ -45,12 +61,7 @@ void setup_lexinfo(kx_context_t *ctx, const char *file, kx_yyin_t *yyin) kx_lexinfo.inner.brcount = 0; kx_lexinfo.inner.quote = 0; kx_lexinfo.in = *yyin; -} - -void init_lexer(kx_context_t *ctx) -{ - kv_init(kx_lex_stack); - g_parse_ctx = ctx; + kx_lexinfo.pkgkey = NULL; } void free_lexer(void) @@ -59,6 +70,15 @@ void free_lexer(void) kv_destroy(kx_lex_stack); } +void init_lexer(kx_context_t *ctx) +{ + if (g_parse_ctx) { + free_lexer(); + } + kv_init(kx_lex_stack); + g_parse_ctx = ctx; +} + void kx_make_bin_mode(void) { g_binmode = 1; @@ -69,64 +89,161 @@ void kx_make_regex_mode(int br) g_regexmode = br; } -static void load_using_module_asta(const char *name, int len) +static void kx_trace_versions(const char *key, const char *op, package_t *pkg) { + if (g_parse_ctx->options.verbose) { + kx_trace(g_parse_ctx, get_nested_level(), "[package:version/%s] %s (%s", op, key, pkg->vers); + pkg = pkg->next; + while (pkg) { + kx_trace(g_parse_ctx, 0, " -> %s", pkg->vers); + pkg = pkg->next; + } + kx_trace(g_parse_ctx, 0, ")\n"); + } +} + +static int is_package_installed(const char *key) +{ + khint_t k = kh_get(package, g_packages, key); + return k != kh_end(g_packages); +} + +static void push_package_version(const char *key, const char *ver) +{ + khint_t k = kh_get(package, g_packages, key); + if (k != kh_end(g_packages)) { + key = kx_const_str(g_parse_ctx, key); + ver = kx_const_str(g_parse_ctx, ver); + package_t *pkg = kh_value(g_packages, k); + package_t *newp = kx_calloc(1, sizeof(package_t)); + newp->vers = ver; + newp->next = pkg; + kh_value(g_packages, k) = newp; + kx_trace_versions(key, "add", newp); + } +} + +static void pop_package_version(const char *key) +{ + khint_t k = kh_get(package, g_packages, key); + if (k != kh_end(g_packages)) { + package_t *pkg = kh_value(g_packages, k); + if (pkg->next) { + package_t *next = pkg->next; + kx_free(pkg); + kh_value(g_packages, k) = next; + kx_trace_versions(key, "del", next); + } + } +} + +static int set_package_version(const char *pkgname, int pos) +{ + khint_t k = kh_get(package, g_packages, pkgname); + if (k != kh_end(g_packages)) { + package_t *pkg = kh_value(g_packages, k); + const char *p = pkg->vers; + if (p) { + kx_strbuf[pos++] = PATH_DELCH; + while (*p) { + kx_strbuf[pos++] = *p++; + } + kx_strbuf[pos++] = PATH_DELCH; + kx_strbuf[pos++] = 'l'; + kx_strbuf[pos++] = 'i'; + kx_strbuf[pos++] = 'b'; + kx_strbuf[pos++] = PATH_DELCH; + } + } + return pos; +} + +static const char *search_using_path(const char *name, char *libname, int size) +{ + const char *file = NULL; + /* Trying to search the file in the same directory first. */ + snprintf(libname, size-1, "%s%c%s.kx", parent_path(kx_lexinfo.file), PATH_DELCH, name); + if (file_exists(libname)) { + file = libname; + } else { + snprintf(libname, size-1, "%s.kx", name); + file = kxlib_file_exists(libname); + } + return file; +} + +static int load_using_module_asta(const char *name, int len, const char *pkgname, const char *pkgkey) +{ + int r = ';'; + int nested = get_nested_level(); char *path = kx_calloc(len+2, sizeof(char)); memcpy(path, name, len); path[len-2] = 0; const char *search = kxlib_file_exists(path); + if (!search) { + const char *msg = pkgname && !is_package_installed(pkgname) + ? static_format("Package(%s) not installed", pkgname, path) + : static_format("Library file not found(%s)", path); + kx_yyerror(msg); + while (kx_lexinfo.ch && kx_lexinfo.ch != ';') { + kx_lex_next(kx_lexinfo); + } + } else { + kx_trace(g_parse_ctx, nested, "[using:dir] %s\n", search); - kv_push(kx_lexinfo_t, kx_lex_stack, kx_lexinfo); - kx_dirent_t *entry = NULL; - kx_dir_t *dir = kx_open_dir(search); - while ((entry = kx_read_dir(dir)) != NULL) { - if (entry->d_name[0] != '.') { - const char *file = make_path(search, entry->d_name); - setup_lexinfo(g_parse_ctx, file, &(kx_yyin_t){ - .fp = NULL, - .str = NULL, - .file = const_str(g_parse_ctx, file) - }); - kv_push(kx_lexinfo_t, kx_lex_stack, kx_lexinfo); + kv_push(kx_lexinfo_t, kx_lex_stack, kx_lexinfo); + kx_dirent_t *entry = NULL; + kx_dir_t *dir = kx_open_dir(search); + while ((entry = kx_read_dir(dir)) != NULL) { + if (entry->d_name[0] != '.') { + const char *file = make_path(search, entry->d_name); + kx_trace(g_parse_ctx, nested, "[using:*] %s\n", file); + setup_lexinfo(g_parse_ctx, file, &(kx_yyin_t){ + .fp = NULL, + .str = NULL, + .file = const_str(g_parse_ctx, file) + }); + if (pkgkey) { + kx_lexinfo.pkgkey = pkgkey; + pkgkey = NULL; + } + kv_push(kx_lexinfo_t, kx_lex_stack, kx_lexinfo); + } } + + kx_close_dir(dir); + r = kx_yylex(); } - kx_close_dir(dir); kx_free(path); + return r; } -static int load_using_module(const char *name, int no_error) +static int load_using_module(const char *name, const char *pkgname, const char *pkgkey, int no_error) { char libname[LIBNAME_BUFSIZE*2] = {0}; const char *file = NULL; int len = strlen(name); if (name[len-1] == '*') { if (name[len-2] != PATH_DELCH) { - kx_yywarning("Can not use '*' with a current directoy in 'using' directive"); + kx_yyerror("Can not use '*' with a current directoy in 'using' directive"); } else { - load_using_module_asta(name, len); - return kx_yylex(); + return load_using_module_asta(name, len, pkgname, pkgkey); } } else { - /* Trying to search the file in the same directory first. */ - snprintf(libname, LIBNAME_BUFSIZE*2-1, "%s%c%s.kx", parent_path(kx_lexinfo.file), PATH_DELCH, name); - if (file_exists(libname)) { - file = libname; - } else { - snprintf(libname, LIBNAME_BUFSIZE*2-1, "%s.kx", name); - if (!(file = kxlib_file_exists(libname))) { - if (!no_error) { - char buf[LIBNAME_BUFSIZE*3] = {0}; - snprintf(buf, LIBNAME_BUFSIZE*3-1, "File not found(%s)", libname); - kx_yywarning(buf); - } - while (kx_lexinfo.ch && kx_lexinfo.ch != ';') { - kx_lex_next(kx_lexinfo); - } - return no_error ? ';' : ERROR; + file = search_using_path(name, libname, LIBNAME_BUFSIZE*2-1); + if (!file) { + if (!no_error) { + const char *msg = pkgname && !is_package_installed(pkgname) + ? static_format("Package(%s) not installed", pkgname, name) + : static_format("Library file not found(%s)", name); + kx_yyerror(msg); } + while (kx_lexinfo.ch && kx_lexinfo.ch != ';') { + kx_lex_next(kx_lexinfo); + } + return ';'; } - kv_push(kx_lexinfo_t, kx_lex_stack, kx_lexinfo); FILE *fp = fopen(file, "r"); setup_lexinfo(g_parse_ctx, file, &(kx_yyin_t){ @@ -134,6 +251,9 @@ static int load_using_module(const char *name, int no_error) .str = NULL, .file = const_str(g_parse_ctx, file) }); + if (pkgkey) { + kx_lexinfo.pkgkey = pkgkey; + } } kx_lex_next(kx_lexinfo); @@ -151,16 +271,65 @@ static int process_using(void) while (kx_is_whitespace(kx_lexinfo)) { kx_lex_next(kx_lexinfo); } + const char *pkgname = NULL; + int pushed_version = 0; + int is_package = kx_lexinfo.ch == '@'; + int is_package_version = 0; + int is_package_verspos = 0; + int is_package_namepos = 0; int pos = 0; - kx_strbuf[pos++] = kx_lexinfo.ch; + if (is_package) { + kx_strbuf[pos++] = 'p'; + kx_strbuf[pos++] = 'a'; + kx_strbuf[pos++] = 'c'; + kx_strbuf[pos++] = 'k'; + kx_strbuf[pos++] = 'a'; + kx_strbuf[pos++] = 'g'; + kx_strbuf[pos++] = 'e'; + kx_strbuf[pos++] = PATH_DELCH; + is_package_namepos = pos; + } else { + kx_strbuf[pos++] = kx_lexinfo.ch; + } kx_lex_next(kx_lexinfo); while (pos < POSMAX && (kx_is_filechar(kx_lexinfo) || kx_lexinfo.ch == '*')) { - kx_strbuf[pos++] = kx_lexinfo.ch == '.' ? PATH_DELCH : kx_lexinfo.ch; + if (is_package) { + if (is_package_version == 0 && kx_lexinfo.ch == '.') { + kx_strbuf[pos] = 0; + pkgname = kx_const_str(g_parse_ctx, kx_strbuf + is_package_namepos); + pos = set_package_version(pkgname, pos); + is_package_version = 2; + } else if (is_package_version == 0 && kx_lexinfo.ch == '(') { + kx_strbuf[pos] = 0; + pkgname = kx_const_str(g_parse_ctx, kx_strbuf + is_package_namepos); + is_package_version = 1; + kx_strbuf[pos++] = PATH_DELCH; + is_package_verspos = pos; + } else if (is_package_version == 1 && kx_lexinfo.ch == ')') { + kx_strbuf[pos] = 0; + const char *ver = kx_const_str(g_parse_ctx, kx_strbuf + is_package_verspos); + pushed_version = 1; + push_package_version(pkgname, ver); + is_package_version = 2; + kx_strbuf[pos++] = PATH_DELCH; + kx_strbuf[pos++] = 'l'; + kx_strbuf[pos++] = 'i'; + kx_strbuf[pos++] = 'b'; + } else { + if (is_package_version == 1) { + kx_strbuf[pos++] = kx_lexinfo.ch; + } else { + kx_strbuf[pos++] = kx_lexinfo.ch == '.' ? PATH_DELCH : kx_lexinfo.ch; + } + } + } else { + kx_strbuf[pos++] = kx_lexinfo.ch == '.' ? PATH_DELCH : kx_lexinfo.ch; + } kx_lex_next(kx_lexinfo); } kx_strbuf[pos] = 0; - return load_using_module(kx_strbuf, no_error); + return load_using_module(kx_strbuf, pkgname, pushed_version ? pkgname : NULL, no_error); } static int get_keyword_token(const char *val) @@ -646,7 +815,7 @@ static int process_import(void) return ';'; case 6: g_import = 0; - return load_using_module(modulename, 1); + return load_using_module(modulename, NULL, NULL, 1); } return ERROR; } @@ -683,7 +852,13 @@ int kx_yylex() if (kx_lexinfo.in.fp && kx_lexinfo.in.fp != stdin) { fclose(kx_lexinfo.in.fp); } + if (kx_lexinfo.pkgkey) { + pop_package_version(kx_lexinfo.pkgkey); + } kx_lexinfo = kv_pop(kx_lex_stack); + if (kx_lexinfo.in.file) { + kx_trace(g_parse_ctx, get_nested_level(), "[<<] %s:%d\n", kx_lexinfo.in.file, kx_lexinfo.line); + } if (!kx_lexinfo.in.fp && !kx_lexinfo.in.str) { kx_lexinfo.in.fp = fopen(kx_lexinfo.in.file, "r"); } diff --git a/src/loadlib.c b/src/loadlib.c index ecbdb62b6..2daab0bf9 100644 --- a/src/loadlib.c +++ b/src/loadlib.c @@ -107,6 +107,21 @@ void unload_library(void *h) } #endif +const char *kxlib_package_file(void) +{ + const char *checkfile = make_path_with(get_kinx_path(), "lib"PATH_DELIM"package", "kxpackage.def"); + if (file_exists(checkfile)) { + return checkfile; + } + #if !defined(KCC_WINDOWS) + checkfile = make_path_with("/usr/bin", "kinxlib/package", "kxpackage.def"); + if (file_exists(checkfile)) { + return checkfile; + } + #endif + return NULL; +} + const char *kxlib_file_exists_no_current(const char *file) { const char *checkfile = make_path(get_kinx_path(), file); diff --git a/src/mainlib.c b/src/mainlib.c index 380b47d3b..e42c2ea20 100644 --- a/src/mainlib.c +++ b/src/mainlib.c @@ -1,6 +1,7 @@ #define KX_LIB_DLL #include #include +#include #include #include #include @@ -38,6 +39,20 @@ extern void alloc_initialize(void); extern void alloc_finalize(void); extern void init_allocator(void); extern volatile int g_terminated; +extern khash_t(package) *g_packages; +extern const char *kxlib_package_file(void); + +int kx_trace_fmt(kx_context_t *ctx, int nested, const char *fmt, ...) +{ + for (int i = 0; i < nested; ++i) { + printf(" "); + } + va_list list; + va_start(list, fmt); + int r = vprintf(fmt, list); + va_end(list); + return r; +} #ifdef YYDEBUG extern int kx_yydebug; @@ -122,7 +137,18 @@ static void version(int detail) if (detail) { printf("- platform: %s\n", sljit_get_platform_name()); printf("- path: %s\n", get_kinx_path()); - printf("\n"); + + /* Package List */ + if (kh_end(g_packages) > 0) { + printf("\nPackages:\n"); + for (khint_t k = 0; k < kh_end(g_packages); ++k) { + if (kh_exist(g_packages, k)) { + const char *key = kh_key(g_packages, k); + package_t *p = kh_value(g_packages, k); + printf(" * %s %s\n", key, p->vers); + } + } + } } } @@ -166,13 +192,105 @@ static void setup_run_environment(const char *filename) #endif } -DllExport int do_main(int ac, char **av) +static void setup_package_info(kx_context_t *ctx) { - int r = 1; - init_allocator(); - alloc_initialize(); - pthread_mutex_init(&g_mutex, NULL); + const char *pkgfile = kxlib_package_file(); + if (!pkgfile) { + return; + } + kx_trace(ctx, 0, "[package:ini] %s\n", pkgfile); + FILE *fp = fopen(pkgfile, "r"); + if (fp) { + char buf[1024] = {0}; + while (fgets(buf, 1020, fp)) { + char *p = strrchr(buf, '='); + if (!p || p == buf) continue; + char *s = p; + while (buf < s) { + --s; + if (*s != ' ' && *s != '\t') { + break; + } + *s = 0; + } + if (buf == s) continue; + ++p; + while (*p) { + if (*p != ' ' && *p != '\t') { + break; + } + ++p; + } + char *e = p + strlen(p) - 1; + while (*e == '\n' || *e == '\r') { + *e = 0; + --e; + } + if (*p) { + int absent; + const char *key = kx_const_str(ctx, buf); + const char *ver = kx_const_str(ctx, p); + kx_trace(ctx, 0, "[add:package/version] %s (%s)\n", key, ver); + package_t *pkg = kx_calloc(1, sizeof(package_t)); + pkg->vers = ver; + khint_t k = kh_put(package, g_packages, key, &absent); + kh_value(g_packages, k) = pkg; + if (absent) { + kh_key(g_packages, k) = key; + } + } + } + fclose(fp); + } +} + +static void free_package_info(void) +{ + for (khint_t k = 0; k < kh_end(g_packages); ++k) { + if (kh_exist(g_packages, k)) { + package_t *p = kh_value(g_packages, k); + while (p) { + package_t *n = p->next; + kx_free(p); + p = n; + } + } + } + kh_destroy(package, g_packages); +} + +const char *search_exec_file(kx_context_t *ctx, const char *execname) +{ + const char *execfile = kxlib_exec_file_exists(execname); + if (execfile) { + kx_trace(ctx, 0, "[exec:found] %s\n", execfile); + return execfile; + } + + kstr_t *ksv = ks_new(); + for (khint_t k = 0; k < kh_end(g_packages); ++k) { + if (kh_exist(g_packages, k)) { + const char *pkgname = kh_key(g_packages, k); + package_t *p = kh_value(g_packages, k); + ks_clear(ksv); + ks_appendf(ksv, "lib%cpackage%c%s%c%s%cbin%c%s.kx", PATH_DELCH, PATH_DELCH, pkgname, PATH_DELCH, p->vers, PATH_DELCH, PATH_DELCH, execname); + kx_trace(ctx, 0, "[exec:check] %s", ks_string(ksv)); + if (file_exists(ks_string(ksv))) { + execfile = kx_const_str(ctx, ks_string(ksv)); + kx_trace(ctx, 0, " ... found\n"); + break; + } else { + kx_trace(ctx, 0, " ... not found\n"); + } + } + } + ks_free(ksv); + return execfile; +} + +DllExport int do_main(int ac, char **av) +{ #ifdef YYDEBUG kx_yydebug = 1; #endif @@ -191,7 +309,16 @@ DllExport int do_main(int ac, char **av) } #endif + /* initializations */ + init_allocator(); + alloc_initialize(); + pthread_mutex_init(&g_mutex, NULL); + g_packages = kh_init(package); + + /* option check */ + int r = 1; int error_code = -1; + int disp_version = -1; const char *filename = NULL; const char *workdir = NULL; kx_context_t *ctx = make_context(); @@ -205,8 +332,8 @@ DllExport int do_main(int ac, char **av) case '-': get_long_option(optarg, lname, param); if (!strcmp(lname, "version")) { - version(1); - goto CLEANUP; + disp_version = 1; + goto END_OF_OPT; } else if (!strcmp(lname, "dot")) { ctx->options.dot = 1; } else if (!strcmp(lname, "native-call-max-depth")) { @@ -215,8 +342,8 @@ DllExport int do_main(int ac, char **av) ctx->options.with_native = param[0] ? strtol(param, NULL, 0) : 1; } else if (!strcmp(lname, "exception-detail-info")) { ctx->options.exception_detail_info = param[0] ? strtol(param, NULL, 0) : 1; - } else if (!strcmp(lname, "native-verbose")) { - ctx->options.native_verbose = param[0] ? strtol(param, NULL, 0) : 1; + } else if (!strcmp(lname, "verbose")) { + ctx->options.verbose = param[0] ? strtol(param, NULL, 0) : 1; } else if (!strcmp(lname, "case-threshold")) { ctx->options.case_threshold = param[0] ? strtol(param, NULL, 0) : 16; } else if (!strcmp(lname, "error-code")) { @@ -260,8 +387,8 @@ DllExport int do_main(int ac, char **av) usage(); goto CLEANUP; case 'v': - version(0); - goto CLEANUP; + disp_version = 0; + goto END_OF_OPT; default: usage(); goto CLEANUP; @@ -269,6 +396,12 @@ DllExport int do_main(int ac, char **av) } END_OF_OPT: + setup_package_info(ctx); + if (disp_version >= 0) { + version(disp_version); + goto CLEANUP; + } + #if defined(_WIN32) || defined(_WIN64) if (GetConsoleCP() == CP_UTF8) { ctx->options.utf8inout = 1; @@ -281,7 +414,7 @@ DllExport int do_main(int ac, char **av) } kx_lexinfo.quiet = ctx->options.quiet; if (execname) { - const char *execfile = kxlib_exec_file_exists(execname); + const char *execfile = search_exec_file(ctx, execname); if (!execfile) { fprintf(stderr, "No internal execution code(%s).\n", execname); r = 1; @@ -394,7 +527,9 @@ DllExport int do_main(int ac, char **av) start_display_def_ast(kx_ast_root); } + kx_trace(ctx, 0, "[cleanup] terminating the program...\n"); g_terminated = 1; + free_package_info(); context_cleanup(ctx); free_nodes(); pthread_mutex_destroy(&g_mutex); @@ -407,7 +542,9 @@ DllExport int do_main(int ac, char **av) #if defined(_WIN32) || defined(_WIN64) WSACleanup(); #endif - return error_code >= 0 ? error_code : r; + r = error_code >= 0 ? error_code : r; + kx_trace(ctx, 0, "[done:status] %d\n", r); + return r; } /* Interfaces As a Library */ diff --git a/src/parser.c b/src/parser.c index e5508ae51..921813db1 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2841,7 +2841,7 @@ int yyparse(YYPARSE_ARG) { yyval.obj = kx_gen_bexpr_object(KXST_EXPRSEQ, YYASP(1-4).obj, kx_gen_uexpr_object_line(KXOP_MKOBJ, NULL, YYASP(3-4).intval)); } break; case 363: #line 824 "src/kinx.y" -{ yyval.obj = kx_gen_bexpr_object(KXST_EXPRLIST, YYASP(1-3).obj, YYASP(3-3).obj); } break; +{ yyval.obj = kx_gen_exprlist(YYASP(1-3).obj, YYASP(3-3).obj); } break; case 364: #line 828 "src/kinx.y" { yyval.obj = kx_gen_keyvalue_object(YYASP(2-5).strval, YYASP(5-5).obj); } break; @@ -3254,9 +3254,9 @@ int yyparse(YYPARSE_ARG) .name = kx_check_the_name(YYASP(3-4).obj), .stmt = kx_gen_bexpr_object(KXST_STMTLIST, - kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("this", KX_UNKNOWN_T, YYASP(2-4).intval), + kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("this", KX_OBJ_T, YYASP(2-4).intval), kx_gen_bexpr_object(KXOP_CALL, kx_gen_bexpr_object(KXOP_IDX, YYASP(3-4).obj, kx_gen_str_object("create")), YYASP(4-4).obj)), - kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("super", KX_UNKNOWN_T, YYASP(2-4).intval), + kx_gen_bexpr_object(KXOP_DECL, kx_gen_var_object_line("super", KX_OBJ_T, YYASP(2-4).intval), kx_gen_bexpr_object(KXOP_CALL, kx_gen_bexpr_object(KXOP_IDX, kx_gen_var_object("System", KX_UNKNOWN_T), kx_gen_str_object("makeSuper")), kx_gen_var_object("this", KX_UNKNOWN_T))) ), };