Skip to content

Commit

Permalink
Allow esac as the first pattern of a case branch
Browse files Browse the repository at this point in the history
POSIX has been changed to allow `esac` as the first pattern of a case
branch. This commit implements this change in the parser.
  • Loading branch information
magicant committed Nov 16, 2024
1 parent d05054f commit d1b232e
Show file tree
Hide file tree
Showing 8 changed files with 19 additions and 43 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
- Improved POSIX.1-2024 support:
- An interactive shell no longer exits on an error in the `exec`
built-in, even if the POSIXly-correct mode is on.
- The shell's syntax now always allows `esac` as the first pattern
of a case branch as in `case esac in (esac|case) echo ok; esac`.
Previously, it was a syntax error in the POSIXly-correct mode.
- Updated the sample initialization script (yashrc):
- Added aliases h='fc -l' and j='jobs'.
- Added the wrapper function for `doas` in an attempt to remove the
Expand Down
3 changes: 3 additions & 0 deletions NEWS.ja
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
- POSIX.1-2024 のサポートを強化:
- POSIX 準拠モードであっても、対話シェルが `exec` 組込みで失敗した
ときはシェルを終了しないようにした
- `case` コマンドの分岐の最初のパターンとして `esac` が常に使用
できるようになった。(例: `case esac in (esac|case) echo ok; esac`)
以前は POSIX 準拠モードでは構文エラーとなっていた
- 初期化スクリプト (yashrc) のサンプルを更新:
- エイリアス定義 h='fc -l' and j='jobs' を追加
- doas コマンドのラッパー関数を追加し、端末に紛らわしいウィンドウ
Expand Down
4 changes: 1 addition & 3 deletions doc/ja/syntax.txt
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ Case 文の構文::
Caseitem の構文::
+({{パターン}}) {{コマンド}}...;;+

+case+ と +in+ の間の単語はちょうど一トークンでなければなりません。この単語トークンは予約語としては認識されませんが、+&+ などの記号を含めるには適切な<<quotes,クォート>>が必要です。+in+ と +esac+ の間には任意の個数の caseitem を置きます (0 個でもよい)。Caseitem の最初の +(+ +esac+ の直前の +;;+ は省略できます。また{{コマンド}}が +;+ で終わる場合はその +;+ も省略できます。Caseitem の +)+ と +;;+ との間に{{コマンド}}が一つもなくても構いません。
+case+ と +in+ の間の単語はちょうど一トークンでなければなりません。この単語トークンは予約語としては認識されませんが、+&+ などの記号を含めるには適切な<<quotes,クォート>>が必要です。+in+ と +esac+ の間には任意の個数の caseitem を置きます (0 個でもよい)。Caseitem の最初の +(+ は (最初の{{パターン}}が +esac+ でない限り) 省略できます。最後の{{コマンド}}が改行で終端されている場合は +esac+ の直前の +;;+ は省略できます。Caseitem の +)+ と +;;+ との間に{{コマンド}}が一つもなくても構いません。

Caseitem の{{パターン}}にはトークンを指定します。各トークンを +|+ で区切ることで複数のトークンをパターンとして指定することもできます。

Expand All @@ -232,8 +232,6 @@ Case 文の実行では、まず{{単語}}が{zwsp}link:expand.html[四種展開

Case 文全体の終了ステータスは、実行した{{コマンド}}の終了ステータスです。{{コマンド}}が実行されなかった場合 (どのパターンもマッチしなかったか、caseitem が一つもないか、マッチしたパターンの後にコマンドがない場合) は、終了ステータスは 0 です。

link:posix.html[POSIX 準拠モード]では、(+|+ で区切られた最初の) {{パターン}}トークンを +esac+ にすることはできません。

[[double-bracket]]
=== 二重ブラケットコマンド

Expand Down
9 changes: 3 additions & 6 deletions doc/syntax.txt
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,9 @@ The {{word}} between the +case+ and +in+ tokens must be exactly one word. The
{{word}} is not treated as a keyword, but you need to <<quotes,quote>>
separator characters (such as +&+ and +|+) to include them as part of the
{{word}}. Between the +in+ and +esac+ tokens you can put any number of case
items (may be none). You can omit the first +(+ token of a case item and the
last +;;+ token before the +esac+ token. If the last {{command}} of a case
item is terminated by a semicolon, you can omit the semicolon as well. The
items (may be none). You can omit the first +(+ token of a case item unless
the first pattern is +esac+. If the last {{command}} is terminated by a
linebreak, you can omit the last +;;+ token before the +esac+ token. The
{{command}}s in a case item may be empty.

The {{patterns}} in a case item are one or more tokens each separated by a +|+
Expand All @@ -399,9 +399,6 @@ exit status is zero if no
{{command}}s were executed, that is, there were no case items, no matching
pattern was found, or no commands were associated with the matching pattern.

In the link:posix.html[POSIXly-correct mode], the first pattern in a case item
cannot be +esac+ (even if you do not omit the +(+ token).

[[double-bracket]]
=== Double-bracket command

Expand Down
8 changes: 2 additions & 6 deletions parser.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Yash: yet another shell */
/* parser.c: syntax parser */
/* (C) 2007-2023 magicant */
/* (C) 2007-2024 magicant */

/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -2691,11 +2691,7 @@ void **parse_case_patterns(parsestate_T *ps)

if (ps->tokentype == TT_LPAREN) { /* ignore the first '(' */
next_token(ps);
do {
if (posixly_correct && ps->tokentype == TT_ESAC)
serror(ps,
Ngt("an unquoted `esac' cannot be the first case pattern"));
} while (psubstitute_alias(ps, 0));
psubstitute_alias_recursive(ps, 0);
}

const wchar_t *predecessor = L"(";
Expand Down
14 changes: 0 additions & 14 deletions tests/alias-y.tst
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,6 @@ __ERR__
#'
#`
(
posix="true"
test_Oe -e 2 'reserved word esac as pattern (-o POSIX)'
alias CASE='case esac in ( ' ESAC='Esac' Esac='esac'
CASE ESAC ) echo not reached; esac
__IN__
syntax error: an unquoted `esac' cannot be the first case pattern
__ERR__
#'
#`

)

(
if ! testee -c 'command -v [[' >/dev/null; then
skip="true"
Expand Down
8 changes: 7 additions & 1 deletion tests/case-p.tst
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,13 @@ test_reserved_word_as_pattern "$LINENO" then
test_reserved_word_as_pattern "$LINENO" until
test_reserved_word_as_pattern "$LINENO" while

test_oE 'reserved word esac as (non-first) pattern'
test_oE 'esac as first pattern'
case esac in (esac) echo matched;; esac
__IN__
matched
__OUT__

test_oE 'esac as non-first pattern'
case esac in -|esac) echo matched;; esac
__IN__
matched
Expand Down
13 changes: 0 additions & 13 deletions tests/case-y.tst
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
# case-y.tst: yash-specific test of case command

(
posix="true"

test_Oe -e 2 'reserved word esac as pattern (-o POSIX)'
case x in (esac) echo not reached; esac
__IN__
syntax error: an unquoted `esac' cannot be the first case pattern
__ERR__
#'
#`
)
test_oe 'patterns separated by | are expanded and matched in order'
case 1 in
$(echo expanded 0 >&2; echo 0) |\
Expand Down

0 comments on commit d1b232e

Please sign in to comment.