diff --git a/src/compiler/boot.dst b/src/compiler/boot.dst index b96f27ee..68f18f5f 100644 --- a/src/compiler/boot.dst +++ b/src/compiler/boot.dst @@ -853,13 +853,16 @@ onvalue." # Are we done yet? (var going true) + # The parser object + (def p (parser 1)) + # Fiber stream of characters (def chars (coro (def buf @"") (var len 1) (while (< 0 len) (buffer-clear buf) - (chunks buf) + (chunks buf p) (:= len (length buf)) (for [i 0 len] (yield (get buf i)))) @@ -867,7 +870,6 @@ onvalue." # Fiber stream of values (def vals (coro - (def p (parser 1)) (while going (switch (parser-status p) :full (yield (parser-produce p)) @@ -977,18 +979,18 @@ environment is needed, use run-context." (:= k (next newenv k)))) (defmacro import [path & args] - (apply tuple import* '_env path args)) + (apply tuple import* '_env path args)) (defn repl [getchunk] - "Run a repl. The first parameter is an optional function to call to + "Run a repl. The first parameter is an optional function to call to get a chunk of source code. Should return nil for end of file." (def newenv (make-env)) - (defn chunks [buf] - (file-write stdout "> ") - (file-flush stdout) - (file-read stdin :line buf)) + (defn chunks [buf p] + (file-write stdout (string (parser-state p) "> ")) + (file-flush stdout) + (file-read stdin :line buf)) (defn onvalue [x] - (put newenv '_ @{:value x}) - (pp x)) + (put newenv '_ @{:value x}) + (pp x)) (run-context newenv (if getchunk getchunk chunks) onvalue default-error-handler)) diff --git a/src/core/io.c b/src/core/io.c index b21961c5..a2e73eed 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -221,7 +221,9 @@ static int dst_io_fwrite(DstArgs args) { return dst_throw(args, "file is not writeable"); for (i = 1; i < args.n; i++) { if (!checkchars(args, i, &str, &len)) return 1; - if (!fwrite(str, len, 1, iof->file)) return dst_throw(args, "error writing to file"); + if (len) { + if (!fwrite(str, len, 1, iof->file)) return dst_throw(args, "error writing to file"); + } } return dst_return(args, dst_wrap_abstract(iof)); } diff --git a/src/include/dst/dstparse.h b/src/include/dst/dstparse.h index c9e08d6a..90210a35 100644 --- a/src/include/dst/dstparse.h +++ b/src/include/dst/dstparse.h @@ -63,6 +63,7 @@ int dst_parser_consume(DstParser *parser, uint8_t c); enum DstParserStatus dst_parser_status(DstParser *parser); Dst dst_parser_produce(DstParser *parser); const char *dst_parser_error(DstParser *parser); +void dst_parser_flush(DstParser *parser); int dst_parse_cfun(DstArgs args); diff --git a/src/mainclient/init.dst b/src/mainclient/init.dst index 0b373656..24494b5a 100644 --- a/src/mainclient/init.dst +++ b/src/mainclient/init.dst @@ -2,6 +2,7 @@ (var *should-repl* :private false) (var *no-file* :private true) +(var *use-getline* :private true) # Flag handlers (def handlers :private { @@ -10,11 +11,13 @@ (print "Options are:") (print " -h Show this help") (print " -v Print the version string") + (print " -s Use raw stdin instead of getline like functionality") (print " -e Execute a string of dst") (print " -r Enter the repl after running all scripts") (os-exit 0) 1) "v" (fn [] (print VERSION) (os-exit 0) 1) + "s" (fn [] (:= *use-getline* false) (:= *should-repl* true) 1) "r" (fn [] (:= *should-repl* true) 1) "e" (fn [i] (:= *no-file* false) @@ -38,8 +41,12 @@ (import arg) (++ i)))) +(defn xgetline [buf p] + (def prompt (string (parser-state p) "> ")) + (getline prompt buf)) + (when (or *should-repl* *no-file*) (print (string "Dst " VERSION " Copyright (C) 2017-2018 Calvin Rose")) - (repl getline)) + (repl (if *use-getline* xgetline))) ) diff --git a/src/mainclient/line.c b/src/mainclient/line.c index 582356ff..9ee7ea61 100644 --- a/src/mainclient/line.c +++ b/src/mainclient/line.c @@ -24,9 +24,12 @@ /* Common */ int dst_line_getter(DstArgs args) { - if (args.n < 1 || !dst_checktype(args.v[0], DST_BUFFER)) - return dst_throw(args, "expected buffer"); - dst_line_get(dst_unwrap_buffer(args.v[0])); + dst_fixarity(args, 2); + dst_check(args, 0, DST_STRING); + dst_check(args, 1, DST_BUFFER); + dst_line_get( + dst_unwrap_string(args.v[0]), + dst_unwrap_buffer(args.v[1])); return dst_return(args, args.v[0]); } @@ -54,8 +57,8 @@ void dst_line_deinit() { ; } -void dst_line_get(DstBuffer *buffer) { - fputs("> ", stdout); +void dst_line_get(const uint8_t *p, DstBuffer *buffer) { + fputs((const char *)p, stdout); simpleline(buffer); } @@ -431,7 +434,8 @@ static int checktermsupport() { return 1; } -void dst_line_get(DstBuffer *buffer) { +void dst_line_get(const uint8_t *p, DstBuffer *buffer) { + prompt = (const char *)p; buffer->count = 0; historyi = 0; if (!isatty(STDIN_FILENO) || !checktermsupport()) { diff --git a/src/mainclient/line.h b/src/mainclient/line.h index 00a0747c..3486e52b 100644 --- a/src/mainclient/line.h +++ b/src/mainclient/line.h @@ -28,7 +28,7 @@ void dst_line_init(); void dst_line_deinit(); -void dst_line_get(DstBuffer *buffer); +void dst_line_get(const uint8_t *p, DstBuffer *buffer); int dst_line_getter(DstArgs args); #endif diff --git a/src/parser/parse.c b/src/parser/parse.c index 1c3bf24d..3c450e0b 100644 --- a/src/parser/parse.c +++ b/src/parser/parse.c @@ -120,7 +120,10 @@ struct DstParseState { #define PFLAG_CONTAINER 1 #define PFLAG_BUFFER 2 -#define PFLAG_SQRBRACKETS 4 +#define PFLAG_PARENS 4 +#define PFLAG_SQRBRACKETS 8 +#define PFLAG_CURLYBRACKETS 16 +#define PFLAG_STRING 32 static void pushstate(DstParser *p, Consumer consumer, int flags) { DstParseState s; @@ -372,8 +375,8 @@ static int dotable(DstParser *p, DstParseState *state, uint8_t c) { return root(p, state, c); } -#define PFLAG_INSTRING 8 -#define PFLAG_END_CANDIDATE 16 +#define PFLAG_INSTRING 64 +#define PFLAG_END_CANDIDATE 128 static int longstring(DstParser *p, DstParseState *state, uint8_t c) { if (state->flags & PFLAG_INSTRING) { /* We are inside the long string */ @@ -426,19 +429,19 @@ static int ampersand(DstParser *p, DstParseState *state, uint8_t c) { dst_v_pop(p->states); switch (c) { case '{': - pushstate(p, dotable, PFLAG_CONTAINER); + pushstate(p, dotable, PFLAG_CONTAINER | PFLAG_CURLYBRACKETS); return 1; case '"': - pushstate(p, stringchar, PFLAG_BUFFER); + pushstate(p, stringchar, PFLAG_BUFFER | PFLAG_STRING); return 1; case '\\': - pushstate(p, longstring, PFLAG_BUFFER); + pushstate(p, longstring, PFLAG_BUFFER | PFLAG_STRING); return 1; case '[': pushstate(p, doarray, PFLAG_CONTAINER | PFLAG_SQRBRACKETS); return 1; case '(': - pushstate(p, doarray, PFLAG_CONTAINER); + pushstate(p, doarray, PFLAG_CONTAINER | PFLAG_PARENS); return 1; default: break; @@ -479,13 +482,13 @@ static int root(DstParser *p, DstParseState *state, uint8_t c) { p->error = "mismatched delimiter"; return 1; case '(': - pushstate(p, dotuple, PFLAG_CONTAINER); + pushstate(p, dotuple, PFLAG_CONTAINER | PFLAG_PARENS); return 1; case '[': pushstate(p, dotuple, PFLAG_CONTAINER | PFLAG_SQRBRACKETS); return 1; case '{': - pushstate(p, dostruct, PFLAG_CONTAINER); + pushstate(p, dostruct, PFLAG_CONTAINER | PFLAG_CURLYBRACKETS); return 1; } } @@ -509,14 +512,18 @@ enum DstParserStatus dst_parser_status(DstParser *parser) { return DST_PARSE_ROOT; } +void dst_parser_flush(DstParser *parser) { + dst_v_empty(parser->argstack); + dst_v__cnt(parser->states) = 1; + dst_v_empty(parser->buf); +} + const char *dst_parser_error(DstParser *parser) { enum DstParserStatus status = dst_parser_status(parser); if (status == DST_PARSE_ERROR) { const char *e = parser->error; - dst_v_empty(parser->argstack); - dst_v__cnt(parser->states) = 1; parser->error = NULL; - dst_v_empty(parser->buf); + dst_parser_flush(parser); return e; } return NULL; @@ -688,6 +695,36 @@ static int cfun_produce(DstArgs args) { return dst_return(args, val); } +static int cfun_flush(DstArgs args) { + DstParser *p = checkparser(args); + if (!p) return 1; + dst_parser_flush(p); + return dst_return(args, args.v[0]); +} + +static int cfun_state(DstArgs args) { + int32_t i; + uint8_t *buf = NULL; + const uint8_t *str; + DstParser *p = checkparser(args); + if (!p) return 1; + for (i = 0; i < dst_v_count(p->states); i++) { + DstParseState *s = p->states + i; + if (s->flags & PFLAG_PARENS) { + dst_v_push(buf, '('); + } else if (s->flags & PFLAG_SQRBRACKETS) { + dst_v_push(buf, '['); + } else if (s->flags & PFLAG_CURLYBRACKETS) { + dst_v_push(buf, '{'); + } else if (s->flags & PFLAG_STRING) { + dst_v_push(buf, '"'); + } + } + str = dst_string(buf, dst_v_count(buf)); + dst_v_free(buf); + return dst_return(args, dst_wrap_string(str)); +} + /* AST */ static int cfun_unwrap1(DstArgs args) { if (args.n != 1) return dst_throw(args, "expected 1 argument"); @@ -730,6 +767,8 @@ static const DstReg cfuns[] = { {"parser-byte", cfun_byte}, {"parser-error", cfun_error}, {"parser-status", cfun_status}, + {"parser-flush", cfun_flush}, + {"parser-state", cfun_state}, {"ast-unwrap", cfun_unwrap}, {"ast-unwrap1", cfun_unwrap1}, {"ast-wrap", cfun_wrap}, diff --git a/test/suite1.dst b/test/suite1.dst index 5703010b..e78a1919 100644 --- a/test/suite1.dst +++ b/test/suite1.dst @@ -73,4 +73,6 @@ (assert (= "hello, \"world\"" \\hello, "world"\\), "long string with embedded quotes") (assert (= "hello, \\\\\\ \"world\"" \=\hello, \\\ "world"\=\), "long string with embedded quotes and backslashes") +(print :hello) + (end-suite)