Skip to content

Claude/advance project y32 it#17

Merged
hyperpolymath merged 5 commits into
mainfrom
claude/advance-project-Y32IT
Apr 20, 2026
Merged

Claude/advance project y32 it#17
hyperpolymath merged 5 commits into
mainfrom
claude/advance-project-Y32IT

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

No description provided.

claude added 5 commits April 16, 2026 13:28
Two long-standing correctness holes in shell-function support:

1. Function bodies containing control structures (`if/fi`, `for/done`,
   `while/done`, `case/esac`) were fragmented. `parse_function_def`
   split the body naively on `;` and `\n`, so `f() { if x; then y; fi; }`
   became `["if x", "then y", "fi"]` — three strings that failed to
   parse individually. The body is now stored as a raw string between
   the outermost braces, with proper brace-depth tracking that respects
   single/double quotes. At call time `execute_function_call` uses the
   existing control-structure-aware `split_on_semicolons` so a function
   body parses the same as any other script fragment.

2. `return` inside nested control structures did nothing. The function
   executor detected returns with `cmd_str.starts_with("return")`,
   which never matched when `return` was buried inside an `if`, `for`,
   or `while`. Introduces `ExecutionResult::Return { exit_code }` — a
   sentinel that propagates through every control-structure handler
   (`Command::If`, `WhileLoop`, `ForLoop`, `LogicalOp`, `Source`,
   `execute_block`) until it reaches `execute_function_call`, which
   converts it to a regular exit-code result. The fragile string-match
   detection is gone.

Also:

- `tests/function_control_flow_tests.rs`: 8 regression tests covering
  if/for/case in function bodies, `return` from nested `if`/`for`, and
  a brace-in-quoted-string edge case.
- `tests/security_tests.rs`: `security_no_privilege_escalation` now
  skips cleanly when running as root (with an optional
  `VSH_ALLOW_ROOT_TESTS=1` override) instead of panicking. The test was
  a meta-check about the test environment, not correctness, and was
  making the suite non-portable to containerised CI.

Test results: 703 passing, 0 failing, 14 ignored (up from 602).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
…source

Previously `execute_script_content` and `Command::Source` iterated
`content.lines()` and parsed each line in isolation, so the canonical
POSIX shape

    if true
    then
      mkdir d
    fi

shredded into three lines that failed to parse individually. The same
went for `for/do/done`, `while/do/done`, `case/esac`, and multi-line
function bodies.

Changes:

- `parser.rs`: extract `split_on_top_level` from `split_on_semicolons`
  and add `split_on_statement_separators`, which additionally treats
  top-level `\n` as a statement boundary. It also tracks brace depth
  (scoped to the statement-separator variant) so a `;` inside a
  function body — `foo() { a; b; }` — does not end the statement.
  Normal `${VAR}` expansions balance themselves and do not affect the
  depth.
- `parser.rs::parse_command_block`: use the new splitter so control
  structure BODIES can span multiple lines too.
- `main.rs::execute_script_content`: strip whole-line comments, then
  feed the entire content through the new splitter.
- `executable.rs::Command::Source`: same fix for sourced files.
- `state.rs::next_fifo_id`: switch to a process-wide atomic counter.
  The per-state counter started at 0 for every `ShellState`, so
  parallel tests with the same PID collided on
  `/tmp/vsh-fifo-<pid>-0`, causing `test_fifo_creation` /
  `test_fifo_path_unique` to race intermittently.
- `tests/multiline_script_tests.rs`: 9 regression tests — 4 unit tests
  for the splitter (newline, quoted newline, multi-line if/fi, function
  defs) and 5 end-to-end tests for sourced scripts (multi-line if,
  multi-line for, multi-line function defs, comment-only lines, and
  mixed single-/multi-line statements).

Test results: 712 passing, 0 failing, 14 ignored (up from 703).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
Tilde expansion was previously hard-coded only inside the `cd`
command. It now runs as the first step of `expand_variables`, so every
command that uses variable expansion — `mkdir`, `touch`, `echo`,
`cp`, `mv`, external commands, etc. — gets tilde expansion for free.

Supported forms (per POSIX §2.6.1):

  ~         → $HOME
  ~/path    → $HOME/path
  ~+        → $PWD
  ~+/path   → $PWD/path
  ~-        → $OLDPWD
  ~-/path   → $OLDPWD/path

Not expanded inside single or double quotes ('~' / "~" stay literal).
This is enforced by `quoted_word_to_string` escaping `~` → `\~` in
quoted contexts; `expand_tilde` recognises the escape and emits a
literal `~`.

~user (getpwnam lookup) is not yet implemented; the tilde is left
literal in that case.

The `cd`-specific `~/` handling is removed since it's now redundant.

