Skip to content

fix(declare): bash declaration engine, locals, attributes and set options (105 BashBox cases) - #2597

Merged
chaliy merged 22 commits into
mainfrom
claude/project-thread-eafupr-bb-vars
Oct 8, 2026
Merged

chaliy merged 22 commits into
mainfrom
claude/project-thread-eafupr-bb-vars

Conversation

@chaliy

@chaliy chaliy commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

declare/typeset/local/readonly/export now share one declaration engine that follows bash: attributes (-i -l -u -n -r -x -a -A -g -I) are applied and printed like bash (declare -p, declare -f), locals use bash's dynamic scoping (shallow binding, local - restoring options), and namerefs resolve through scopes.

Also in this batch:

  • Arithmetic uses bash's grammar evaluator. Errors such as $((1/0)) abort the line with bash's message.
  • [[ -v name ]], [[ -v a[i] ]], scalars used as arrays (${x[0]}), and read name validation.
  • Full set -o/shopt -o option set, $- in bash order, BASH_SUBSHELL also counting <(...), PIPESTATUS edge cases, subshell env isolation.
  • Array literals brace- and glob-expand in every form: a=(x{1,2} *.txt), a+=(...), declare -a/local -a/typeset -a. Quoted words and assoc keyed values stay literal, like bash.

Why

