Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: case pattern parsing



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