summaryrefslogtreecommitdiffstats
path: root/parser.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2017-06-16 23:18:48 -0700
committerKaz Kylheku <kaz@kylheku.com>2017-06-16 23:18:48 -0700
commit7947bf8dcdde549456493295f12ab03b5ec852b2 (patch)
tree785254241c5625e390acf84afb19826e3782712d /parser.c
parent5da101a52afd8d99f7ede8ef8fbf61afbd52cb02 (diff)
downloadtxr-7947bf8dcdde549456493295f12ab03b5ec852b2.tar.gz
txr-7947bf8dcdde549456493295f12ab03b5ec852b2.tar.bz2
txr-7947bf8dcdde549456493295f12ab03b5ec852b2.zip
listener: inform linenoise of incomplete syntax.
Multi-line expressions can now be entered just using Enter for line breaks without Ctrl-V. Multi-line mode becomes default. * parser.c (is_balanced_line): New static function. (repl): Install is_balanced_line as enter callback in linenoise object. (parse_init): Default *listener-multi-line-p* variable to t. * txr.1: Documentation about multi-line mode updated.
Diffstat (limited to 'parser.c')
-rw-r--r--parser.c152
1 files changed, 151 insertions, 1 deletions
diff --git a/parser.c b/parser.c
index 11bf27f7..3597c0e9 100644
--- a/parser.c
+++ b/parser.c
@@ -929,6 +929,155 @@ static val repl_warning(val out_stream, val exc, struct args *rest)
uw_throw(continue_s, nil);
}
+static int is_balanced_line(const char *line, void *ctx)
+{
+ enum state {
+ ST_START, ST_CMNT, ST_PAR, ST_BKT, ST_BRC, ST_HASH,
+ ST_LIT, ST_QLIT, ST_RGX, ST_CHR, ST_ESC, ST_AT
+ };
+ int count[32], sp = 0;
+ enum state state[32];
+ count[sp] = 0;
+ state[sp] = ST_START;
+ char ch;
+
+ while ((ch = *line++) != 0) {
+ again:
+ if (sp >= 30)
+ return 1;
+
+ count[sp+1] = 0;
+ count[sp+2] = 0;
+
+ switch (state[sp]) {
+ case ST_START:
+ case ST_PAR:
+ case ST_BKT:
+ case ST_BRC:
+ switch (ch) {
+ case ';':
+ state[++sp] = ST_CMNT;
+ break;
+ case '#':
+ state[++sp] = ST_HASH;
+ break;
+ case '"':
+ state[++sp] = ST_LIT;
+ break;
+ case '`':
+ state[++sp] = ST_QLIT;
+ break;
+ case '(':
+ if (state[sp] == ST_PAR)
+ count[sp]++;
+ else
+ state[++sp] = ST_PAR;
+ break;
+ case '[':
+ if (state[sp] == ST_BKT)
+ count[sp]++;
+ else
+ state[++sp] = ST_BKT;
+ break;
+ case ')': case ']': case '}':
+ {
+ enum state match = state[sp];
+
+ while (sp > 0 && state[sp] != match)
+ sp--;
+ if (state[sp] != match)
+ return 1;
+ if (count[sp] == 0)
+ sp--;
+ else
+ count[sp]--;
+ break;
+ }
+ }
+ break;
+ case ST_CMNT:
+ if (ch == '\r')
+ sp--;
+ break;
+ case ST_HASH:
+ switch (ch) {
+ case '\\':
+ state[sp] = ST_CHR;
+ break;
+ case '/':
+ state[sp] = ST_RGX;
+ break;
+ case ';':
+ --sp;
+ break;
+ default:
+ --sp;
+ goto again;
+ }
+ break;
+ case ST_LIT:
+ switch (ch) {
+ case '"':
+ sp--;
+ break;
+ case '\\':
+ state[++sp] = ST_ESC;
+ break;
+ }
+ break;
+ case ST_QLIT:
+ switch (ch) {
+ case '`':
+ sp--;
+ break;
+ case '\\':
+ state[++sp] = ST_ESC;
+ break;
+ case '@':
+ state[++sp] = ST_AT;
+ break;
+ }
+ break;
+ case ST_RGX:
+ switch (ch) {
+ case '/':
+ sp--;
+ break;
+ case '\\':
+ state[++sp] = ST_ESC;
+ break;
+ }
+ break;
+ case ST_CHR:
+ --sp;
+ break;
+ case ST_ESC:
+ --sp;
+ break;
+ case ST_AT:
+ switch (ch) {
+ case '(':
+ state[sp] = ST_PAR;
+ break;
+ case '[':
+ state[sp] = ST_BKT;
+ break;
+ case '{':
+ state[sp] = ST_BRC;
+ break;
+ default:
+ sp--;
+ break;
+ }
+ }
+ }
+
+ if (state[sp] == ST_CMNT)
+ sp--;
+
+ return sp == 0 && state[sp] == ST_START && count[sp] == 0;
+}
+
val repl(val bindings, val in_stream, val out_stream)
{
val ifd = stream_get_prop(in_stream, fd_k);
@@ -969,6 +1118,7 @@ val repl(val bindings, val in_stream, val out_stream)
lino_set_completion_cb(ls, provide_completions, 0);
lino_set_atom_cb(ls, provide_atom, 0);
+ lino_set_enter_cb(ls, is_balanced_line, 0);
lino_set_tempfile_suffix(ls, ".tl");
if (rcfile)
@@ -1144,7 +1294,7 @@ void parse_init(void)
stream_parser_hash = make_hash(t, nil, nil);
parser_l_init();
reg_var(listener_hist_len_s, num_fast(500));
- reg_var(listener_multi_line_p_s, nil);
+ reg_var(listener_multi_line_p_s, t);
reg_var(listener_sel_inclusive_p_s, nil);
reg_fun(circref_s, func_n1(circref));
}