mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 19:19:53 +00:00
Merge pull request #124 from ALSchwalm/parse-state
Add support for getting more detailed parser state
This commit is contained in:
commit
779fcf2d54
@ -1788,7 +1788,7 @@
|
|||||||
(default chunks (fn [buf p] (getline (string "repl:"
|
(default chunks (fn [buf p] (getline (string "repl:"
|
||||||
(parser/where p)
|
(parser/where p)
|
||||||
":"
|
":"
|
||||||
(parser/state p) "> ")
|
(parser/state p :delimiters) "> ")
|
||||||
buf)))
|
buf)))
|
||||||
(default onsignal (fn [f x]
|
(default onsignal (fn [f x]
|
||||||
(case (fiber/status f)
|
(case (fiber/status f)
|
||||||
@ -1806,7 +1806,7 @@ _fiber is bound to the suspended fiber
|
|||||||
|
|
||||||
```)
|
```)
|
||||||
(repl (fn [buf p]
|
(repl (fn [buf p]
|
||||||
(def status (parser/state p))
|
(def status (parser/state p :delimiters))
|
||||||
(def c (parser/where p))
|
(def c (parser/where p))
|
||||||
(def prompt (string "debug[" level "]:" c ":" status "> "))
|
(def prompt (string "debug[" level "]:" c ":" status "> "))
|
||||||
(getline prompt buf))
|
(getline prompt buf))
|
||||||
|
177
src/core/parse.c
177
src/core/parse.c
@ -144,6 +144,8 @@ DEF_PARSER_STACK(_pushstate, JanetParseState, states, statecount, statecap)
|
|||||||
#define PFLAG_LONGSTRING 0x4000
|
#define PFLAG_LONGSTRING 0x4000
|
||||||
#define PFLAG_READERMAC 0x8000
|
#define PFLAG_READERMAC 0x8000
|
||||||
#define PFLAG_ATSYM 0x10000
|
#define PFLAG_ATSYM 0x10000
|
||||||
|
#define PFLAG_COMMENT 0x20000
|
||||||
|
#define PFLAG_TOKEN 0x40000
|
||||||
|
|
||||||
static void pushstate(JanetParser *p, Consumer consumer, int flags) {
|
static void pushstate(JanetParser *p, Consumer consumer, int flags) {
|
||||||
JanetParseState s;
|
JanetParseState s;
|
||||||
@ -357,7 +359,12 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||||||
|
|
||||||
static int comment(JanetParser *p, JanetParseState *state, uint8_t c) {
|
static int comment(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||||
(void) state;
|
(void) state;
|
||||||
if (c == '\n') p->statecount--;
|
if (c == '\n') {
|
||||||
|
p->statecount--;
|
||||||
|
p->bufcount = 0;
|
||||||
|
} else {
|
||||||
|
push_buf(p, c);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +472,7 @@ static int atsign(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pushstate(p, tokenchar, 0);
|
pushstate(p, tokenchar, PFLAG_TOKEN);
|
||||||
push_buf(p, '@'); /* Push the leading at-sign that was dropped */
|
push_buf(p, '@'); /* Push the leading at-sign that was dropped */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -479,7 +486,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||||||
p->error = "unexpected character";
|
p->error = "unexpected character";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
pushstate(p, tokenchar, 0);
|
pushstate(p, tokenchar, PFLAG_TOKEN);
|
||||||
return 0;
|
return 0;
|
||||||
case '\'':
|
case '\'':
|
||||||
case ',':
|
case ',':
|
||||||
@ -491,10 +498,10 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||||||
pushstate(p, stringchar, PFLAG_STRING);
|
pushstate(p, stringchar, PFLAG_STRING);
|
||||||
return 1;
|
return 1;
|
||||||
case '#':
|
case '#':
|
||||||
pushstate(p, comment, 0);
|
pushstate(p, comment, PFLAG_COMMENT);
|
||||||
return 1;
|
return 1;
|
||||||
case '@':
|
case '@':
|
||||||
pushstate(p, atsign, 0);
|
pushstate(p, atsign, PFLAG_ATSYM);
|
||||||
return 1;
|
return 1;
|
||||||
case '`':
|
case '`':
|
||||||
pushstate(p, longstring, PFLAG_LONGSTRING);
|
pushstate(p, longstring, PFLAG_LONGSTRING);
|
||||||
@ -634,7 +641,7 @@ void janet_parser_deinit(JanetParser *parser) {
|
|||||||
free(parser->states);
|
free(parser->states);
|
||||||
}
|
}
|
||||||
|
|
||||||
void janet_parser_clone(JanetParser *src, JanetParser *dest) {
|
void janet_parser_clone(const JanetParser *src, JanetParser *dest) {
|
||||||
/* Misc fields */
|
/* Misc fields */
|
||||||
dest->flag = src->flag;
|
dest->flag = src->flag;
|
||||||
dest->pending = src->pending;
|
dest->pending = src->pending;
|
||||||
@ -857,35 +864,155 @@ static Janet cfun_parse_where(int32_t argc, Janet *argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Janet cfun_parse_state(int32_t argc, Janet *argv) {
|
static Janet janet_wrap_parse_state(JanetParseState *s, Janet *args,
|
||||||
|
uint8_t *buff, uint32_t bufcount) {
|
||||||
|
JanetTable *state = janet_table(0);
|
||||||
|
const uint8_t *buffer;
|
||||||
|
int add_buffer = 0;
|
||||||
|
const char *type = NULL;
|
||||||
|
|
||||||
|
if (s->flags & PFLAG_CONTAINER) {
|
||||||
|
JanetArray *container_args = janet_array(s->argn);
|
||||||
|
container_args->count = s->argn;
|
||||||
|
memcpy(container_args->data, args, sizeof(args[0])*s->argn);
|
||||||
|
janet_table_put(state, janet_ckeywordv("args"),
|
||||||
|
janet_wrap_array(container_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->flags & PFLAG_PARENS || s->flags & PFLAG_SQRBRACKETS) {
|
||||||
|
if (s->flags & PFLAG_ATSYM) {
|
||||||
|
type = "array";
|
||||||
|
} else {
|
||||||
|
type = "tuple";
|
||||||
|
}
|
||||||
|
} else if (s->flags & PFLAG_CURLYBRACKETS) {
|
||||||
|
if (s->flags & PFLAG_ATSYM) {
|
||||||
|
type = "table";
|
||||||
|
} else {
|
||||||
|
type = "struct";
|
||||||
|
}
|
||||||
|
} else if (s->flags & PFLAG_STRING || s->flags & PFLAG_LONGSTRING) {
|
||||||
|
if (s->flags & PFLAG_BUFFER) {
|
||||||
|
type = "buffer";
|
||||||
|
} else {
|
||||||
|
type = "string";
|
||||||
|
}
|
||||||
|
add_buffer = 1;
|
||||||
|
} else if (s->flags & PFLAG_COMMENT) {
|
||||||
|
type = "comment";
|
||||||
|
add_buffer = 1;
|
||||||
|
} else if (s->flags & PFLAG_TOKEN) {
|
||||||
|
type = "token";
|
||||||
|
add_buffer = 1;
|
||||||
|
} else if (s->flags & PFLAG_ATSYM) {
|
||||||
|
type = "at";
|
||||||
|
} else if (s->flags & PFLAG_READERMAC) {
|
||||||
|
int c = s->flags & 0xFF;
|
||||||
|
type = (c == '\'') ? "quote" :
|
||||||
|
(c == ',') ? "unquote" :
|
||||||
|
(c == ';') ? "splice" :
|
||||||
|
(c == '~') ? "quasiquote" : "<reader>";
|
||||||
|
} else {
|
||||||
|
type = "root";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
janet_table_put(state, janet_ckeywordv("type"),
|
||||||
|
janet_ckeywordv(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add_buffer) {
|
||||||
|
buffer = janet_string(buff, bufcount);
|
||||||
|
janet_table_put(state, janet_ckeywordv("buffer"), janet_wrap_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
janet_table_put(state, janet_ckeywordv("start"),
|
||||||
|
janet_wrap_integer(s->start));
|
||||||
|
return janet_wrap_table(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParserStateGetter {
|
||||||
|
const char *name;
|
||||||
|
Janet(*fn)(const JanetParser *p);
|
||||||
|
};
|
||||||
|
|
||||||
|
static Janet parser_state_delimiters(const JanetParser *_p) {
|
||||||
|
JanetParser *clone = janet_abstract(&janet_parse_parsertype, sizeof(JanetParser));
|
||||||
|
janet_parser_clone(_p, clone);
|
||||||
size_t i;
|
size_t i;
|
||||||
const uint8_t *str;
|
const uint8_t *str;
|
||||||
size_t oldcount;
|
size_t oldcount;
|
||||||
janet_fixarity(argc, 1);
|
oldcount = clone->bufcount;
|
||||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
for (i = 0; i < clone->statecount; i++) {
|
||||||
oldcount = p->bufcount;
|
JanetParseState *s = clone->states + i;
|
||||||
for (i = 0; i < p->statecount; i++) {
|
|
||||||
JanetParseState *s = p->states + i;
|
|
||||||
if (s->flags & PFLAG_PARENS) {
|
if (s->flags & PFLAG_PARENS) {
|
||||||
push_buf(p, '(');
|
push_buf(clone, '(');
|
||||||
} else if (s->flags & PFLAG_SQRBRACKETS) {
|
} else if (s->flags & PFLAG_SQRBRACKETS) {
|
||||||
push_buf(p, '[');
|
push_buf(clone, '[');
|
||||||
} else if (s->flags & PFLAG_CURLYBRACKETS) {
|
} else if (s->flags & PFLAG_CURLYBRACKETS) {
|
||||||
push_buf(p, '{');
|
push_buf(clone, '{');
|
||||||
} else if (s->flags & PFLAG_STRING) {
|
} else if (s->flags & PFLAG_STRING) {
|
||||||
push_buf(p, '"');
|
push_buf(clone, '"');
|
||||||
} else if (s->flags & PFLAG_LONGSTRING) {
|
} else if (s->flags & PFLAG_LONGSTRING) {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
for (i = 0; i < s->argn; i++) {
|
for (i = 0; i < s->argn; i++) {
|
||||||
push_buf(p, '`');
|
push_buf(clone, '`');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str = janet_string(p->buf + oldcount, (int32_t)(p->bufcount - oldcount));
|
str = janet_string(clone->buf + oldcount, (int32_t)(clone->bufcount - oldcount));
|
||||||
p->bufcount = oldcount;
|
clone->bufcount = oldcount;
|
||||||
return janet_wrap_string(str);
|
return janet_wrap_string(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Janet parser_state_frames(const JanetParser *p) {
|
||||||
|
size_t i;
|
||||||
|
JanetArray *states = janet_array(p->statecount);
|
||||||
|
states->count = p->statecount;
|
||||||
|
uint8_t *buf = p->buf;
|
||||||
|
Janet *args = p->args;
|
||||||
|
for (i = p->statecount; i > 0; --i) {
|
||||||
|
JanetParseState *s = p->states + (i - 1);
|
||||||
|
states->data[i - 1] = janet_wrap_parse_state(s, args, buf, p->bufcount);
|
||||||
|
args -= s->argn;
|
||||||
|
}
|
||||||
|
return janet_wrap_array(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ParserStateGetter parser_state_getters[] = {
|
||||||
|
{"frames", parser_state_frames},
|
||||||
|
{"delimiters", parser_state_delimiters},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static Janet cfun_parse_state(int32_t argc, Janet *argv) {
|
||||||
|
janet_arity(argc, 1, 2);
|
||||||
|
const uint8_t *key = NULL;
|
||||||
|
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||||
|
if (argc == 2) {
|
||||||
|
key = janet_getkeyword(argv, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
/* Get one result */
|
||||||
|
for (const struct ParserStateGetter *sg = parser_state_getters;
|
||||||
|
sg->name != NULL; sg++) {
|
||||||
|
if (janet_cstrcmp(key, sg->name)) continue;
|
||||||
|
return sg->fn(p);
|
||||||
|
}
|
||||||
|
janet_panicf("unexpected keyword %v", janet_wrap_keyword(key));
|
||||||
|
return janet_wrap_nil();
|
||||||
|
} else {
|
||||||
|
/* Put results in table */
|
||||||
|
JanetTable *tab = janet_table(0);
|
||||||
|
for (const struct ParserStateGetter *sg = parser_state_getters;
|
||||||
|
sg->name != NULL; sg++) {
|
||||||
|
janet_table_put(tab, janet_ckeywordv(sg->name), sg->fn(p));
|
||||||
|
}
|
||||||
|
return janet_wrap_table(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Janet cfun_parse_clone(int32_t argc, Janet *argv) {
|
static Janet cfun_parse_clone(int32_t argc, Janet *argv) {
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
JanetParser *src = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
JanetParser *src = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||||
@ -980,11 +1107,15 @@ static const JanetReg parse_cfuns[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parser/state", cfun_parse_state,
|
"parser/state", cfun_parse_state,
|
||||||
JDOC("(parser/state parser)\n\n"
|
JDOC("(parser/state parser &opt key)\n\n"
|
||||||
"Returns a string representation of the internal state of the parser. "
|
"Returns a representation of the internal state of the parser. If a key is passed, "
|
||||||
"Each byte in the string represents a nested data structure. For example, "
|
"only that information about the state is returned. Allowed keys are:\n\n"
|
||||||
|
"\t:delimiters - Each byte in the string represents a nested data structure. For example, "
|
||||||
"if the parser state is '([\"', then the parser is in the middle of parsing a "
|
"if the parser state is '([\"', then the parser is in the middle of parsing a "
|
||||||
"string inside of square brackets inside parentheses. Can be used to augment a REPL prompt.")
|
"string inside of square brackets inside parentheses. Can be used to augment a REPL prompt."
|
||||||
|
"\t:frames - Each table in the array represents a 'frame' in the parser state. Frames "
|
||||||
|
"contain information about the start of the expression being parsed as well as the "
|
||||||
|
"type of that expression and some type-specific information.")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parser/where", cfun_parse_where,
|
"parser/where", cfun_parse_where,
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
(defn noprompt [_] "")
|
(defn noprompt [_] "")
|
||||||
(defn getprompt [p]
|
(defn getprompt [p]
|
||||||
(def offset (parser/where p))
|
(def offset (parser/where p))
|
||||||
(string "janet:" offset ":" (parser/state p) "> "))
|
(string "janet:" offset ":" (parser/state p :delimiters) "> "))
|
||||||
(def prompter (if *quiet* noprompt getprompt))
|
(def prompter (if *quiet* noprompt getprompt))
|
||||||
(defn getstdin [prompt buf]
|
(defn getstdin [prompt buf]
|
||||||
(file/write stdout prompt)
|
(file/write stdout prompt)
|
||||||
|
Loading…
Reference in New Issue
Block a user