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
//! Integration tests for the macro system.
//!
//! These tests verify the complete macro expansion pipeline including
//! pattern matching, template expansion, hygiene preservation, and
//! integration with the overall evaluation system.
#[cfg(test)]
mod tests {
use crate::macro_system::*;
use crate::ast::{Expr, Literal, Formals, Program};
use crate::diagnostics::{Spanned, Span};
use crate::eval::Environment;
use std::rc::Rc;
use std::collections::HashMap;
/// Helper function to create a test span.
fn test_span() -> Span {
Span::new(0, 0, 0, 0, 0, None)
}
/// Helper function to create a spanned expression.
fn spanned_expr(expr: Expr) -> Spanned<Expr> {
Spanned::new(expr, test_span())
}
/// Helper function to create an identifier expression.
fn identifier(name: &str) -> Spanned<Expr> {
spanned_expr(Expr::Identifier(name.to_string()))
}
/// Helper function to create a number literal.
fn number(n: f64) -> Spanned<Expr> {
spanned_expr(Expr::Literal(Literal::Number(n)))
}
/// Helper function to create an application expression.
fn application(operator: Spanned<Expr>, operands: Vec<Spanned<Expr>>) -> Spanned<Expr> {
spanned_expr(Expr::Application {
operator: Box::new(operator),
operands,
})
}
#[test]
fn test_macro_expander_creation() {
let expander = MacroExpander::new();
assert_eq!(expander.expansion_depth, 0);
assert_eq!(expander.max_expansion_depth, 100);
}
#[test]
fn test_macro_expander_with_builtins() {
let expander = MacroExpander::with_builtins();
// Should have built-in macros registered
// This test will pass even if builtins aren't fully implemented
assert_eq!(expander.expansion_depth, 0);
}
#[test]
fn test_basic_expression_expansion() {
let mut expander = MacroExpander::new();
// Test expanding a simple expression that doesn't contain macros
let expr = application(
identifier("+"),
vec![number(1.0), number(2.0)]
);
let result = expander.expand(&expr);
assert!(result.is_ok(), "Basic expression expansion should succeed");
// The result should be essentially the same as the input
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::Application { .. }));
}
#[test]
fn test_lambda_expansion() {
let mut expander = MacroExpander::new();
// Test expanding a lambda expression
let lambda_body = vec![application(
identifier("+"),
vec![identifier("x"), number(1.0)]
)];
let lambda_expr = spanned_expr(Expr::Lambda {
formals: Formals::Proper(vec!["x".to_string()]),
metadata: HashMap::new(),
body: lambda_body,
});
let result = expander.expand(&lambda_expr);
assert!(result.is_ok(), "Lambda expansion should succeed");
// The result should be a lambda with expanded body
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::Lambda { .. }));
}
#[test]
fn test_define_expansion() {
let mut expander = MacroExpander::new();
// Test expanding a define expression
let define_expr = spanned_expr(Expr::Define {
name: "test".to_string(),
value: Box::new(application(
identifier("*"),
vec![number(2.0), number(3.0)]
)),
metadata: HashMap::new(),
});
let result = expander.expand(&define_expr);
assert!(result.is_ok(), "Define expansion should succeed");
// The result should be a define with expanded value
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::Define { .. }));
}
#[test]
fn test_if_expansion() {
let mut expander = MacroExpander::new();
// Test expanding an if expression
let if_expr = spanned_expr(Expr::If {
test: Box::new(application(
identifier(">"),
vec![identifier("x"), number(0.0)]
)),
consequent: Box::new(identifier("x")),
alternative: Some(Box::new(number(0.0))),
});
let result = expander.expand(&if_expr);
assert!(result.is_ok(), "If expansion should succeed");
// The result should be an if with expanded components
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::If { .. }));
}
#[test]
#[ignore] // Will pass when macro transformer evaluation is implemented
fn test_define_syntax_expansion() {
let mut expander = MacroExpander::new();
// Test expanding a define-syntax expression
// This creates a simple macro definition
let transformer_expr = spanned_expr(Expr::Application {
operator: Box::new(identifier("syntax-rules")),
operands: vec![
// Empty literals list
spanned_expr(Expr::Application {
operator: Box::new(identifier("quote")),
operands: vec![spanned_expr(Expr::Application {
operator: Box::new(identifier("quote")),
operands: vec![],
})],
}),
// Simple rule: (when test expr) -> (if test expr)
spanned_expr(Expr::Application {
operator: Box::new(identifier("quote")),
operands: vec![
spanned_expr(Expr::Application {
operator: Box::new(identifier("when")),
operands: vec![identifier("test"), identifier("expr")],
}),
spanned_expr(Expr::Application {
operator: Box::new(identifier("if")),
operands: vec![identifier("test"), identifier("expr")],
}),
],
}),
],
});
let define_syntax_expr = spanned_expr(Expr::DefineSyntax {
name: "when".to_string(),
transformer: Box::new(transformer_expr),
});
let result = expander.expand(&define_syntax_expr);
assert!(result.is_ok(), "Define-syntax expansion should succeed");
// The result should be an empty begin (macro definitions don't evaluate to values)
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::Begin(_)));
}
#[test]
fn test_expansion_depth_limit() {
let mut expander = MacroExpander::new();
expander.max_expansion_depth = 2; // Set low limit for testing
// This test would need a recursive macro to properly test depth limits
// For now, we just verify the limit is respected
assert_eq!(expander.max_expansion_depth, 2);
}
#[test]
fn test_program_expansion() {
let mut expander = MacroExpander::new();
// Create a test program with multiple expressions
let expressions = vec![
spanned_expr(Expr::Define {
name: "x".to_string(),
value: Box::new(number(42.0)),
metadata: HashMap::new(),
}),
application(
identifier("+"),
vec![identifier("x"), number(1.0)]
),
];
let program = Program::with_expressions(expressions);
let result = expander.expand_program(&program);
assert!(result.is_ok(), "Program expansion should succeed");
let expanded_program = result.unwrap();
assert_eq!(expanded_program.expressions.len(), 2);
}
#[test]
fn test_nested_application_expansion() {
let mut expander = MacroExpander::new();
// Test expanding nested applications
let nested_expr = application(
identifier("+"),
vec![
application(
identifier("*"),
vec![number(2.0), number(3.0)]
),
application(
identifier("-"),
vec![number(10.0), number(5.0)]
),
]
);
let result = expander.expand(&nested_expr);
assert!(result.is_ok(), "Nested application expansion should succeed");
let expanded = result.unwrap();
assert!(matches!(expanded.inner, Expr::Application { .. }));
}
#[test]
fn test_empty_program_expansion() {
let mut expander = MacroExpander::new();
let empty_program = Program::with_expressions(vec![]);
let result = expander.expand_program(&empty_program);
assert!(result.is_ok(), "Empty program expansion should succeed");
let expanded_program = result.unwrap();
assert_eq!(expanded_program.expressions.len(), 0);
}
#[test]
fn test_macro_environment_isolation() {
let expander1 = MacroExpander::new();
let expander2 = MacroExpander::new();
// Each expander should have its own macro environment
// This is a structural test since we can't easily define macros yet
assert!(!std::ptr::eq(
expander1.macro_env().as_ref(),
expander2.macro_env().as_ref()
));
}
// ============================================================================
// HYGIENE TESTS
// ============================================================================
#[test]
#[ignore] // Will pass when hygiene system is fully implemented
fn test_basic_hygiene_preservation() {
let mut expander = MacroExpander::new();
// Test that variable names don't clash due to macro expansion
// This would require a working macro system to properly test
let expr = identifier("x");
let result = expander.expand(&expr);
assert!(result.is_ok());
}
#[test]
#[ignore] // Will pass when pattern matching is implemented
fn test_pattern_variable_capture() {
// Test that pattern variables are properly captured and substituted
// This requires working pattern matching and template expansion
let pattern = Pattern::Identifier("x".to_string());
let template = Template::Identifier("x".to_string());
// Test structure only for now
assert!(matches!(pattern, Pattern::Identifier(_)));
assert!(matches!(template, Template::Identifier(_)));
}
// ============================================================================
// ERROR HANDLING TESTS
// ============================================================================
#[test]
fn test_invalid_macro_expansion_error() {
let mut expander = MacroExpander::new();
// Test that trying to expand undefined macros doesn't crash
let expr = application(
identifier("undefined-macro"),
vec![number(1.0)]
);
// This should succeed (treat as regular function call)
let result = expander.expand(&expr);
assert!(result.is_ok());
}
#[test]
#[ignore] // Will pass when error handling is improved
fn test_macro_expansion_error_reporting() {
let mut expander = MacroExpander::new();
// Test that macro expansion errors are properly reported
// This would need a macro that can fail to properly test
let expr = identifier("test");
let result = expander.expand(&expr);
// For now, just ensure it doesn't crash
assert!(result.is_ok());
}
// ============================================================================
// INTEGRATION WITH EVALUATION SYSTEM
// ============================================================================
#[test]
#[ignore] // Will pass when full integration is implemented
fn test_macro_expansion_in_evaluation_pipeline() {
// Test that macros are properly expanded before evaluation
// This requires integration with the main evaluation system
// For now, this is a placeholder for future integration tests
assert!(true, "Integration test placeholder");
}
#[test]
#[ignore] // Will pass when built-in macros are implemented
fn test_builtin_macro_functionality() {
let expander = MacroExpander::with_builtins();
// Test that built-in macros like 'when', 'unless', etc. work correctly
// This requires actual built-in macro implementations
assert!(expander.macro_env().lookup("when").is_some() ||
expander.macro_env().lookup("when").is_none()); // Accept either state for now
}
// ============================================================================
// PERFORMANCE TESTS
// ============================================================================
#[test]
fn test_macro_expansion_performance() {
let mut expander = MacroExpander::new();
// Test that macro expansion doesn't have exponential performance
let start = std::time::Instant::now();
// Create a moderately complex expression
let mut expr = number(1.0);
for i in 2..=20 {
expr = application(
identifier("+"),
vec![expr, number(i as f64)]
);
}
let result = expander.expand(&expr);
let duration = start.elapsed();
assert!(result.is_ok(), "Complex expression expansion should succeed");
assert!(duration.as_millis() < 100, "Expansion should be fast");
}
}