Third step of adopting the BashBox corpus (#2575): its bash-verified cases showed declare/local/attribute behavior as the largest gap cluster.

Before / After

BashBox corpus cases still skipped: 420 → 315 (105 unskipped: declare 39, attributes 26, local 20, arithmetic 9, read 8, shell 3).

Comparison against host bash (LANG=C.UTF-8): only the known environment-specific mismatches remain (read_status_at_eof, empty_heredoc; they depend on the container's /dev/null).

Example now passing (was skipped), output matches bash 5.2:

$ declare -i x=1+2; x+=3; echo $x; x='2*3'; echo $x; x=abc; echo $x; x=; echo $x; declare -p x
6
6
0
0
declare -i x="0"

Array literals, new spec cases in arrays.test.sh checked against host bash: declare -a d=(x{1,2}) gives x1 x2.

Risk

  • Medium
  • Touches hot paths: Interpreter.env is now an Arc (copy-on-write, subshells share it), locals use shallow binding. parallel_execution bench: every workload within 3% of main (results in crates/bashkit/benches/results/).
  • Array literal expansion is still capped by the array-entry budget (new tm_dos_060_array_literal_brace_expansion_respects_budget).
  • Known gaps left: arr=($v) does not glob split fields yet.

Checklist

  • Tests added or updated
  • Backward compatibility considered

Generated by Claude Code

chaliy added 12 commits October 7, 2026 16:35
Copies (script, stdout) pairs from shipfastlabs/bashbox (MIT) into
spec_cases/bash/bashbox-*.test.sh, keeping only cases whose recorded
output matches host bash. 624 pass and join the bash comparison gate;
420 are marked TODO skips: real gaps where bashkit differs from bash.
Attribution in NOTICE; process in knowledge/operations/testing.md.
Replace the string-splitting arithmetic evaluator with a tokenizer and a
precedence-climbing parser that follows bash's expr.c grammar (comma,
assignment operators, ternary, short-circuit with noeval, right-assoc
`**`, base#digits up to 64, pre/post increment on elements).

Errors now behave as in bash: `$((...))` errors print the bash message
and abort the rest of the current line (new ControlFlow::Abort /
Error::LineAbort); `((...))` and `let` return status 1. The body of a
`((...))` command is read raw so `<<=`/`>>` are not lexed as redirects.

Unskips the bashbox-arithmetic-array cases and
bashbox_declare_a_subshell_ends_with_status_1.
declare/typeset/local/export/readonly now run through one engine
(interpreter/declare.rs) with bash's option sets, error messages and
declare -p output (quoting, assoc arrays in bash hash order).

Locals use shallow binding saved in the call frame: nested functions,
unset of a caller's local, declare -g past a local and local -n follow
bash. Compound operands stay one CompoundAssignment word part, so keyed
elements with quotes or backticks lex as one element and analysis walks
substitutions inside them. Readonly assignment and circular namerefs
abandon the line as in bash.

Unskips 60 bashbox declare/attributes/local-scope/read-names cases.
A scalar is element 0 of an array for ${x[@]}, ${#x[@]}, ${!x[@]},
${x[0]} and arithmetic element writes. Associative arrays expand in
bash's hash order. [[ -v ]] was missing and is now implemented.
${!ref} resolves a nameref chain, a nameref for-loop variable is
re-pointed per word, and builtin assignment through a circular nameref
fails the command. read takes options only before names, stops at an
invalid name, validates -a names and supports -N.

Unskips 15 bashbox declare/local-scope/read-names cases.
set knows every set -o option and letter, validates before changing
anything, lets each o in a cluster take the next argument and lists
options for a trailing -o; shopt -o/-po operate on them. $- uses bash's
letter order and defaults (hBc). BASH_SUBSHELL counts subshells, command
and process substitutions. ( ), [[ ]] and (( )) set PIPESTATUS. The
exported environment is snapshotted with subshell state, so (E=2) no
longer leaks. local LINENO is an ordinary variable and cd with HOME
unset fails.

Unskips 12 bashbox attributes/local-scope/shell-variables cases; the
remaining declare skips name their reason (printf -v, ANSI-C bytes).
…-eafupr-bb-vars

# Conflicts:
#	THIRD_PARTY_LICENSES/README.md
#	crates/bashkit/src/builtins/read.rs
#	crates/bashkit/src/builtins/vars.rs
#	crates/bashkit/src/interpreter/mod.rs
#	crates/bashkit/src/parser/lexer.rs
#	crates/bashkit/tests/spec_cases/bash/bashbox-arithmetic-array.test.sh
#	crates/bashkit/tests/spec_cases/bash/bashbox-attributes.test.sh
#	crates/bashkit/tests/spec_cases/bash/bashbox-declare.test.sh
#	crates/bashkit/tests/spec_cases/bash/bashbox-local-scope.test.sh
#	crates/bashkit/tests/spec_cases/bash/bashbox-read-names.test.sh
#	crates/bashkit/tests/spec_cases/bash/bashbox-shell-variables.test.sh
#	knowledge/log.md
…-eafupr-bb-vars

# Conflicts:
#	crates/bashkit/src/interpreter/mod.rs
#	crates/bashkit/tests/integration/blackbox_security_tests.rs
#	knowledge/log.md
Shared by a=(...), a+=(...), declare -a, typeset -a and local -a.
Mixed quoted words like "x"{1,2} expand; keyed assoc values stay
literal; [k]=v{1,2} lexes as one word.
…-eafupr-bb-vars

# Conflicts:
#	crates/bashkit/src/interpreter/mod.rs
#	knowledge/log.md
@chaliy chaliy self-assigned this Oct 7, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit b5b8196 Commit Preview URL

Branch Preview URL
Oct 08 2026, 12:12 AM

chaliy added 10 commits October 7, 2026 21:01
A function call is the recursion path. Dispatching it from the same
future as the builtin and PATH-search arms put that whole frame on the
stack at every call level, so 16 nested calls overflowed a 2 MiB stack
in debug builds. Functions now get their own small future, and prefix
assignments are boxed. Debug CLI now reaches the depth limit with
1.75 MiB of stack (main needed more than 1.94 MiB).
…-eafupr-bb-vars

# Conflicts:
#	crates/bashkit/src/interpreter/mod.rs
#	knowledge/log.md
…-eafupr-bb-vars

# Conflicts:
#	knowledge/log.md
…-eafupr-bb-vars

# Conflicts:
#	knowledge/log.md
…-eafupr-bb-vars

# Conflicts:
#	knowledge/log.md
…-eafupr-bb-vars

# Conflicts:
#	knowledge/log.md
@chaliy
chaliy merged commit daae80a into main Oct 8, 2026
44 checks passed
@chaliy
chaliy deleted the claude/project-thread-eafupr-bb-vars branch October 8, 2026 00:39
chaliy added a commit that referenced this pull request Oct 8, 2026
The behavior landed with the declaration engine (#2597); these cases pin it:
a callee's `unset` uncovers the caller's binding for scalars and arrays, and a
local unset in its own scope stays invisible for the rest of the call.

Claude-Session: https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant