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
use super::Compilelet;
use crate::compiler::{CompiledMethod, Compiler, Multimethod};
use crate::types::{CompilerResult, Expression, ExpressionKind};
use strontium::machine::instruction::Instruction;
// Implement a compilelet which defines a method within the compiler using a Block as the body.
pub struct MethodCompilelet;
impl Compilelet for MethodCompilelet {
fn compile(
&self,
compiler: &mut Compiler,
expression: Expression,
_target_register: Option<String>,
) -> CompilerResult<Vec<Instruction>> {
match expression.kind.clone() {
ExpressionKind::Method(method) => {
let method_id = Compiler::generate_method_id(&method.name, &method.signature);
// Register with multimethod dispatch table
if let Some(multimethod) = compiler.multimethods.get_mut(&method.name) {
multimethod.add_method(&compiler.parser, method.clone())?;
} else {
let mut m = Multimethod::new(&method.name);
m.add_method(&compiler.parser, method.clone())?;
compiler.multimethods.insert(method.name.clone(), m);
}
// Extract parameter names from the signature
let parameter_names = if let Some(ref sig) = method.signature {
Compiler::extract_variable_names(sig)
} else {
vec![]
};
// Convert the signature to a dispatch pattern for runtime matching
let dispatch_pattern =
Compiler::pattern_to_dispatch_pattern(&method.signature, &compiler.parser);
// Store a placeholder entry BEFORE compiling the body
// This allows recursive methods to reference themselves
compiler.compiled_methods.insert(
method_id.clone(),
CompiledMethod {
id: method_id.clone(),
method_name: method.name.clone(),
pattern: dispatch_pattern,
instructions: vec![], // Placeholder - will be filled in
parameter_names: parameter_names.clone(),
},
);
// Set up local variable scope for compiling the method body
let old_locals = compiler.context.local_variables.clone();
compiler.context.local_variables = parameter_names.iter().cloned().collect();
// Build method preamble: copy argument from 'arg' register to local variables
let mut body_instructions = vec![];
for param_name in ¶meter_names {
body_instructions.push(Instruction::StoreLocal {
name: param_name.clone(),
register: "arg".to_string(),
});
}
// Compile the method body with result going to 'ret' register
body_instructions.append(
&mut compiler
.compile_expression(*method.body.clone(), Some("ret".to_string()))?,
);
// Add RETURN instruction at end of method
body_instructions.push(Instruction::Return);
// Restore previous scope
compiler.context.local_variables = old_locals;
// Update the compiled method with actual instructions
if let Some(compiled) = compiler.compiled_methods.get_mut(&method_id) {
compiled.instructions = body_instructions;
}
}
_ => (),
}
Ok(vec![])
}
}