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
use crate::vm::environment::{BindingLocation, EnvironmentMap};
use crate::vm::vcell::VCell;
/// Lambda
///
/// Lambda represents a unit of executable bytecode constructed
/// by the compiler with an entry point of bc[0].
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Lambda {
pub top_level: bool,
pub envmap: EnvironmentMap,
pub args: Vec<VCell>,
pub is_vararg: bool,
pub bc: Vec<VCell>,
}
impl Lambda {
/// New
///
/// Create a new lambda with an empty bytecode vector
/// and the given argument list.
///
/// # Arguments
/// `args` - A vector of VCell::Ptr, each guaranteed to
/// point to a symbol representing a formal argument.
pub fn new(args: Vec<VCell>) -> Lambda {
Lambda {
top_level: false,
is_vararg: false,
envmap: EnvironmentMap::new(),
args,
bc: vec![],
}
}
/// New From IOF
///
/// Create a new lambda, and populate its environment map
/// given the IOF and set of free symbols.
pub fn new_from_iof(
args: Vec<VCell>,
internally_defined: Vec<VCell>,
iof: &Lambda,
free_symbols: &[VCell],
is_vararg: bool,
) -> Lambda {
let envmap = EnvironmentMap::new_from_iof(&args, &internally_defined, iof, free_symbols);
Lambda {
top_level: false,
args,
is_vararg,
envmap,
bc: vec![],
}
}
pub fn set_top_level(&mut self) {
self.top_level = true;
}
pub fn is_top_level(&self) -> bool {
self.top_level
}
/// Get
///
/// Get the opcode or operand at the given index
pub fn get(&self, index: usize) -> Option<&VCell> {
self.bc.get(index)
}
/// Emit
///
/// Emit the byte code to the internal bc vector. This
/// method is used during compilation when forming a lambda.
pub fn emit<T: Into<VCell>>(&mut self, vcell: T) {
self.bc.push(vcell.into());
}
/// Binding Location
///
/// Return the binding for the given symbol. First check the lexical environment.
/// It's possible the arguments were copied into the lexical environment in case they're
/// needed by inner procedures, or for other reasons such as set!.
///
/// If the symbol is neither in the lexical environmenr or a known argument then it's
/// globally bound.
///
/// # Arguments
/// `sym` - The symbol to lookup the binding location of.
pub fn binding_location(&self, sym: &VCell) -> BindingLocation {
if let Some(slot) = self.envmap.get_slot(sym) {
BindingLocation::Environment(slot)
} else if let Some((arg, _)) = self.args.iter().enumerate().find(|it| it.1 == sym) {
BindingLocation::Argument(arg)
} else {
BindingLocation::Global
}
}
/// Argument Count
///
/// Return the number of arguments
pub fn argc(&self) -> usize {
self.args.len()
}
}
impl From<Vec<VCell>> for Lambda {
fn from(bc: Vec<VCell>) -> Self {
Lambda {
top_level: false,
envmap: EnvironmentMap::new(),
args: vec![],
is_vararg: false,
bc,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lambda_binding() {
let lambda = Lambda::new(vec![VCell::ptr(100), VCell::ptr(200)]);
assert_eq!(
lambda.binding_location(&VCell::ptr(100)),
BindingLocation::Argument(0)
);
assert_eq!(
lambda.binding_location(&VCell::ptr(200)),
BindingLocation::Argument(1)
);
assert_eq!(
lambda.binding_location(&VCell::ptr(300)),
BindingLocation::Global
);
}
}