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
/// RED TEST: env() call should convert to EnvVar variant in IR
/// Tests that env("HOME") is properly recognized and converted to ShellValue::EnvVar
#[test]
fn test_env_call_converts_to_ir() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "home".to_string(),
value: Expr::FunctionCall {
name: "env".to_string(),
args: vec![Expr::Literal(Literal::Str("HOME".to_string()))],
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => {
match &stmts[0] {
ShellIR::Let { name, value, .. } => {
assert_eq!(name, "home");
// RED: This will fail until we implement EnvVar variant
match value {
ShellValue::EnvVar { name, default } => {
assert_eq!(name, "HOME");
assert_eq!(default, &None);
}
other => panic!("Expected EnvVar, got {:?}", other),
}
}
_ => panic!("Expected Let statement"),
}
}
_ => panic!("Expected Sequence"),
}
}
/// RED TEST: env_var_or() call should convert to EnvVar with default value
/// Tests that env_var_or("PREFIX", "/usr/local") converts to EnvVar with Some(default)
#[test]
fn test_env_var_or_call_converts_to_ir() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "prefix".to_string(),
value: Expr::FunctionCall {
name: "env_var_or".to_string(),
args: vec![
Expr::Literal(Literal::Str("PREFIX".to_string())),
Expr::Literal(Literal::Str("/usr/local".to_string())),
],
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => {
match &stmts[0] {
ShellIR::Let { name, value, .. } => {
assert_eq!(name, "prefix");
// RED: This will fail until we implement EnvVar variant with default
match value {
ShellValue::EnvVar { name, default } => {
assert_eq!(name, "PREFIX");
assert_eq!(default, &Some("/usr/local".to_string()));
}
other => panic!("Expected EnvVar with default, got {:?}", other),
}
}
_ => panic!("Expected Let statement"),
}
}
_ => panic!("Expected Sequence"),
}
}
/// RED TEST: env() in variable assignment context
/// Tests that env() works in typical variable assignment patterns
#[test]
fn test_env_in_assignment() {
let ast = RestrictedAst {
functions: vec![Function {
name: "setup".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![
Stmt::Let {
name: "user".to_string(),
value: Expr::FunctionCall {
name: "env".to_string(),
args: vec![Expr::Literal(Literal::Str("USER".to_string()))],
},
declaration: true,
},
Stmt::Let {
name: "path".to_string(),
value: Expr::FunctionCall {
name: "env".to_string(),
args: vec![Expr::Literal(Literal::Str("PATH".to_string()))],
},
declaration: true,
},
],
}],
entry_point: "setup".to_string(),
};
let ir = from_ast(&ast).unwrap();
// RED: This will fail until EnvVar variant exists
match ir {
ShellIR::Sequence(stmts) => {
assert_eq!(stmts.len(), 2);
// Check first env() call
match &stmts[0] {
ShellIR::Let { value, .. } => {
assert!(
matches!(value, ShellValue::EnvVar { name, default }
if name == "USER" && default.is_none()),
"First env() should be EnvVar for USER"
);
}
_ => panic!("Expected Let statement"),
}
// Check second env() call
match &stmts[1] {
ShellIR::Let { value, .. } => {
assert!(
matches!(value, ShellValue::EnvVar { name, default }
if name == "PATH" && default.is_none()),
"Second env() should be EnvVar for PATH"
);
}
_ => panic!("Expected Let statement"),
}
}
_ => panic!("Expected Sequence"),
}
}
// ============= Sprint 27b: Command-Line Arguments Support - RED PHASE =============