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
use super::{Instruction, RlValue};
use crate::{
    expressions::{Expr, Value},
    lexing::Lexer,
    parsing::Parser,
    syntax::Punct,
};

#[cfg(test)]
mod tests;

pub(crate) fn compile(source: &str) -> Vec<Instruction> {
    let tokens = Lexer::from(source).lex_all().expect("Lexing failed");
    let expr = Parser::from(tokens).parse_all().expect("Parsing failed");

    compile_expr(&expr, source)
}

// string handling is shit
pub(crate) fn compile_expr(expr: &Expr, source: &str) -> Vec<Instruction> {
    let mk_str = |(start, end): &(usize, usize)| -> String {
        source.chars().skip(*start).take(*end - *start).collect()
    };

    let mut instrs = Vec::new();

    match expr {
        Expr::Multiple(exprs) => {
            for expr in exprs.iter() {
                instrs.append(&mut compile_expr(&expr, source));
            }
        }
        Expr::Declaration { name, val } => {
            instrs.append(&mut compile_expr(&val, source));
            instrs.push(Instruction::Declare(mk_str(name)));
        }
        // TODO: optimizations
        Expr::Rpn(exprs) => {
            return exprs
                .iter()
                .map(|expr| match expr {
                    Expr::Value(Value::Num(x)) => Instruction::Push(RlValue::Num(*x)),
                    Expr::IdentLookup(range) => Instruction::PushLocal(mk_str(range)),
                    Expr::Op(Punct::Add) => Instruction::Add,
                    Expr::Op(Punct::Sub) => Instruction::Sub,
                    Expr::Op(Punct::Mul) => Instruction::Mul,
                    _ => unimplemented!("{:?}", &expr),
                })
                .collect()
        }
        Expr::Value(Value::Num(x)) => instrs.push(Instruction::Push(RlValue::Num(*x))),
        Expr::Value(Value::Str((start, end))) => {
            instrs.push(Instruction::Push(RlValue::Str(mk_str(&(*start, *end)))));
        }
        Expr::Range { from, to } => {
            instrs.append(&mut compile_expr(&from, source));
            instrs.append(&mut compile_expr(&to, source));
            instrs.push(Instruction::MakeRange);
        }
        Expr::IdentLookup(range) => {
            instrs.push(Instruction::PushLocal(mk_str(range)));
        }
        Expr::FunctionDeclaration { name, params, body } => {
            let mut body = compile_expr(&body, source);

            // function body instructions are preceded by 2 instructions
            // - the function body address pointer
            // - the after-function-body address pointer
            // so that they don't execute initially
            let start = instrs.len() + 3;
            // also taking into consideration the `return` instruction
            let end = start + body.len() + params.len() + 1;

            instrs.push(Instruction::Push(RlValue::Function(
                start,
                params.len() as u8,
            )));
            instrs.push(Instruction::Push(RlValue::Addr(end)));
            instrs.push(Instruction::Jump);

            // the function args will already be on the stack
            instrs.append(
                &mut params
                    .iter()
                    .rev()
                    .map(|p| Instruction::Declare(mk_str(p)))
                    .collect(),
            );

            instrs.append(&mut body);
            instrs.push(Instruction::Ret);
            instrs.push(Instruction::Declare(mk_str(name)));
        }
        Expr::FunctionCall { name, args } => {
            let fn_name = mk_str(name);
            args.iter()
                .for_each(|arg| instrs.append(&mut compile_expr(arg, source)));

            let instr = match fn_name.as_ref() {
                "print" => Instruction::CallNative(fn_name),
                _ => Instruction::Call(fn_name),
            };

            instrs.push(instr);
        }
        _ => unimplemented!("{:?}", &expr),
    }

    instrs
}