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
use cas_compute::{numerical::value::Value, primitive::int};
use cas_error::Error;
use cas_parser::parser::{
ast::{for_expr::For, loop_expr::Loop, while_expr::While, RangeKind},
token::op::BinOpKind,
};
use crate::{item::Symbol, Compile, Compiler, InstructionKind};
impl Compile for Loop {
fn compile(&self, compiler: &mut Compiler) -> Result<(), Error> {
let loop_start = compiler.new_end_label();
let loop_end = compiler.new_unassociated_label();
compiler.with_state(|state| {
// in case `continue` and `break` expressions are inside, we need the loop start and
// end labels for their jumps
state.loop_start = Some(loop_start);
state.loop_end = Some(loop_end);
}, |compiler| {
compiler.new_scope(|compiler| self.body.compile(compiler))?;
compiler.add_instr(InstructionKind::Drop);
Ok(())
})?;
compiler.add_instr(InstructionKind::Jump(loop_start));
// NOTE: we don't have to do the same "hack" as in `while` loops, since `loop`s cannot
// terminate without a `break` expression
compiler.set_end_label(loop_end);
Ok(())
}
}
impl Compile for While {
fn compile(&self, compiler: &mut Compiler) -> Result<(), Error> {
let condition_start = compiler.new_end_label();
self.condition.compile(compiler)?;
let end_with_no_break = compiler.new_unassociated_label();
let loop_end = compiler.new_unassociated_label();
compiler.add_instr_with_spans(
InstructionKind::JumpIfFalse(end_with_no_break),
// for error if condition doesn't evaluate to boolean
vec![self.condition.span(), self.body.span()],
);
compiler.with_state(|state| {
// in case `continue` and `break` expressions are inside, we need the loop start and
// end labels for their jumps
state.loop_start = Some(condition_start);
state.loop_end = Some(loop_end);
}, |compiler| {
compiler.new_scope(|compiler| self.body.compile(compiler))?;
compiler.add_instr(InstructionKind::Drop);
Ok(())
})?;
compiler.add_instr(InstructionKind::Jump(condition_start));
// if the loop doesn't terminate through a `break` expression, we need to load something to
// the stack so that the automatically generated `Drop` instruction has something to drop
//
// TODO: this can be optimized out at some point
compiler.set_end_label(end_with_no_break);
compiler.add_instr(InstructionKind::LoadConst(Value::Unit));
compiler.set_end_label(loop_end);
Ok(())
}
}
impl Compile for For {
fn compile(&self, compiler: &mut Compiler) -> Result<(), Error> {
// ```
// for i in 0..10 then print(i)
// ```
//
// equivalent to:
//
// ```
// i = 0
// while i < 10 {
// print(i)
// i += 1
// }
// ```
//
// but with control flow; specifically, `continue` also increments `i`
//
// TODO: this will one day be generalized to work on any iterator
compiler.new_scope(|compiler| {
// compile range end up here so that the index variable isn't in scope, then insert it
// down at the condition
let chunk = compiler.new_chunk_get(|compiler| {
self.range.end.compile(compiler)
})?;
// assign: initialize index in range, jump past initial increment
self.range.start.compile(compiler)?;
let symbol_id = compiler.add_symbol(&self.variable)?;
compiler.add_instr(InstructionKind::AssignVar(symbol_id));
// condition: continue summing while the variable is in the range:
// `symbol_id < self.range.end`
let condition_start = compiler.new_end_label();
compiler.add_instr(InstructionKind::LoadVar(Symbol::User(symbol_id)));
compiler.add_chunk_instrs(chunk);
match self.range.kind {
RangeKind::HalfOpen => compiler.add_instr(InstructionKind::Binary(BinOpKind::Less)),
RangeKind::Closed => compiler.add_instr(InstructionKind::Binary(BinOpKind::LessEq)),
}
let end_with_no_break = compiler.new_unassociated_label();
let loop_end = compiler.new_unassociated_label();
compiler.add_instr(InstructionKind::JumpIfFalse(end_with_no_break));
// body: run body
let index_start = compiler.new_unassociated_label();
compiler.with_state(|state| {
// in case `continue` and `break` expressions are inside, we need the loop start and
// end labels for their jumps
state.loop_start = Some(index_start);
state.loop_end = Some(loop_end);
}, |compiler| {
self.body.compile(compiler)?;
compiler.add_instr(InstructionKind::Drop);
Ok(())
})?;
// increment index
compiler.set_end_label(index_start);
compiler.add_instr(InstructionKind::LoadVar(Symbol::User(symbol_id)));
compiler.add_instr(InstructionKind::LoadConst(Value::Integer(int(1))));
compiler.add_instr(InstructionKind::Binary(BinOpKind::Add));
compiler.add_instr(InstructionKind::AssignVar(symbol_id));
// jump back to condition
compiler.add_instr(InstructionKind::Jump(condition_start));
// if the loop doesn't terminate through a `break` expression, we need to load
// something to the stack so that the automatically generated `Drop` instruction has
// something to drop
//
// TODO: same potential optimization as in `while` loops
compiler.set_end_label(end_with_no_break);
compiler.add_instr(InstructionKind::LoadConst(Value::Unit));
compiler.set_end_label(loop_end);
Ok(())
})?;
Ok(())
}
}