Test results: 721 passing, 0 failing, 14 ignored (up from 712).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
Two key POSIX §2.6.5 integration points for $IFS word splitting:

1. **for-loop word list** — `for x in $var; do ... done` now expands
   each word in the `in` clause, then applies `ifs_split` using the
   current `$IFS` (defaulting to space/tab/newline). This means
   `ITEMS="a b c"; for x in $ITEMS; do mkdir $x; done` creates three
   directories instead of one named "a b c".

2. **read builtin** — `read a b c` now splits stdin input by `$IFS`
   and assigns fields to named variables. The last variable receives
   the remainder (POSIX behaviour). The parser now collects multiple
   variable names instead of just one. `read` with no args still
   defaults to `$REPLY`.

The existing `ifs_split()` function in posix_builtins.rs (which had
full POSIX semantics — whitespace/non-whitespace IFS handling, empty
IFS preventing splitting, leading/trailing delimiter handling) was
already tested but not wired into any execution path. Now it is.

Tests: 8 new tests covering default IFS, custom IFS (comma-separated),
empty IFS, newline splitting, literal word lists, and multi-variable
read parsing.

Test results: 729 passing, 0 failing, 14 ignored (up from 721).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
The `trap 'handler' INT` mechanism was registering handlers in the
TrapTable but never firing them. Now:

- Both REPL variants (basic and enhanced/reedline) check for pending
  SIGINT after each command cycle and execute the registered INT trap
  handler if one exists.
