Skip to content

Commit 0f147cc

Browse files
hyperpolymathclaude
andcommitted
chore: sync local changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b1aca3c commit 0f147cc

7 files changed

Lines changed: 2135 additions & 0 deletions

File tree

docs/POSIX-2024-NOTES.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# POSIX 2024 (Issue 8) Compatibility Notes — Valence Shell
3+
4+
**Author**: Jonathan D.A. Jewell
5+
**Last Updated**: 2026-03-10
6+
**Reference**: IEEE Std 1003.1-2024 (POSIX.1-2024, Issue 8)
7+
8+
---
9+
10+
## Overview
11+
12+
POSIX.1-2024 (Issue 8) was published in June 2024, replacing POSIX.1-2017 (Issue 7).
13+
This document tracks which POSIX 2024 changes are relevant to valence-shell and our
14+
current compatibility status.
15+
16+
## Relevant POSIX 2024 Changes
17+
18+
### Shell Grammar Changes
19+
20+
| Change | Relevant? | vsh Status |
21+
|--------|-----------|------------|
22+
| `pipefail` option added to shell | Yes | Not implemented |
23+
| `local` builtin standardized | Yes | Not implemented (listed as missing builtin) |
24+
| `readarray`/`mapfile` not added | N/A | N/A |
25+
| Here-string `<<<` **not** standardized | Info | Implemented as extension |
26+
| `[[` compound command **not** standardized | Info | Implemented as extension |
27+
| `$'...'` (ANSI-C quoting) **not** standardized | Info | Not implemented |
28+
29+
### New/Changed Utilities
30+
31+
| Utility | POSIX 2024 Status | vsh Status |
32+
|---------|-------------------|------------|
33+
| `timeout` | Newly standardized | Not implemented (external only) |
34+
| `realpath` | Newly standardized | Not implemented |
35+
| `yes` | Newly standardized | Not implemented (external only) |
36+
| `getconf` | Updated | Not relevant (system utility) |
37+
| `find` `-delete` | Newly standardized flag | Not relevant (external command) |
38+
| `sed` `-E` (ERE) | Newly standardized flag | Not relevant (external command) |
39+
| `xargs` `-P` | Newly standardized flag | Not relevant (external command) |
40+
41+
### Variable/Expansion Changes
42+
43+
| Change | Relevant? | vsh Status |
44+
|--------|-----------|------------|
45+
| `${param@operator}` transformations | Yes | Not implemented |
46+
| `EXECIGNORE` variable | Low | Not implemented |
47+
| Unset vs empty distinction clarified | Yes | Partially handled (`:-` vs `-`) |
48+
49+
### Signal Handling
50+
51+
| Change | Relevant? | vsh Status |
52+
|--------|-----------|------------|
53+
| `trap` behavior clarifications | Yes | `trap` not implemented at all |
54+
| `SIGCHLD` handling specified | Yes | Not implemented |
55+
56+
## Compatibility Matrix
57+
58+
| POSIX 2024 Area | Supported | Partial | Not Yet |
59+
|-----------------|-----------|---------|---------|
60+
| Simple commands | Yes | | |
61+
| Pipelines | Yes | | |
62+
| Redirections (standard) | Yes | | |
63+
| Variables & expansion | | Yes | |
64+
| Glob expansion | Yes | | |
65+
| Arithmetic expansion | Yes | | |
66+
| Command substitution | Yes | | |
67+
| Control flow (if/while/for/case) | Yes | | |
68+
| Functions | | | Not yet |
69+
| `trap` / signal handling | | | Not yet |
70+
| `local` builtin | | | Not yet |
71+
| `pipefail` | | | Not yet |
72+
| Job control | | Yes | |
73+
| Shell scripts (.sh) | | | Not yet |
74+
| `${param@operator}` | | | Not yet |
75+
76+
## Priority for vsh
77+
78+
POSIX 2024 changes that should be tracked as vsh approaches v1.0:
79+
80+
1. **`local` builtin** — now standardized; implement alongside shell functions (M12)
81+
2. **`pipefail`** — valuable for robust scripting; implement after pipelines are solid
82+
3. **`${param@operator}`** — useful but lower priority than functions and scripts
83+
4. **`trap`** — essential for any script-capable shell; implement in M11 builtin expansion
84+
5. **Signal handling clarifications** — align with POSIX 2024 once `trap`/SIGCHLD land
85+
86+
## Notes
87+
88+
- vsh already implements several features that remain non-POSIX even in Issue 8
89+
(here-strings `<<<`, `[[ ]]`, process substitution, brace expansion).
90+
These are kept as extensions.
91+
- The formal verification strategy is orthogonal to POSIX compliance level;
92+
reversibility proofs apply regardless of which POSIX edition is targeted.
93+
- Full POSIX 2024 compliance requires completing milestones M10-M14 first
94+
(see `docs/POSIX_COMPLIANCE.md`).
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
//! Shell Benchmarks (TASK 6)
3+
//!
4+
//! Measures:
5+
//! 1. Shell startup time (cold start to state-ready)
6+
//! 2. Command parsing throughput (commands/sec)
7+
//! 3. Pipeline execution overhead
8+
//! 4. State checkpoint/restore cycle time
9+
//!
10+
//! Run with: `cargo bench --bench shell_benchmarks`
11+
//!
12+
//! Author: Jonathan D.A. Jewell
13+
14+
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
15+
use tempfile::TempDir;
16+
use vsh::commands::{mkdir, rmdir};
17+
use vsh::parser::parse_command;
18+
use vsh::state::ShellState;
19+
20+
// ============================================================
21+
// 1. Shell Startup Time
22+
// ============================================================
23+
24+
/// Benchmark: Cold start from nothing to state-ready
25+
fn bench_shell_startup(c: &mut Criterion) {
26+
c.bench_function("shell_startup_cold", |b| {
27+
b.iter(|| {
28+
let temp = TempDir::new().unwrap();
29+
let state = ShellState::new(temp.path().to_str().unwrap()).unwrap();
30+
black_box(&state);
31+
});
32+
});
33+
}
34+
35+
/// Benchmark: Startup with pre-existing state file
36+
fn bench_shell_startup_with_state(c: &mut Criterion) {
37+
c.bench_function("shell_startup_with_state", |b| {
38+
// Create state once, then benchmark re-opening
39+
let temp = TempDir::new().unwrap();
40+
let root = temp.path().to_str().unwrap();
41+
{
42+
let mut state = ShellState::new(root).unwrap();
43+
for i in 0..50 {
44+
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
45+
}
46+
}
47+
48+
b.iter(|| {
49+
let state = ShellState::new(root).unwrap();
50+
black_box(&state);
51+
});
52+
});
53+
}
54+
55+
// ============================================================
56+
// 2. Command Parsing Throughput
57+
// ============================================================
58+
59+
/// Benchmark: Simple command parsing
60+
fn bench_parse_simple(c: &mut Criterion) {
61+
let mut group = c.benchmark_group("parse_throughput");
62+
group.throughput(Throughput::Elements(1));
63+
64+
group.bench_function("simple_mkdir", |b| {
65+
b.iter(|| {
66+
let cmd = parse_command("mkdir test_dir").unwrap();
67+
black_box(cmd);
68+
});
69+
});
70+
71+
group.bench_function("simple_echo", |b| {
72+
b.iter(|| {
73+
let cmd = parse_command("echo hello world").unwrap();
74+
black_box(cmd);
75+
});
76+
});
77+
78+
group.bench_function("with_redirections", |b| {
79+
b.iter(|| {
80+
let cmd = parse_command("echo hello > output.txt 2>&1").unwrap();
81+
black_box(cmd);
82+
});
83+
});
84+
85+
group.bench_function("variable_assignment", |b| {
86+
b.iter(|| {
87+
let cmd = parse_command("MY_VAR=hello_world").unwrap();
88+
black_box(cmd);
89+
});
90+
});
91+
92+
group.bench_function("pipeline", |b| {
93+
b.iter(|| {
94+
let cmd = parse_command("ls | grep test | wc -l").unwrap();
95+
black_box(cmd);
96+
});
97+
});
98+
99+
group.bench_function("logical_operators", |b| {
100+
b.iter(|| {
101+
let cmd = parse_command("test -f file && echo yes || echo no").unwrap();
102+
black_box(cmd);
103+
});
104+
});
105+
106+
group.bench_function("function_definition", |b| {
107+
b.iter(|| {
108+
let cmd = parse_command("setup() { mkdir src; touch src/main.rs; }").unwrap();
109+
black_box(cmd);
110+
});
111+
});
112+
113+
group.bench_function("control_structure_if", |b| {
114+
b.iter(|| {
115+
let cmd = parse_command("if [ -f test ]; then echo yes; else echo no; fi").unwrap();
116+
black_box(cmd);
117+
});
118+
});
119+
120+
group.finish();
121+
}
122+
123+
/// Benchmark: Batch command parsing (many commands in sequence)
124+
fn bench_parse_batch(c: &mut Criterion) {
125+
let commands = vec![
126+
"mkdir project",
127+
"touch project/README.md",
128+
"echo hello > project/greeting.txt",
129+
"VAR=value",
130+
"export PATH",
131+
"ls | grep txt",
132+
"if [ -d project ]; then echo yes; fi",
133+
"cd project",
134+
"pwd",
135+
"cd ..",
136+
];
137+
138+
let mut group = c.benchmark_group("parse_batch");
139+
group.throughput(Throughput::Elements(commands.len() as u64));
140+
141+
group.bench_function("10_commands", |b| {
142+
b.iter(|| {
143+
for cmd_str in &commands {
144+
let cmd = parse_command(cmd_str).unwrap();
145+
black_box(&cmd);
146+
}
147+
});
148+
});
149+
150+
group.finish();
151+
}
152+
153+
// ============================================================
154+
// 3. Pipeline Execution Overhead
155+
// ============================================================
156+
157+
/// Benchmark: Parsing pipeline commands of increasing length
158+
fn bench_pipeline_parsing(c: &mut Criterion) {
159+
let mut group = c.benchmark_group("pipeline_parsing");
160+
161+
for stages in [2, 3, 5, 10] {
162+
let pipeline: String = (0..stages)
163+
.map(|i| format!("cmd{}", i))
164+
.collect::<Vec<_>>()
165+
.join(" | ");
166+
167+
group.bench_function(format!("{}_stages", stages), |b| {
168+
b.iter(|| {
169+
let cmd = parse_command(&pipeline).unwrap();
170+
black_box(cmd);
171+
});
172+
});
173+
}
174+
175+
group.finish();
176+
}
177+
178+
// ============================================================
179+
// 4. State Checkpoint/Restore Cycle Time
180+
// ============================================================
181+
182+
/// Benchmark: Creating a checkpoint
183+
fn bench_checkpoint_create(c: &mut Criterion) {
184+
c.bench_function("checkpoint_create", |b| {
185+
let temp = TempDir::new().unwrap();
186+
let mut state = ShellState::new(temp.path().to_str().unwrap()).unwrap();
187+
188+
// Pre-populate with some operations
189+
for i in 0..20 {
190+
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
191+
}
192+
193+
let mut checkpoint_idx = 0;
194+
b.iter(|| {
195+
let name = format!("cp_{}", checkpoint_idx);
196+
state
197+
.checkpoints
198+
.insert(name, (state.history.len(), chrono::Utc::now()));
199+
checkpoint_idx += 1;
200+
black_box(&state.checkpoints);
201+
});
202+
});
203+
}
204+
205+
/// Benchmark: Full checkpoint-modify-restore cycle
206+
fn bench_checkpoint_restore_cycle(c: &mut Criterion) {
207+
c.bench_function("checkpoint_restore_cycle", |b| {
208+
b.iter(|| {
209+
let temp = TempDir::new().unwrap();
210+
let mut state = ShellState::new(temp.path().to_str().unwrap()).unwrap();
211+
212+
// Create initial state
213+
for i in 0..5 {
214+
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
215+
}
216+
217+
// Checkpoint
218+
let history_len = state.history.len();
219+
state
220+
.checkpoints
221+
.insert("before".to_string(), (history_len, chrono::Utc::now()));
222+
223+
// Modify
224+
for i in 5..10 {
225+
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
226+
}
227+
228+
// Restore (undo back to checkpoint)
229+
let ops_to_undo = state.history.len() - history_len;
230+
for _ in 0..ops_to_undo {
231+
if let Some(last) = state.history.last() {
232+
if !last.undone {
233+
let path = last.path.clone();
234+
let _ = rmdir(&mut state, &path, false);
235+
}
236+
}
237+
}
238+
239+
black_box(&state);
240+
});
241+
});
242+
}
243+
244+
// ============================================================
245+
// Benchmark groups
246+
// ============================================================
247+
248+
criterion_group!(
249+
benches,
250+
bench_shell_startup,
251+
bench_shell_startup_with_state,
252+
bench_parse_simple,
253+
bench_parse_batch,
254+
bench_pipeline_parsing,
255+
bench_checkpoint_create,
256+
bench_checkpoint_restore_cycle,
257+
);
258+
criterion_main!(benches);

0 commit comments

Comments
 (0)