Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: case pattern parsing
- X-seq: zsh-workers 35168
- From: Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: PATCH: case pattern parsing
- Date: Sun, 17 May 2015 21:53:41 +0100
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
This patch parses case patterns properly, removing the hack that
alternation was done by "|" within a single pattern that was shifted
around to remove spaces. It should also means cases with and without a
leading "(" are parsed the same way.
Because of the change in wordcode I've updated the version number.
I have some ideas about how to get back (most of) the feature that it
was possible to use multiple words in a pattern, but this time with some
help from the lexer. However, that's not in yet and I don't have time
to do it right now. I've updated all the case statements in completion
functions I could see so they should work anyway.
As an additional bonus, I can now read the loop.c code that handled
cases without gibbering loudly about the programme counter arithmetic.
pws
diff --git a/Completion/Unix/Command/_ant b/Completion/Unix/Command/_ant
index 8e4bd82..ee9f7d9 100644
--- a/Completion/Unix/Command/_ant
+++ b/Completion/Unix/Command/_ant
@@ -123,8 +123,7 @@ case $state in
# Output target again indicating its the default one.
print -n "'${default_target}:(Default target) ' "
;;
- (Searching:*|Main:targets:|Subtargets:|BUILD:SUCCESSFUL|Total:time:
- *)
+ (Searching:*|Main:targets:|Subtargets:|BUILD:SUCCESSFUL|Total:time:*)
;;
(*)
# Return target and description
diff --git a/Completion/Unix/Command/_cp b/Completion/Unix/Command/_cp
index 4c4dea2..7087b4e 100644
--- a/Completion/Unix/Command/_cp
+++ b/Completion/Unix/Command/_cp
@@ -13,7 +13,7 @@ if _pick_variant gnu=GNU unix --version; then
'-H[follow command-line symbolic links]' \
'(-l --link)'{-l,--link}'[link files instead of copying]' \
'(-L --dereference)'{-L,--dereference}'[always follow symbolic links]' \
- (-n --no-clobber -i --interactive){-n,--no-clobber}"[don't overwrite an existing file]" \
+ '(-n --no-clobber -i --interactive)'{-n,--no-clobber}"[don't overwrite an existing file]" \
'(-P --no-dereference)'{-P,--no-dereference}'[never follow symbolic links]' \
'-p[same as --preserve=mode,ownership,timestamps]' \
'--preserve=-[preserve specified attributes]:: :_values -s , attribute mode timestamps ownership links context xattr all' \
diff --git a/Completion/Unix/Command/_locate b/Completion/Unix/Command/_locate
index 0b35b7c..694f506 100644
--- a/Completion/Unix/Command/_locate
+++ b/Completion/Unix/Command/_locate
@@ -25,7 +25,7 @@ case $basename in
ltype=gnu
;;
- (*illegal option*)
+ (*"illegal option"*)
if [[ $OSTYPE == (freebsd|openbsd|dragonfly|darwin)* ]]; then
ltype=bsd
else
diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make
index 225c0af..c14a34c 100644
--- a/Completion/Unix/Command/_make
+++ b/Completion/Unix/Command/_make
@@ -65,7 +65,7 @@ _make-parseMakefile () {
do
case "$input " in
# VARIABLE = value OR VARIABLE ?= value
- ([[:alnum:]][[:alnum:]_]#[ $TAB]#(\?|)=*)
+ ([[:alnum:]][[:alnum:]_]#[" "$TAB]#(\?|)=*)
var=${input%%[ $TAB]#(\?|)=*}
val=${input#*=}
val=${val##[ $TAB]#}
@@ -74,7 +74,7 @@ _make-parseMakefile () {
# VARIABLE := value OR VARIABLE ::= value
# Evaluated immediately
- ([[:alnum:]][[:alnum:]_]#[ $TAB]#:(:|)=*)
+ ([[:alnum:]][[:alnum:]_]#[" "$TAB]#:(:|)=*)
var=${input%%[ $TAB]#:(:|)=*}
val=${input#*=}
val=${val##[ $TAB]#}
@@ -97,7 +97,7 @@ _make-parseMakefile () {
;;
# Include another makefile
- (${~incl} *)
+ (${~incl}" "*)
local f=${input##${~incl} ##}
if [[ $incl == '.include' ]]
then
diff --git a/Completion/Unix/Command/_tar b/Completion/Unix/Command/_tar
index ce58524..1e99ac0 100644
--- a/Completion/Unix/Command/_tar
+++ b/Completion/Unix/Command/_tar
@@ -23,7 +23,7 @@ local _tar_cmd tf tmp tmpb del index
if _pick_variant gnu=GNU unix --version; then
case "$($service --version)" in
- (tar \(GNU tar\) (#b)([0-9.-]##)*)
+ ("tar (GNU tar) "(#b)([0-9.-]##)*)
autoload -z is-at-least
is-at-least 1.14.91 "$match[1]" || _cmd_variant[$service]="gnu-old"
;;
diff --git a/Completion/Unix/Type/_path_commands b/Completion/Unix/Type/_path_commands
index 22d2aae..423563c 100644
--- a/Completion/Unix/Type/_path_commands
+++ b/Completion/Unix/Type/_path_commands
@@ -25,7 +25,7 @@ return 1
_call_whatis() {
case "$(whatis --version)" in
- (whatis from *)
+ ("whatis from "*)
local -A args
zparseopts -D -A args s: r:
apropos "${args[-r]:-"$@"}" | fgrep "($args[-s]"
diff --git a/Completion/X/Command/_xrandr b/Completion/X/Command/_xrandr
index 9d9323c..b085156 100644
--- a/Completion/X/Command/_xrandr
+++ b/Completion/X/Command/_xrandr
@@ -51,7 +51,7 @@ _arguments -C \
case $state in
value)
case $words[CURRENT-1] in
- (scaling* mode)
+ (scaling*" mode")
_description value expl "output property 'scaling mode'"
compadd "$@" "$expl[@]" None Full Center Full\ aspect && return 0
;;
diff --git a/Config/version.mk b/Config/version.mk
index d04d57d..acab748 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
# This must also serve as a shell script, so do not add spaces around the
# `=' signs.
-VERSION=5.0.7-dev-2
-VERSION_DATE='May 5, 2015'
+VERSION=5.0.7-dev-3
+VERSION_DATE='May 17, 2015'
diff --git a/Src/lex.c b/Src/lex.c
index 841fb0b..87b0cd3 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -761,6 +761,8 @@ gettok(void)
lexstop = 0;
return BAR;
case LX1_INPAR:
+ if (incasepat == 2)
+ return INPAR;
d = hgetc();
if (d == '(') {
if (infor) {
diff --git a/Src/loop.c b/Src/loop.c
index d025fbb..e4e8e2d 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -545,7 +545,7 @@ execcase(Estate state, int do_exec)
Wordcode end, next;
wordcode code = state->pc[-1];
char *word, *pat;
- int npat, save;
+ int npat, save, nalts, ialt, patok;
Patprog *spprog, pprog;
end = state->pc + WC_CASE_SKIP(code);
@@ -561,60 +561,74 @@ execcase(Estate state, int do_exec)
if (wc_code(code) != WC_CASE)
break;
- pat = NULL;
- pprog = NULL;
save = 0;
- npat = state->pc[1];
- spprog = state->prog->pats + npat;
-
next = state->pc + WC_CASE_SKIP(code);
+ nalts = *state->pc++;
+ ialt = patok = 0;
if (isset(XTRACE)) {
- char *opat;
-
- pat = dupstring(opat = ecrawstr(state->prog, state->pc, NULL));
- singsub(&pat);
- save = (!(state->prog->flags & EF_HEAP) &&
- !strcmp(pat, opat) && *spprog != dummy_patprog2);
-
printprompt4();
fprintf(xtrerr, "case %s (", word);
- quote_tokenized_output(pat, xtrerr);
- fprintf(xtrerr, ")\n");
- fflush(xtrerr);
}
- state->pc += 2;
- if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
- pprog = *spprog;
-
- if (!pprog) {
- if (!pat) {
- char *opat;
+ while (!patok && nalts) {
+ npat = state->pc[1];
+ spprog = state->prog->pats + npat;
+ pprog = NULL;
+ pat = NULL;
+
+ if (isset(XTRACE)) {
int htok = 0;
-
- pat = dupstring(opat = ecrawstr(state->prog,
- state->pc - 2, &htok));
+ pat = dupstring(ecrawstr(state->prog, state->pc, &htok));
if (htok)
singsub(&pat);
- save = (!(state->prog->flags & EF_HEAP) &&
- !strcmp(pat, opat) && *spprog != dummy_patprog2);
+
+ if (ialt++)
+ fprintf(stderr, " | ");
+ quote_tokenized_output(pat, xtrerr);
}
- if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
- NULL)))
- zerr("bad pattern: %s", pat);
- else if (save)
- *spprog = pprog;
+
+ if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
+ pprog = *spprog;
+
+ if (!pprog) {
+ if (!pat) {
+ char *opat;
+ int htok = 0;
+
+ pat = dupstring(opat = ecrawstr(state->prog,
+ state->pc, &htok));
+ if (htok)
+ singsub(&pat);
+ save = (!(state->prog->flags & EF_HEAP) &&
+ !strcmp(pat, opat) && *spprog != dummy_patprog2);
+ }
+ if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
+ NULL)))
+ zerr("bad pattern: %s", pat);
+ else if (save)
+ *spprog = pprog;
+ }
+ if (pprog && pattry(pprog, word))
+ patok = 1;
+ state->pc += 2;
+ nalts--;
+ }
+ state->pc += 2 * nalts;
+ if (isset(XTRACE)) {
+ fprintf(xtrerr, ")\n");
+ fflush(xtrerr);
}
- if (pprog && pattry(pprog, word)) {
+ if (patok) {
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
do_exec));
while (!retflag && wc_code(code) == WC_CASE &&
WC_CASE_TYPE(code) == WC_CASE_AND) {
state->pc = next;
- code = *state->pc;
- state->pc += 3;
- next = state->pc + WC_CASE_SKIP(code) - 2;
+ code = *state->pc++;
+ next = state->pc + WC_CASE_SKIP(code);
+ nalts = *state->pc++;
+ state->pc += 2 * nalts;
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
do_exec));
}
diff --git a/Src/parse.c b/Src/parse.c
index 985eb8e..c938d2d 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -349,9 +349,8 @@ ecadd(wordcode c)
eclen += a;
}
ecbuf[ecused] = c;
- ecused++;
- return ecused - 1;
+ return ecused++;
}
/* Delete a wordcode. */
@@ -1128,7 +1127,7 @@ par_for(int *cmplx)
static void
par_case(int *cmplx)
{
- int oecused = ecused, brflag, p, pp, n = 1, type;
+ int oecused = ecused, brflag, p, pp, palts, type, nalts;
int ona, onc;
p = ecadd(0);
@@ -1153,7 +1152,7 @@ par_case(int *cmplx)
YYERRORV(oecused);
}
brflag = (tok == INBRACE);
- incasepat = 1;
+ incasepat = 2;
incmdpos = 0;
noaliases = ona;
nocorrect = onc;
@@ -1166,8 +1165,10 @@ par_case(int *cmplx)
zshlex();
if (tok == OUTBRACE)
break;
- if (tok == INPAR)
+ if (tok == INPAR) {
+ incasepat = 1;
zshlex();
+ }
if (tok != STRING)
YYERRORV(oecused);
if (!strcmp(tokstr, "esac"))
@@ -1176,89 +1177,45 @@ par_case(int *cmplx)
incasepat = 0;
incmdpos = 1;
type = WC_CASE_OR;
+ pp = ecadd(0);
+ palts = ecadd(0);
+ nalts = 0;
for (;;) {
+ ecstr(str);
+ ecadd(ecnpats++);
+ nalts++;
+
zshlex();
if (tok == OUTPAR) {
incasepat = 0;
incmdpos = 1;
zshlex();
break;
- } else if (tok == BAR) {
- char *str2;
- int sl = strlen(str);
-
- incasepat = 1;
- incmdpos = 0;
- str2 = hcalloc(sl + 2);
- strcpy(str2, str);
- str2[sl] = Bar;
- str2[sl+1] = '\0';
- str = str2;
- } else {
- int sl = strlen(str);
-
- if (!sl || str[sl - 1] != Bar) {
- /* POSIX allows (foo*) patterns */
- int pct;
- char *s;
-
- for (s = str, pct = 0; *s; s++) {
- if (*s == Inpar)
- pct++;
- if (!pct)
- break;
- if (pct == 1) {
- if (*s == Bar || *s == Inpar)
- while (iblank(s[1]))
- chuck(s+1);
- if (*s == Bar || *s == Outpar)
- while (iblank(s[-1]) &&
- (s < str + 1 || s[-2] != Meta))
- chuck(--s);
- }
- if (*s == Outpar)
- pct--;
- }
- if (*s || pct || s == str)
- YYERRORV(oecused);
- /* Simplify pattern by removing surrounding (...) */
- sl = strlen(str);
- DPUTS(*str != Inpar || str[sl - 1] != Outpar,
- "BUG: strange case pattern");
- str[sl - 1] = '\0';
- chuck(str);
- break;
- } else {
- char *str2;
-
- if (tok != STRING)
- YYERRORV(oecused);
- str2 = hcalloc(sl + strlen(tokstr) + 1);
- strcpy(str2, str);
- strcpy(str2 + sl, tokstr);
- str = str2;
- }
- }
+ } else if (tok != BAR)
+ YYERRORV(oecused);
+
+ zshlex();
+ if (tok != STRING)
+ YYERRORV(oecused);
+ str = dupstring(tokstr);
}
- pp = ecadd(0);
- ecstr(str);
- ecadd(ecnpats++);
par_save_list(cmplx);
- n++;
if (tok == SEMIAMP)
type = WC_CASE_AND;
else if (tok == SEMIBAR)
type = WC_CASE_TESTAND;
ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp);
+ ecbuf[palts] = nalts;
if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag))
break;
if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR)
YYERRORV(oecused);
- incasepat = 1;
+ incasepat = 2;
incmdpos = 0;
zshlex();
}
incmdpos = 1;
+ incasepat = 0;
zshlex();
ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p);
diff --git a/Src/text.c b/Src/text.c
index b58c251..958303c 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -602,6 +602,7 @@ gettext2(Estate state)
case WC_CASE:
if (!s) {
Wordcode end = state->pc + WC_CASE_SKIP(code);
+ wordcode nalts;
taddstr("case ");
taddstr(ecgetstr(state, EC_NODUP, NULL));
@@ -622,8 +623,13 @@ gettext2(Estate state)
taddchr(' ');
taddstr("(");
code = *state->pc++;
- taddstr(ecgetstr(state, EC_NODUP, NULL));
- state->pc++;
+ nalts = *state->pc++;
+ while (nalts--) {
+ taddstr(ecgetstr(state, EC_NODUP, NULL));
+ state->pc++;
+ if (nalts)
+ taddstr(" | ");
+ }
taddstr(") ");
tindent++;
n = tpush(code, 0);
@@ -631,6 +637,7 @@ gettext2(Estate state)
n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end);
}
} else if (state->pc < s->u._case.end) {
+ wordcode nalts;
dec_tindent();
switch (WC_CASE_TYPE(code)) {
case WC_CASE_OR:
@@ -638,11 +645,11 @@ gettext2(Estate state)
break;
case WC_CASE_AND:
- taddstr(";&");
+ taddstr(" ;&");
break;
default:
- taddstr(";|");
+ taddstr(" ;|");
break;
}
if (tnewlins)
@@ -651,8 +658,13 @@ gettext2(Estate state)
taddchr(' ');
taddstr("(");
code = *state->pc++;
- taddstr(ecgetstr(state, EC_NODUP, NULL));
- state->pc++;
+ nalts = *state->pc++;
+ while (nalts--) {
+ taddstr(ecgetstr(state, EC_NODUP, NULL));
+ state->pc++;
+ if (nalts)
+ taddstr(" | ");
+ }
taddstr(") ");
tindent++;
s->code = code;
@@ -666,11 +678,11 @@ gettext2(Estate state)
break;
case WC_CASE_AND:
- taddstr(";&");
+ taddstr(" ;&");
break;
default:
- taddstr(";|");
+ taddstr(" ;|");
break;
}
dec_tindent();
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index 2de2919..1ba0a54 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -572,6 +572,7 @@
$ZTST_testdir/../Src/zsh -f myscript
127q:PATHSCRIPT option not used.
?$ZTST_testdir/../Src/zsh: can't open input file: myscript
+# '
$ZTST_testdir/../Src/zsh -fc 'echo $0; echo $1' myargzero myargone
0:$0 is traditionally if bizarrely set to the first argument with -c
@@ -611,3 +612,41 @@
>BEGIN
>mytrue
>END
+
+ fn() {
+ case $1 in
+ ( one | two | three )
+ print Matched $1
+ ;;
+ ( fo* | fi* | si* )
+ print Pattern matched $1
+ ;;
+ ( []x | a[b]* )
+ print Character class matched $1
+ ;;
+ esac
+ }
+ which fn
+ fn one
+ fn two
+ fn three
+ fn four
+ fn five
+ fn six
+ fn abecedinarian
+ fn xylophone
+0: case word handling
+>fn () {
+> case $1 in
+> (one | two | three) print Matched $1 ;;
+> (fo* | fi* | si*) print Pattern matched $1 ;;
+> ([]x | a[b]*) print Character class matched $1 ;;
+> esac
+>}
+>Matched one
+>Matched two
+>Matched three
+>Pattern matched four
+>Pattern matched five
+>Pattern matched six
+>Character class matched abecedinarian
Messages sorted by:
Reverse Date,
Date,
Thread,
Author