cas_compiler/expr/
sum.rs

1use cas_compute::{numerical::value::Value, primitive::int};
2use cas_error::Error;
3use cas_parser::parser::{ast::{RangeKind, Sum}, token::op::BinOpKind};
4use crate::{item::Symbol, Compile, Compiler, InstructionKind};
5
6impl Compile for Sum {
7    fn compile(&self, compiler: &mut Compiler) -> Result<(), Error> {
8        // ```
9        // sum n in 1..10 of n
10        // ```
11        //
12        // equivalent to:
13        //
14        // ```
15        // {
16        //     out = 0
17        //     for n in 1..10 {
18        //         out += n
19        //     }
20        //     out
21        // }
22        // ```
23        //
24        // but there is **no** control flow
25
26        compiler.new_scope(|compiler| {
27            // initialize the sum to 0
28            compiler.add_instr(InstructionKind::LoadConst(Value::Integer(int(0))));
29
30            // compile range end up here so that the index variable isn't in scope, then insert it
31            // down at the condition
32            let chunk = compiler.new_chunk_get(|compiler| {
33                self.range.end.compile(compiler)
34            })?;
35
36            // assign: initialize index in range
37            self.range.start.compile(compiler)?;
38            let symbol_id = compiler.add_symbol(&self.variable)?;
39            compiler.add_instr(InstructionKind::AssignVar(symbol_id));
40
41            // condition: continue summing while the variable is in the range:
42            // `symbol_id < self.range.end`
43            let condition_start = compiler.new_end_label();
44            compiler.add_instr(InstructionKind::LoadVar(Symbol::User(symbol_id)));
45            compiler.add_chunk_instrs(chunk);
46            match self.range.kind {
47                RangeKind::HalfOpen => compiler.add_instr(InstructionKind::Binary(BinOpKind::Less)),
48                RangeKind::Closed => compiler.add_instr(InstructionKind::Binary(BinOpKind::LessEq)),
49            }
50
51            let loop_end = compiler.new_unassociated_label();
52            compiler.add_instr(InstructionKind::JumpIfFalse(loop_end));
53
54            // body: compute body, add it to cummulative sum
55            self.body.compile(compiler)?;
56            compiler.add_instr(InstructionKind::Binary(BinOpKind::Add));
57
58            // increment index
59            compiler.add_instr(InstructionKind::LoadVar(Symbol::User(symbol_id)));
60            compiler.add_instr(InstructionKind::LoadConst(Value::Integer(int(1))));
61            compiler.add_instr(InstructionKind::Binary(BinOpKind::Add));
62            compiler.add_instr(InstructionKind::AssignVar(symbol_id));
63
64            compiler.add_instr(InstructionKind::Jump(condition_start));
65
66            compiler.set_end_label(loop_end);
67            Ok(())
68        })?;
69        Ok(())
70    }
71}