- The enhanced REPL also fires INT traps on Ctrl-C (reedline returns
  Signal::CtrlC directly, bypassing the ctrlc crate's async flag).
- A new public helper `run_pending_traps(state)` in executable.rs
  encapsulates the check-and-fire logic. It checks the SIGINT flag,
  clears it, looks up the INT trap handler, parses and executes it.
  Returns true if the handler produced an Exit result.
- EXIT trap was already firing at shell exit (main.rs). No change.

Tests: 7 new tests covering trap registration, trap reset, INT trap
firing via run_pending_traps (with and without handler), no-signal
noop, and EXIT trap manual firing.

Test results: 736 passing, 0 failing, 14 ignored (up from 729).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4
@hyperpolymath hyperpolymath merged commit 660b1a9 into main Apr 20, 2026
24 of 52 checks passed
@hyperpolymath hyperpolymath deleted the claude/advance-project-Y32IT branch April 20, 2026 11:45
hyperpolymath added a commit that referenced this pull request Apr 27, 2026
* feat(rust-cli): fix function control-flow and return propagation

Two long-standing correctness holes in shell-function support:

1. Function bodies containing control structures (`if/fi`, `for/done`,
   `while/done`, `case/esac`) were fragmented. `parse_function_def`
   split the body naively on `;` and `\n`, so `f() { if x; then y; fi; }`
   became `["if x", "then y", "fi"]` — three strings that failed to
   parse individually. The body is now stored as a raw string between
   the outermost braces, with proper brace-depth tracking that respects
   single/double quotes. At call time `execute_function_call` uses the
   existing control-structure-aware `split_on_semicolons` so a function
   body parses the same as any other script fragment.

2. `return` inside nested control structures did nothing. The function
   executor detected returns with `cmd_str.starts_with("return")`,
   which never matched when `return` was buried inside an `if`, `for`,
   or `while`. Introduces `ExecutionResult::Return { exit_code }` — a
   sentinel that propagates through every control-structure handler
   (`Command::If`, `WhileLoop`, `ForLoop`, `LogicalOp`, `Source`,
   `execute_block`) until it reaches `execute_function_call`, which
   converts it to a regular exit-code result. The fragile string-match
   detection is gone.

Also:

- `tests/function_control_flow_tests.rs`: 8 regression tests covering
  if/for/case in function bodies, `return` from nested `if`/`for`, and
  a brace-in-quoted-string edge case.
- `tests/security_tests.rs`: `security_no_privilege_escalation` now
  skips cleanly when running as root (with an optional
  `VSH_ALLOW_ROOT_TESTS=1` override) instead of panicking. The test was
  a meta-check about the test environment, not correctness, and was
  making the suite non-portable to containerised CI.

Test results: 703 passing, 0 failing, 14 ignored (up from 602).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4

* feat(rust-cli): support multi-line control structures in scripts and source

Previously `execute_script_content` and `Command::Source` iterated
`content.lines()` and parsed each line in isolation, so the canonical
POSIX shape

    if true
    then
      mkdir d
    fi

shredded into three lines that failed to parse individually. The same
went for `for/do/done`, `while/do/done`, `case/esac`, and multi-line
function bodies.

Changes:

- `parser.rs`: extract `split_on_top_level` from `split_on_semicolons`
  and add `split_on_statement_separators`, which additionally treats
  top-level `\n` as a statement boundary. It also tracks brace depth
  (scoped to the statement-separator variant) so a `;` inside a
  function body — `foo() { a; b; }` — does not end the statement.
  Normal `${VAR}` expansions balance themselves and do not affect the
  depth.
- `parser.rs::parse_command_block`: use the new splitter so control
  structure BODIES can span multiple lines too.
- `main.rs::execute_script_content`: strip whole-line comments, then
  feed the entire content through the new splitter.
- `executable.rs::Command::Source`: same fix for sourced files.
- `state.rs::next_fifo_id`: switch to a process-wide atomic counter.
  The per-state counter started at 0 for every `ShellState`, so
  parallel tests with the same PID collided on
  `/tmp/vsh-fifo-<pid>-0`, causing `test_fifo_creation` /
  `test_fifo_path_unique` to race intermittently.
- `tests/multiline_script_tests.rs`: 9 regression tests — 4 unit tests
  for the splitter (newline, quoted newline, multi-line if/fi, function
  defs) and 5 end-to-end tests for sourced scripts (multi-line if,
  multi-line for, multi-line function defs, comment-only lines, and
  mixed single-/multi-line statements).

Test results: 712 passing, 0 failing, 14 ignored (up from 703).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4

* feat(rust-cli): implement POSIX §2.6.1 tilde expansion globally

Tilde expansion was previously hard-coded only inside the `cd`
command. It now runs as the first step of `expand_variables`, so every
command that uses variable expansion — `mkdir`, `touch`, `echo`,
`cp`, `mv`, external commands, etc. — gets tilde expansion for free.

Supported forms (per POSIX §2.6.1):

  ~         → $HOME
  ~/path    → $HOME/path
  ~+        → $PWD
  ~+/path   → $PWD/path
  ~-        → $OLDPWD
  ~-/path   → $OLDPWD/path

Not expanded inside single or double quotes ('~' / "~" stay literal).
This is enforced by `quoted_word_to_string` escaping `~` → `\~` in
quoted contexts; `expand_tilde` recognises the escape and emits a
literal `~`.

~user (getpwnam lookup) is not yet implemented; the tilde is left
literal in that case.

The `cd`-specific `~/` handling is removed since it's now redundant.

Test results: 721 passing, 0 failing, 14 ignored (up from 712).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4

* feat(rust-cli): wire IFS word splitting into for-loops and read builtin

Two key POSIX §2.6.5 integration points for $IFS word splitting:

1. **for-loop word list** — `for x in $var; do ... done` now expands
   each word in the `in` clause, then applies `ifs_split` using the
   current `$IFS` (defaulting to space/tab/newline). This means
   `ITEMS="a b c"; for x in $ITEMS; do mkdir $x; done` creates three
   directories instead of one named "a b c".

2. **read builtin** — `read a b c` now splits stdin input by `$IFS`
   and assigns fields to named variables. The last variable receives
   the remainder (POSIX behaviour). The parser now collects multiple
   variable names instead of just one. `read` with no args still
   defaults to `$REPLY`.

The existing `ifs_split()` function in posix_builtins.rs (which had
full POSIX semantics — whitespace/non-whitespace IFS handling, empty
IFS preventing splitting, leading/trailing delimiter handling) was
already tested but not wired into any execution path. Now it is.

Tests: 8 new tests covering default IFS, custom IFS (comma-separated),
empty IFS, newline splitting, literal word lists, and multi-variable
read parsing.

Test results: 729 passing, 0 failing, 14 ignored (up from 721).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4

* feat(rust-cli): wire SIGINT trap handler into REPL loops

The `trap 'handler' INT` mechanism was registering handlers in the
TrapTable but never firing them. Now:

- Both REPL variants (basic and enhanced/reedline) check for pending
  SIGINT after each command cycle and execute the registered INT trap
  handler if one exists.
- The enhanced REPL also fires INT traps on Ctrl-C (reedline returns
  Signal::CtrlC directly, bypassing the ctrlc crate's async flag).
- A new public helper `run_pending_traps(state)` in executable.rs
  encapsulates the check-and-fire logic. It checks the SIGINT flag,
  clears it, looks up the INT trap handler, parses and executes it.
  Returns true if the handler produced an Exit result.
- EXIT trap was already firing at shell exit (main.rs). No change.

Tests: 7 new tests covering trap registration, trap reset, INT trap
firing via run_pending_traps (with and without handler), no-signal
noop, and EXIT trap manual firing.

Test results: 736 passing, 0 failing, 14 ignored (up from 729).

https://claude.ai/code/session_01EMHrh5Jq32pb98KXoSKLA4

---------

Co-authored-by: Claude <noreply@anthropic.com>
Repository owner deleted a comment from chatgpt-codex-connector Bot May 13, 2026
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.

2 participants