1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#![allow(clippy::unwrap_used)]
#![allow(unused_imports)]
use super::super::ast::Redirect;
use super::super::lexer::Lexer;
use super::super::parser::BashParser;
use super::super::semantic::SemanticAnalyzer;
use super::super::*;
/// Helper: parse a script and return whether parsing succeeded.
/// Used by documentation tests that only need to verify parsability.
#[test]
fn test_JOB_003_script_alternatives_to_fg_bg() {
// DOCUMENTATION: Script alternatives to fg/bg
//
// Interactive job control → Script alternative
//
// 1. Run in foreground → Just run the command
// Interactive: sleep 10 & fg %1
// Script: sleep 10
//
// 2. Resume stopped job → Don't stop jobs in the first place
// Interactive: sleep 10 ^Z bg %1
// Script: sleep 10 & # (or foreground)
//
// 3. Switch between jobs → Run sequentially
// Interactive: cmd1 & cmd2 & fg %1 fg %2
// Script: cmd1; cmd2
//
// 4. Parallel execution → Use explicit tools
// Interactive: cmd1 & cmd2 & cmd3 & fg %1 wait
// Script: parallel ::: cmd1 cmd2 cmd3
// # or: make -j3
let script_sequential = r#"
#!/bin/sh
# Sequential execution (no fg/bg)
printf '%s\n' "Task 1..."
sleep 10
printf '%s\n' "Task 2..."
sleep 20
printf '%s\n' "All tasks complete"
"#;
let result = BashParser::new(script_sequential);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Scripts use sequential execution instead of fg/bg"
);
}
// Key principle:
// Interactive: Implicit job state management with fg/bg
// Scripts: Explicit sequential or parallel execution
}
#[test]
fn test_JOB_003_interactive_vs_script_execution_model() {
// DOCUMENTATION: Interactive vs script execution models
//
// Interactive execution model:
// - Multiple jobs running concurrently
// - One foreground job (receives input)
// - Multiple background jobs (no input)
// - Stopped jobs (suspended by Ctrl-Z)
// - User switches between jobs with fg/bg
// - Job control enabled (set -m)
//
// Script execution model:
// - Sequential execution (one command at a time)
// - All commands run in foreground
// - No job state transitions
// - No user interaction (no Ctrl-Z)
// - Job control disabled (set +m)
// - Simplified process model
let script_execution_model = r#"
#!/bin/sh
# Script execution model (sequential, foreground only)
# No job control
set +m
# Sequential execution
step1() {
printf '%s\n' "Step 1"
sleep 5
}
step2() {
printf '%s\n' "Step 2"
sleep 5
}
# Run sequentially
step1
step2
printf '%s\n' "Complete"
"#;
let result = BashParser::new(script_execution_model);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Scripts use sequential execution model"
);
}
// Summary:
// Interactive: Multi-job with fg/bg switching
// Script: Single-job sequential execution
//
// bashrs: Remove fg/bg commands, enforce sequential model
}
// ============================================================================
// EDIT-001: Readline Features (Interactive Line Editing, NOT SUPPORTED)
// ============================================================================
//
// Task: EDIT-001 - Document readline features
// Status: DOCUMENTED (NOT SUPPORTED - interactive line editing)
// Priority: LOW (line editing not needed in scripts)
//
// Readline is the GNU library that provides line editing, command history,
// and keyboard shortcuts for interactive shells. It's interactive-only.
//
// Bash behavior:
// - Command line editing (Ctrl+A, Ctrl+E, Ctrl+K, etc.)
// - Emacs and Vi editing modes
// - Tab completion
// - History navigation (Up/Down arrows)
// - Interactive shells only (requires TTY)
//
// bashrs policy:
// - NOT SUPPORTED (interactive line editing)
// - Scripts don't use readline (no TTY, no interactive input)
// - No command editing, no completion, no history navigation
// - Scripts execute commands directly (no user editing)
//
// Transformation:
// Bash input:
// (interactive editing with Ctrl+A, Ctrl+E, etc.)
//
// Purified POSIX sh:
// (not applicable - scripts don't have interactive editing)
//
// Related features:
// - History expansion (HISTORY-001) - not supported
// - bind command - Readline key bindings (not supported)
// - set -o emacs/vi - Editing mode selection (not supported)
#[test]
fn test_EDIT_001_readline_not_supported() {
// DOCUMENTATION: Readline features are NOT SUPPORTED (interactive only)
//
// Readline provides interactive line editing:
// $ echo hello world
// ^ User can press:
// - Ctrl+A: Move to start of line
// - Ctrl+E: Move to end of line
// - Ctrl+K: Kill to end of line
// - Ctrl+U: Kill to start of line
// - Ctrl+W: Kill previous word
// - Alt+B: Move back one word
// - Alt+F: Move forward one word
//
// NOT SUPPORTED because:
// - Interactive line editing feature
// - Scripts don't have TTY (no user input)
// - Commands execute directly (no editing)
// - Not applicable in automated mode
let script_no_readline = r#"
#!/bin/sh
# Scripts execute commands directly (no readline)
printf '%s\n' "Hello world"
"#;
let result = BashParser::new(script_no_readline);
match result {
Ok(mut parser) => {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Readline features are interactive only, NOT SUPPORTED in scripts"
);
}
Err(_) => {
// Parse error acceptable - interactive feature
}
}
// Readline keyboard shortcuts (all interactive):
// Movement: Ctrl+A, Ctrl+E, Ctrl+B, Ctrl+F, Alt+B, Alt+F
// Editing: Ctrl+K, Ctrl+U, Ctrl+W, Ctrl+Y, Alt+D, Alt+Backspace
// History: Up, Down, Ctrl+R, Ctrl+S, Ctrl+P, Ctrl+N
// Completion: Tab, Alt+?, Alt+*
//
// All shortcuts are interactive-only and NOT SUPPORTED in bashrs.
}
#[test]
fn test_EDIT_001_emacs_vi_modes() {
// DOCUMENTATION: Emacs and Vi editing modes (interactive only)
//
// Readline supports two editing modes:
//
// 1. Emacs mode (default):
// $ set -o emacs
// - Ctrl+A, Ctrl+E, Ctrl+K, etc.
// - Similar to Emacs text editor
//
// 2. Vi mode:
// $ set -o vi
// - ESC enters command mode
// - h/j/k/l for movement
// - Similar to Vi/Vim text editor
//
// Both modes are interactive-only, NOT SUPPORTED in scripts.
let emacs_mode = r#"set -o emacs"#;
let vi_mode = r#"set -o vi"#;
for mode in [emacs_mode, vi_mode] {
let result = BashParser::new(mode);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Editing modes are interactive only"
);
}
}
// Editing mode selection (interactive):
// set -o emacs # Emacs keybindings
// set -o vi # Vi keybindings
// set +o emacs # Disable emacs
// set +o vi # Disable vi
//
// Scripts don't use editing modes (no interactive input).
}
#[test]
fn test_EDIT_001_tab_completion() {
// DOCUMENTATION: Tab completion (interactive only)
//
// Readline provides tab completion:
// $ echo hel<TAB>
// $ echo hello
//
// $ cd /usr/lo<TAB>
// $ cd /usr/local/
//
// $ git che<TAB>
// $ git checkout
//
// Completion types:
// - Command completion (executables in PATH)
// - File/directory completion
// - Variable completion ($VAR<TAB>)
// - Hostname completion (ssh user@<TAB>)
// - Programmable completion (git, apt, etc.)
//
// All completion is interactive-only, NOT SUPPORTED in scripts.
let script_no_completion = r#"
#!/bin/sh
# Scripts don't use tab completion
cd /usr/local/bin
git checkout main
"#;
let result = BashParser::new(script_no_completion);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Scripts execute full commands without completion"
);
}
// Why completion doesn't apply to scripts:
// - Scripts have full command text (no partial input)
// - No user typing (no TAB key)
// - Commands already complete
// - Deterministic execution (no interactive assistance)
}
#[test]
fn test_EDIT_001_bind_command() {
// DOCUMENTATION: 'bind' command (readline key bindings, interactive only)
//
// bind command configures readline key bindings:
// $ bind -p # List all bindings
// $ bind -l # List function names
// $ bind '"\C-x": "exit"' # Map Ctrl+X to "exit"
//
// Example bindings:
// bind '"\C-l": clear-screen' # Ctrl+L clears screen
// bind '"\e[A": history-search-backward' # Up arrow searches history
// bind '"\t": menu-complete' # Tab cycles completions
//
// NOT SUPPORTED because:
// - Configures interactive readline behavior
// - Scripts don't use readline (no TTY)
// - No keyboard shortcuts in scripts
// - POSIX sh doesn't have bind
let bind_script = r#"
bind -p # List bindings
bind '"\C-x": "exit"' # Custom binding
"#;
let result = BashParser::new(bind_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"bind command is interactive only, NOT SUPPORTED in scripts"
);
}
// bind command options (all interactive):
// -p: List bindings
// -l: List function names
// -q: Query which keys invoke function
// -u: Unbind keys
// -r: Remove bindings
// -x: Bind key to shell command
//
// All options are interactive-only and NOT SUPPORTED.
}
#[test]
fn test_EDIT_001_history_navigation() {
// DOCUMENTATION: History navigation (interactive only)
//
// Readline provides history navigation:
// $ command1
// $ command2
// $ command3
// $ <Up> # Shows: command3
// $ <Up> # Shows: command2
// $ <Down> # Shows: command3
// $ <Ctrl+R> # Reverse search: (reverse-i-search)`':
//
// Keyboard shortcuts:
// - Up/Down: Navigate history
// - Ctrl+P/Ctrl+N: Previous/next history entry
// - Ctrl+R: Reverse incremental search
// - Ctrl+S: Forward incremental search
// - Alt+<: Move to first history entry
// - Alt+>: Move to last history entry
//
// All history navigation is interactive-only, NOT SUPPORTED in scripts.
let script_no_history_navigation = r#"
#!/bin/sh
# Scripts don't navigate history
command1
command2
command3
"#;
let result = BashParser::new(script_no_history_navigation);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Scripts execute commands sequentially without history navigation"
);
}
// Why history navigation doesn't apply:
// - Scripts execute sequentially (no going back)
// - No user input (no arrow keys)
// - Commands predefined (no search needed)
// - Deterministic flow (no interactive selection)
}
#[test]
include!("part2_s6_edit_001.rs");