diff --git a/CHANGELOG.md b/CHANGELOG.md index bae373dd..01fd4413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog All notable changes to this project will be documented in this file. +## Unreleased - ??? +- Fix bug where a buffer overflow could be confused with an out of memory error. +- Change error output to `file:line:column: message`. Column is in bytes - tabs + are considered to have width 1 (instead of 8). + ## 1.14.2 - 2021-01-23 - Allow `JANET_PROFILE` env variable to load a profile before loading the repl. - Update `tracev` macro to allow `def` and `var` inside to work as expected. diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 40e110b3..6160a2c5 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -2054,30 +2054,31 @@ (def [line col] (:where p)) (eprint (if ec "\e[31m" "") - "parse error in " where - " around line " - (string line) - ", column " - (string col) - ": " + ":" + line + ":" + col + ": parse error: " (:error p) (if ec "\e[0m" "")) (eflush)) (defn bad-compile "Default handler for a compile error." - [msg macrof where] + [msg macrof where line col] (def ec (dyn :err-color)) + (eprin + (if ec "\e[31m" "") + where + ":" + line + ":" + col + ": compile error: ") (if macrof - (debug/stacktrace macrof (string msg " while compiling " where)) - (eprint - (if ec "\e[31m" "") - "compile error: " - msg - " while compiling " - where - (if ec "\e[0m" ""))) + (debug/stacktrace macrof msg) + (eprint msg (if ec "\e[0m" ""))) (eflush)) (defn curenv @@ -2130,7 +2131,7 @@ (default guard :ydt) # Evaluate 1 source form in a protected manner - (defn eval1 [source] + (defn eval1 [source &opt l c] (def source (if expand (expand source) source)) (var good true) (var resumeval nil) @@ -2143,11 +2144,7 @@ (do (set good false) (def {:error err :line line :column column :fiber errf} res) - (def msg - (if (<= 0 line) - (string err " on line " line ", column " column) - err)) - (on-compile-error msg errf where)))) + (on-compile-error err errf where (or line l) (or column c))))) guard)) (fiber/setenv f env) (while (fiber/can-resume? f) @@ -2175,6 +2172,10 @@ (fiber/setenv f env) (resume f)) + (defn produce [] + (def tup (p-produce p true)) + [(in tup 0) ;(tuple/sourcemap tup)]) + # Loop (def buf @"") (var parser-not-done true) @@ -2196,7 +2197,7 @@ (while (> len pindex) (+= pindex (p-consume p buf pindex)) (while (p-has-more p) - (eval1 (p-produce p)) + (eval1 ;(produce)) (if (env :exit) (break))) (when (= (p-status p) :error) (parse-err p where) @@ -2205,7 +2206,7 @@ # Check final parser state (unless (env :exit) (while (p-has-more p) - (eval1 (p-produce p)) + (eval1 ;(produce)) (if (env :exit) (break))) (when (= (p-status p) :error) (parse-err p where))) @@ -2450,9 +2451,9 @@ (def [line col] (:where x)) (def pe (string (:error x) " in " y " around line " line ", column " col)) (set exit-error pe)) - (defn bc [&opt x y z] + (defn bc [&opt x y z a b] (when exit - (bad-compile x y z) + (bad-compile x y z a b) (os/exit 1)) (put env :exit true) (def ce (string x " while compiling " z)) diff --git a/src/core/compile.c b/src/core/compile.c index a1733d71..89122b39 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -872,8 +872,12 @@ static Janet cfun(int32_t argc, Janet *argv) { } else { JanetTable *t = janet_table(4); janet_table_put(t, janet_ckeywordv("error"), janet_wrap_string(res.error)); - janet_table_put(t, janet_ckeywordv("line"), janet_wrap_integer(res.error_mapping.line)); - janet_table_put(t, janet_ckeywordv("column"), janet_wrap_integer(res.error_mapping.column)); + if (res.error_mapping.line > 0) { + janet_table_put(t, janet_ckeywordv("line"), janet_wrap_integer(res.error_mapping.line)); + } + if (res.error_mapping.column > 0) { + janet_table_put(t, janet_ckeywordv("column"), janet_wrap_integer(res.error_mapping.column)); + } if (res.macrofiber) { janet_table_put(t, janet_ckeywordv("fiber"), janet_wrap_fiber(res.macrofiber)); } diff --git a/src/core/parse.c b/src/core/parse.c index 45ec285d..f1240cd3 100644 --- a/src/core/parse.c +++ b/src/core/parse.c @@ -175,7 +175,14 @@ static void popstate(JanetParser *p, Janet val) { if (newtop->flags & PFLAG_CONTAINER) { newtop->argn++; /* Keep track of number of values in the root state */ - if (p->statecount == 1) p->pending++; + if (p->statecount == 1) { + p->pending++; + /* Root items are always wrapped in a tuple for source map info. */ + const Janet *tup = janet_tuple_n(&val, 1); + janet_tuple_sm_line(tup) = (int32_t) top.line; + janet_tuple_sm_column(tup) = (int32_t) top.column; + val = janet_wrap_tuple(tup); + } push_arg(p, val); return; } else if (newtop->flags & PFLAG_READERMAC) { @@ -730,6 +737,19 @@ const char *janet_parser_error(JanetParser *parser) { } Janet janet_parser_produce(JanetParser *parser) { + Janet ret; + size_t i; + if (parser->pending == 0) return janet_wrap_nil(); + ret = janet_unwrap_tuple(parser->args[0])[0]; + for (i = 1; i < parser->argcount; i++) { + parser->args[i - 1] = parser->args[i]; + } + parser->pending--; + parser->argcount--; + return ret; +} + +Janet janet_parser_produce_wrapped(JanetParser *parser) { Janet ret; size_t i; if (parser->pending == 0) return janet_wrap_nil(); @@ -980,9 +1000,13 @@ static Janet cfun_parse_error(int32_t argc, Janet *argv) { } static Janet cfun_parse_produce(int32_t argc, Janet *argv) { - janet_fixarity(argc, 1); + janet_arity(argc, 1, 2); JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type); - return janet_parser_produce(p); + if (argc == 2 && janet_truthy(argv[1])) { + return janet_parser_produce_wrapped(p); + } else { + return janet_parser_produce(p); + } } static Janet cfun_parse_flush(int32_t argc, Janet *argv) { @@ -1217,10 +1241,12 @@ static const JanetReg parse_cfuns[] = { }, { "parser/produce", cfun_parse_produce, - JDOC("(parser/produce parser)\n\n" + JDOC("(parser/produce parser &opt wrap)\n\n" "Dequeue the next value in the parse queue. Will return nil if " "no parsed values are in the queue, otherwise will dequeue the " - "next value.") + "next value. If `wrap` is truthy, will return a 1-element tuple that " + "wraps the result. This tuple can be used for source-mapping " + "purposes.") }, { "parser/consume", cfun_parse_consume, diff --git a/src/include/janet.h b/src/include/janet.h index eb59746c..59d6a37c 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1367,6 +1367,7 @@ JANET_API void janet_parser_deinit(JanetParser *parser); JANET_API void janet_parser_consume(JanetParser *parser, uint8_t c); JANET_API enum JanetParserStatus janet_parser_status(JanetParser *parser); JANET_API Janet janet_parser_produce(JanetParser *parser); +JANET_API Janet janet_parser_produce_wrapped(JanetParser *parser); JANET_API const char *janet_parser_error(JanetParser *parser); JANET_API void janet_parser_flush(JanetParser *parser); JANET_API void janet_parser_eof(JanetParser *parser);