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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
#![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_BASH_BUILTIN_006_transformation_strategy() {
let transformation_example = r#"
while IFS= read -r line; do
printf '%s\n' "$line"
done < file.txt
"#;
let _ = parse_script_ok(transformation_example);
}
#[test]
fn test_BASH_BUILTIN_006_mapfile_alias_not_supported() {
// DOCUMENTATION: mapfile is an alias for readarray
//
// mapfile and readarray are the SAME command:
// mapfile -t array < file.txt
// readarray -t array < file.txt
//
// Both are Bash 4.0+ extensions, NOT POSIX
//
// POSIX alternative: Same as readarray
// while IFS= read -r line; do
// process "$line"
// done < file.txt
let mapfile_script = r#"mapfile -t array < input.txt"#;
let result = BashParser::new(mapfile_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"mapfile is Bash-specific alias, NOT SUPPORTED"
);
}
// mapfile = readarray (exact same functionality)
// Both require Bash 4.0+
// Both use arrays (not available in POSIX sh)
}
// DOCUMENTATION: Memory efficiency of while read vs readarray
//
// readarray (Bash): Loads ENTIRE file into memory, O(file size), fails on GB+ files
// while read (POSIX): Processes ONE line at a time, O(1) memory, handles ANY size
//
// Memory comparison: readarray O(n) vs while read O(1)
// Performance: readarray fast for <1MB, while read consistent for any size
// Recommendation: ALWAYS use while read for file processing
#[test]
fn test_BASH_BUILTIN_006_memory_efficiency_comparison() {
let efficient_posix = r#"
# Process large file efficiently (POSIX)
while IFS= read -r line; do
# Process one line at a time
printf '%s\n' "$line"
done < /var/log/huge.log
"#;
let _ = parse_script_ok(efficient_posix);
}
// ============================================================================
// BASH-VAR-001: BASH_VERSION (Bash-specific, NOT SUPPORTED)
// ============================================================================
//
// Task: BASH-VAR-001 - Document BASH_VERSION
// Status: DOCUMENTED (NOT SUPPORTED - Bash-specific variable)
// Priority: LOW (version detection not needed in scripts)
//
// BASH_VERSION contains the Bash version string:
// - BASH_VERSION="5.1.16(1)-release"
// - Used for version detection: if [[ $BASH_VERSION > "4.0" ]]; then ...
//
// Why NOT SUPPORTED:
// - Bash-specific (not available in dash, ash, busybox sh)
// - No equivalent in POSIX sh
// - Script portability: Should work regardless of shell version
// - Version checks violate POSIX-only policy
//
// POSIX Alternative: Remove version checks
// Instead of:
// if [[ $BASH_VERSION > "4.0" ]]; then
// use_bash_4_feature
// fi
//
// Use:
// # Write code that works on ALL POSIX shells
// # Don't depend on specific Bash versions
//
// Purification strategy:
// - Remove BASH_VERSION checks
// - Remove version-dependent code paths
// - Use only POSIX features (works everywhere)
//
// Related Bash version variables (all NOT SUPPORTED):
// - BASH_VERSION (full version string)
// - BASH_VERSINFO (array with version components)
// - BASH_VERSINFO[0] (major version)
// - BASH_VERSINFO[1] (minor version)
#[test]
fn test_BASH_VAR_001_bash_version_not_supported() {
// DOCUMENTATION: BASH_VERSION is NOT SUPPORTED (Bash-specific)
//
// Bash version detection:
// echo "Bash version: $BASH_VERSION"
// if [[ $BASH_VERSION > "4.0" ]]; then
// echo "Bash 4.0 or later"
// fi
//
// This is Bash-specific, not available in POSIX sh
let bash_version_script = r#"echo "Version: $BASH_VERSION""#;
let result = BashParser::new(bash_version_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"BASH_VERSION is Bash-specific, NOT SUPPORTED"
);
}
// NOT SUPPORTED because:
// - Bash-specific (not in dash, ash, busybox sh)
// - No POSIX equivalent
// - Violates portability (should work on any shell)
}
#[test]
fn test_BASH_VAR_001_remove_version_checks() {
// DOCUMENTATION: Version checks should be removed
//
// Bad (Bash-specific version check):
// if [[ $BASH_VERSION > "4.0" ]]; then
// # Use Bash 4+ feature
// readarray -t lines < file.txt
// else
// # Fallback for older Bash
// while read line; do lines+=("$line"); done < file.txt
// fi
//
// Good (POSIX, no version check):
// while IFS= read -r line; do
// # Process line (works everywhere)
// printf '%s\n' "$line"
// done < file.txt
//
// Philosophy:
// - Don't check shell versions
// - Use POSIX features only (works everywhere)
// - Simpler code, better portability
let posix_no_version_check = r#"
while IFS= read -r line; do
printf '%s\n' "$line"
done < file.txt
"#;
let result = BashParser::new(posix_no_version_check);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"POSIX code needs no version checks"
);
}
// Purification removes:
// - BASH_VERSION checks
// - Version-dependent code paths
// - Bash-specific features (use POSIX instead)
}
#[test]
fn test_BASH_VAR_001_bash_versinfo_not_supported() {
// DOCUMENTATION: BASH_VERSINFO array is NOT SUPPORTED
//
// BASH_VERSINFO is an array with version components:
// BASH_VERSINFO[0] = major version (5)
// BASH_VERSINFO[1] = minor version (1)
// BASH_VERSINFO[2] = patch version (16)
// BASH_VERSINFO[3] = build version (1)
// BASH_VERSINFO[4] = release status (release)
// BASH_VERSINFO[5] = architecture (x86_64-pc-linux-gnu)
//
// Example usage (Bash-specific):
// if [ ${BASH_VERSINFO[0]} -ge 4 ]; then
// echo "Bash 4 or later"
// fi
//
// This is Bash-specific, uses arrays (not POSIX)
let bash_versinfo_script = r#"echo "Major version: ${BASH_VERSINFO[0]}""#;
let result = BashParser::new(bash_versinfo_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"BASH_VERSINFO is Bash-specific array, NOT SUPPORTED"
);
}
// NOT SUPPORTED because:
// - Bash-specific variable
// - Uses arrays (not available in POSIX sh)
// - Version detection violates portability
}
#[test]
fn test_BASH_VAR_001_portability_over_version_detection() {
// DOCUMENTATION: Portability philosophy - no version detection
//
// Bash approach (BAD - version-dependent):
// if [[ $BASH_VERSION > "4.0" ]]; then
// # Bash 4+ features
// declare -A assoc_array
// readarray -t lines < file.txt
// else
// # Bash 3.x fallback
// # Complex workarounds
// fi
//
// POSIX approach (GOOD - works everywhere):
// # Use only POSIX features
// # No version checks needed
// # Works on dash, ash, busybox sh, bash, zsh, ksh
//
// while IFS= read -r line; do
// process "$line"
// done < file.txt
//
// Benefits:
// - Simpler code (no version checks)
// - Better portability (works on any POSIX shell)
// - Fewer bugs (no version-specific code paths)
// - Easier testing (same code everywhere)
let portable_posix = r#"
# No version detection needed
# Works on ALL POSIX shells
while IFS= read -r line; do
printf '%s\n' "$line"
done < file.txt
"#;
let result = BashParser::new(portable_posix);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"Portable POSIX code needs no version detection"
);
}
// bashrs philosophy:
// - POSIX-only (no Bash-specific features)
// - No version detection (same code everywhere)
// - Maximum portability (works on minimal shells)
}
#[test]
fn test_BASH_VAR_001_purification_removes_bash_version() {
// DOCUMENTATION: Purification strategy for BASH_VERSION
//
// Step 1: Detect BASH_VERSION usage
// - $BASH_VERSION references
// - ${BASH_VERSINFO[*]} array references
// - Version comparison logic
//
// Step 2: Remove version-dependent code
// - Remove if [[ $BASH_VERSION > "4.0" ]]
// - Remove version checks
// - Remove conditional Bash feature usage
//
// Step 3: Use POSIX alternatives
// - Replace Bash 4+ features with POSIX equivalents
// - readarray → while read
// - declare -A → multiple variables or other structure
// - [[ ]] → [ ]
//
// Example transformation:
// Before (Bash-specific):
// if [[ $BASH_VERSION > "4.0" ]]; then
// readarray -t lines < file.txt
// fi
//
// After (POSIX):
// while IFS= read -r line; do
// # Process line
// done < file.txt
let purified_posix = r#"
# Purified: No BASH_VERSION checks
# Uses POSIX features only
while IFS= read -r line; do
printf '%s\n' "$line"
done < file.txt
"#;
let result = BashParser::new(purified_posix);
if let Ok(mut parser) = result {
let _parse_result = parser.parse();
// Purified code has no BASH_VERSION references
}
// Purification guarantee:
// - No BASH_VERSION in purified output
// - No BASH_VERSINFO in purified output
// - No version-dependent code paths
// - Uses POSIX features only
}
// ============================================================================
// VAR-004: PS1, PS2, PS3, PS4 (Interactive Prompts, NOT SUPPORTED)
// ============================================================================
//
// Task: VAR-004 - Document PS1, PS2, PS3, PS4
// Status: DOCUMENTED (NOT SUPPORTED - interactive only)
// Priority: LOW (prompt variables not needed in scripts)
//
// Prompt variables control interactive shell prompts:
// - PS1: Primary prompt (default: "$ " or "# " for root)
// - PS2: Secondary prompt for multi-line commands (default: "> ")
// - PS3: Prompt for select command (default: "#? ")
// - PS4: Debug prompt for set -x trace (default: "+ ")
//
// Why NOT SUPPORTED:
// - Interactive only (not used in scripts)
// - bashrs is script-mode-only (no interactive features)
// - POSIX sh scripts don't use prompts
// - Prompts displayed to users, not part of script logic
//
// Purification strategy:
// - Remove PS1, PS2, PS3, PS4 assignments
// - Remove prompt customization code
// - Scripts run non-interactively (no prompts displayed)
//
// Related interactive features (all NOT SUPPORTED):
// - PROMPT_COMMAND (executed before each prompt)
// - PROMPT_DIRTRIM (directory name trimming in PS1)
// - PS0 (displayed after command read, before execution)
//
// Note: PS4 is sometimes used in scripts with set -x for debugging,
// but this is debugging-only, not production code.
#[test]
fn test_VAR_004_ps1_prompt_not_supported() {
// DOCUMENTATION: PS1 is NOT SUPPORTED (interactive only)
//
// PS1 controls the primary interactive prompt:
// PS1='$ ' # Simple prompt
// PS1='\u@\h:\w\$ ' # user@host:directory$
// PS1='\[\e[32m\]\u@\h\[\e[0m\]:\w\$ ' # Colored prompt
//
// This is interactive only, not used in scripts
let ps1_script = r#"PS1='$ '"#;
let result = BashParser::new(ps1_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"PS1 is interactive only, NOT SUPPORTED in scripts"
);
}
// NOT SUPPORTED because:
// - Interactive only (displayed to users, not script logic)
// - bashrs is script-mode-only (no interactive prompts)
// - POSIX scripts run non-interactively (no prompts)
}
#[test]
fn test_VAR_004_ps2_continuation_prompt_not_supported() {
// DOCUMENTATION: PS2 is NOT SUPPORTED (interactive only)
//
// PS2 is the continuation prompt for multi-line commands:
// $ echo "first line
// > second line"
//
// The "> " is PS2, default continuation prompt
//
// Custom PS2:
// PS2='... ' # Changes continuation prompt to "... "
//
// This is interactive only, not used in scripts
let ps2_script = r#"PS2='... '"#;
let result = BashParser::new(ps2_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"PS2 is interactive only, NOT SUPPORTED in scripts"
);
}
// NOT SUPPORTED because:
// - Multi-line interactive input (user typing)
// - Scripts are non-interactive (no continuation prompts)
// - Not part of script logic
}
#[test]
fn test_VAR_004_ps3_select_prompt_not_supported() {
// DOCUMENTATION: PS3 is NOT SUPPORTED (interactive only)
//
// PS3 is the prompt for select command:
// select choice in "Option 1" "Option 2" "Option 3"; do
// echo "You selected: $choice"
// break
// done
//
// Default PS3: "#? "
// Custom PS3: PS3="Choose an option: "
//
// This is interactive only (select command requires user input)
let ps3_script = r#"PS3="Choose: ""#;
let result = BashParser::new(ps3_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"PS3 is interactive only, NOT SUPPORTED in scripts"
);
}
// NOT SUPPORTED because:
// - select command is interactive (requires user input)
// - bashrs is script-mode-only (no select menus)
// - POSIX alternative: command-line arguments or config files
}
#[test]
fn test_VAR_004_ps4_debug_prompt_not_production() {
// DOCUMENTATION: PS4 is debugging only (not production code)
//
// PS4 is the debug trace prompt (set -x):
// set -x
// echo "test"
// # Output: + echo test
//
// The "+ " prefix is PS4, default debug prompt
//
// Custom PS4:
// PS4='DEBUG: '
// set -x
// echo "test"
// # Output: DEBUG: echo test
//
// Sometimes used in scripts for debugging, but not production
let ps4_script = r#"PS4='DEBUG: '"#;
let result = BashParser::new(ps4_script);
if let Ok(mut parser) = result {
let parse_result = parser.parse();
assert!(
parse_result.is_ok() || parse_result.is_err(),
"PS4 is debugging only, not production code"
);
}
// NOT PRODUCTION because:
// - Used with set -x (debugging/tracing)
// - Production scripts should not have set -x
// - Purified scripts remove debugging code
}