Skip to main content

compiler/
compiler.rs

1use object::builtins::BuiltIns;
2use serde::Serialize;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6use object::Object;
7use parser::ast::{BlockStatement, Expression, Literal, Node, Statement};
8use parser::lexer::token::Span;
9use parser::lexer::token::TokenKind;
10
11use crate::op_code::Opcode::*;
12use crate::op_code::{cast_u8_to_opcode, make_instructions, Instructions, Opcode};
13use crate::symbol_table::{Symbol, SymbolScope, SymbolTable};
14
15struct CompilationScope {
16    instructions: Instructions,
17    last_instruction: EmittedInstruction,
18    previous_instruction: EmittedInstruction,
19    debug_info: DebugInfo,
20}
21
22pub struct Compiler {
23    pub constants: Vec<Rc<Object>>,
24    pub symbol_table: SymbolTable,
25    function_debug_info: HashMap<usize, DebugInfo>,
26    scopes: Vec<CompilationScope>,
27    scope_index: usize,
28}
29
30pub struct Bytecode {
31    pub instructions: Instructions,
32    pub constants: Vec<Rc<Object>>,
33    pub debug_info: DebugInfo,
34    pub function_debug_info: HashMap<usize, DebugInfo>,
35}
36
37#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
38#[serde(rename_all = "camelCase")]
39pub struct PcSpan {
40    pub pc: usize,
41    pub span: Span,
42}
43
44#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
45#[serde(rename_all = "camelCase")]
46pub struct DebugInfo {
47    pub pc_spans: Vec<PcSpan>,
48}
49
50#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
51#[serde(tag = "type", rename_all = "camelCase")]
52pub enum InstructionScope {
53    Main,
54    Function { constant_index: usize },
55}
56
57#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
58#[serde(rename_all = "camelCase")]
59pub struct InstructionLineMapping {
60    pub line: usize,
61    pub pc: usize,
62    pub scope: InstructionScope,
63}
64
65#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
66#[serde(rename_all = "camelCase")]
67pub struct BytecodeDebugView {
68    pub detail: String,
69    pub main_debug_info: DebugInfo,
70    pub function_debug_info: HashMap<usize, DebugInfo>,
71    pub instruction_lines: Vec<InstructionLineMapping>,
72}
73
74struct ScopedInstructions {
75    instructions: Instructions,
76    debug_info: DebugInfo,
77}
78
79impl Bytecode {
80    pub fn string(&self) -> String {
81        self.debug_view().detail
82    }
83
84    pub fn debug_view(&self) -> BytecodeDebugView {
85        let mut builder = BytecodeDisplayBuilder::new();
86
87        builder.write_line("Instructions:");
88        for line in self.instructions.string().lines() {
89            builder
90                .write_instruction_line(line, InstructionScope::Main, |line| format!("{line}\n"));
91        }
92
93        builder.write_line("");
94        builder.write_line("Constants:");
95
96        if self.constants.is_empty() {
97            builder.write_line("(none)");
98        } else {
99            for (index, constant) in self.constants.iter().enumerate() {
100                match constant.as_ref() {
101                    Object::CompiledFunction(function) => {
102                        builder.write_line(&format!(
103                            "{index:04} CompiledFunction(num_locals={}, num_parameters={})",
104                            function.num_locals, function.num_parameters
105                        ));
106                        builder.write_line("     Instructions:");
107
108                        let instructions = Instructions {
109                            data: function.instructions.clone(),
110                        };
111                        let scope = InstructionScope::Function {
112                            constant_index: index,
113                        };
114                        for line in instructions.string().lines() {
115                            builder.write_instruction_line(line, scope.clone(), |line| {
116                                format!("       {line}\n")
117                            });
118                        }
119                    }
120                    value => builder.write_line(&format!("{index:04} {value}")),
121                }
122            }
123        }
124
125        BytecodeDebugView {
126            detail: builder.output,
127            main_debug_info: self.debug_info.clone(),
128            function_debug_info: self.function_debug_info.clone(),
129            instruction_lines: builder.instruction_lines,
130        }
131    }
132}
133
134struct BytecodeDisplayBuilder {
135    output: String,
136    line: usize,
137    instruction_lines: Vec<InstructionLineMapping>,
138}
139
140impl BytecodeDisplayBuilder {
141    fn new() -> Self {
142        Self {
143            output: String::new(),
144            line: 0,
145            instruction_lines: vec![],
146        }
147    }
148
149    fn write_line(&mut self, line: &str) {
150        self.output.push_str(line);
151        self.output.push('\n');
152        self.line += 1;
153    }
154
155    fn write_instruction_line(
156        &mut self,
157        raw_line: &str,
158        scope: InstructionScope,
159        format_line: impl FnOnce(&str) -> String,
160    ) {
161        if let Some(pc) = parse_instruction_pc(raw_line) {
162            self.instruction_lines.push(InstructionLineMapping {
163                line: self.line,
164                pc,
165                scope,
166            });
167        }
168
169        self.output.push_str(&format_line(raw_line));
170        self.line += 1;
171    }
172}
173
174fn parse_instruction_pc(line: &str) -> Option<usize> {
175    let trimmed = line.trim_start();
176    if trimmed.len() < 4 {
177        return None;
178    }
179
180    let pc_part = &trimmed[..4];
181    if !pc_part.chars().all(|c| c.is_ascii_digit()) {
182        return None;
183    }
184
185    pc_part.parse().ok()
186}
187
188impl DebugInfo {
189    pub fn add_pc_span(&mut self, pc: usize, span: &Span) {
190        if self
191            .pc_spans
192            .last()
193            .map(|last| last.span == *span)
194            .unwrap_or(false)
195        {
196            return;
197        }
198
199        self.pc_spans.push(PcSpan {
200            pc,
201            span: span.clone(),
202        });
203    }
204
205    pub fn span_for_pc(&self, pc: usize) -> Option<&Span> {
206        self.pc_spans
207            .iter()
208            .rev()
209            .find(|pc_span| pc_span.pc <= pc)
210            .map(|pc_span| &pc_span.span)
211    }
212
213    fn truncate_from_pc(&mut self, pc: usize) {
214        self.pc_spans.retain(|pc_span| pc_span.pc < pc);
215    }
216}
217
218#[derive(Clone)]
219pub struct EmittedInstruction {
220    pub opcode: Opcode,
221    pub position: usize,
222}
223
224type CompileError = String;
225
226impl Compiler {
227    pub fn new() -> Compiler {
228        let main_scope = CompilationScope {
229            instructions: Instructions {
230                data: vec![],
231            },
232            last_instruction: EmittedInstruction {
233                opcode: OpNull,
234                position: 0,
235            },
236            previous_instruction: EmittedInstruction {
237                opcode: OpNull,
238                position: 0,
239            },
240            debug_info: DebugInfo::default(),
241        };
242
243        let mut symbol_table = SymbolTable::new();
244        for (key, value) in BuiltIns.iter().enumerate() {
245            symbol_table.define_builtin(key, value.0.to_string());
246        }
247
248        return Compiler {
249            constants: vec![],
250            symbol_table,
251            function_debug_info: HashMap::new(),
252            scopes: vec![main_scope],
253            scope_index: 0,
254        };
255    }
256
257    pub fn new_with_state(symbol_table: SymbolTable, constants: Vec<Rc<Object>>) -> Compiler {
258        let mut compiler = Compiler::new();
259        compiler.constants = constants;
260        compiler.symbol_table = symbol_table;
261        return compiler;
262    }
263
264    pub fn compile(&mut self, node: &Node) -> Result<Bytecode, CompileError> {
265        match node {
266            Node::Program(p) => {
267                for stmt in &p.body {
268                    self.compile_stmt(stmt)?;
269                }
270            }
271            Node::Statement(s) => {
272                self.compile_stmt(s)?;
273            }
274            Node::Expression(e) => {
275                self.compile_expr(e)?;
276            }
277        }
278
279        return Ok(self.bytecode());
280    }
281
282    fn compile_stmt(&mut self, s: &Statement) -> Result<(), CompileError> {
283        match s {
284            Statement::Let(let_statement) => {
285                let symbol = self
286                    .symbol_table
287                    .define(let_statement.identifier.kind.to_string());
288                self.compile_expr(&let_statement.expr)?;
289                if symbol.scope == SymbolScope::Global {
290                    self.emit_with_span(
291                        Opcode::OpSetGlobal,
292                        &vec![symbol.index],
293                        &let_statement.span,
294                    );
295                } else {
296                    self.emit_with_span(
297                        Opcode::OpSetLocal,
298                        &vec![symbol.index],
299                        &let_statement.span,
300                    );
301                }
302                return Ok(());
303            }
304            Statement::Return(r) => {
305                self.compile_expr(&r.argument)?;
306                self.emit_with_span(Opcode::OpReturnValue, &vec![], &r.span);
307                return Ok(());
308            }
309            Statement::Expr(e) => {
310                self.compile_expr(e)?;
311                self.emit_with_span(OpPop, &vec![], expression_span(e));
312                return Ok(());
313            }
314        }
315    }
316
317    fn compile_expr(&mut self, e: &Expression) -> Result<(), CompileError> {
318        match e {
319            Expression::IDENTIFIER(identifier) => {
320                let symbol = self.symbol_table.resolve(identifier.name.clone());
321                match symbol {
322                    Some(symbol) => {
323                        self.load_symbol(&symbol, &identifier.span);
324                    }
325                    None => {
326                        return Err(format!("Undefined variable '{}'", identifier.name));
327                    }
328                }
329            }
330            Expression::LITERAL(l) => match l {
331                Literal::Integer(i) => {
332                    let int = Object::Integer(i.raw);
333                    let operands = vec![self.add_constant(int)];
334                    self.emit_with_span(OpConst, &operands, &i.span);
335                }
336                Literal::Boolean(i) => {
337                    if i.raw {
338                        self.emit_with_span(OpTrue, &vec![], &i.span);
339                    } else {
340                        self.emit_with_span(OpFalse, &vec![], &i.span);
341                    }
342                }
343                Literal::String(s) => {
344                    let string_object = Object::String(s.raw.clone());
345                    let operands = vec![self.add_constant(string_object)];
346                    self.emit_with_span(OpConst, &operands, &s.span);
347                }
348                Literal::Array(array) => {
349                    for element in array.elements.iter() {
350                        self.compile_expr(element)?;
351                    }
352                    self.emit_with_span(OpArray, &vec![array.elements.len()], &array.span);
353                }
354                Literal::Hash(hash) => {
355                    for (key, value) in hash.elements.iter() {
356                        self.compile_expr(&key)?;
357                        self.compile_expr(&value)?;
358                    }
359                    self.emit_with_span(OpHash, &vec![hash.elements.len() * 2], &hash.span);
360                }
361            },
362            Expression::PREFIX(prefix) => {
363                self.compile_expr(&prefix.operand).unwrap();
364                match prefix.op.kind {
365                    TokenKind::MINUS => {
366                        self.emit_with_span(OpMinus, &vec![], &prefix.span);
367                    }
368                    TokenKind::BANG => {
369                        self.emit_with_span(OpBang, &vec![], &prefix.span);
370                    }
371                    _ => {
372                        return Err(format!("unexpected prefix op: {}", prefix.op));
373                    }
374                }
375            }
376            Expression::INFIX(infix) => {
377                if infix.op.kind == TokenKind::LT {
378                    self.compile_expr(&infix.right).unwrap();
379                    self.compile_expr(&infix.left).unwrap();
380                    self.emit_with_span(Opcode::OpGreaterThan, &vec![], &infix.span);
381                    return Ok(());
382                }
383                self.compile_expr(&infix.left).unwrap();
384                self.compile_expr(&infix.right).unwrap();
385                match infix.op.kind {
386                    TokenKind::PLUS => {
387                        self.emit_with_span(OpAdd, &vec![], &infix.span);
388                    }
389                    TokenKind::MINUS => {
390                        self.emit_with_span(OpSub, &vec![], &infix.span);
391                    }
392                    TokenKind::ASTERISK => {
393                        self.emit_with_span(OpMul, &vec![], &infix.span);
394                    }
395                    TokenKind::SLASH => {
396                        self.emit_with_span(OpDiv, &vec![], &infix.span);
397                    }
398                    TokenKind::GT => {
399                        self.emit_with_span(Opcode::OpGreaterThan, &vec![], &infix.span);
400                    }
401                    TokenKind::EQ => {
402                        self.emit_with_span(Opcode::OpEqual, &vec![], &infix.span);
403                    }
404                    TokenKind::NotEq => {
405                        self.emit_with_span(Opcode::OpNotEqual, &vec![], &infix.span);
406                    }
407                    _ => {
408                        return Err(format!("unexpected infix op: {}", infix.op));
409                    }
410                }
411            }
412            Expression::IF(if_node) => {
413                self.compile_expr(&if_node.condition)?;
414                let jump_not_truthy =
415                    self.emit_with_span(OpJumpNotTruthy, &vec![9527], &if_node.span);
416                self.compile_block_statement(&if_node.consequent)?;
417                if self.last_instruction_is(OpPop) {
418                    self.remove_last_pop();
419                }
420
421                let jump_pos = self.emit_with_span(OpJump, &vec![9527], &if_node.span);
422
423                let after_consequence_location = self.current_instruction().data.len();
424                self.change_operand(jump_not_truthy, after_consequence_location);
425
426                if if_node.alternate.is_none() {
427                    self.emit_with_span(OpNull, &vec![], &if_node.span);
428                } else {
429                    self.compile_block_statement(&if_node.clone().alternate.unwrap())?;
430                    if self.last_instruction_is(OpPop) {
431                        self.remove_last_pop();
432                    }
433                }
434                let after_alternative_location = self.current_instruction().data.len();
435                self.change_operand(jump_pos, after_alternative_location);
436            }
437            Expression::Index(index) => {
438                self.compile_expr(&index.object)?;
439                self.compile_expr(&index.index)?;
440                self.emit_with_span(OpIndex, &vec![], &index.span);
441            }
442            Expression::FUNCTION(f) => {
443                let function_span = f.span.clone();
444                self.enter_scope();
445                // f.name
446                for param in f.params.iter() {
447                    self.symbol_table.define(param.name.clone());
448                }
449                self.compile_block_statement(&f.body)?;
450                if self.last_instruction_is(OpPop) {
451                    self.replace_last_pop_with_return();
452                }
453                if !(self.last_instruction_is(OpReturnValue)) {
454                    self.emit_with_span(OpReturn, &vec![], &function_span);
455                }
456                let num_locals = self.symbol_table.num_definitions;
457                let free_symbols = self.symbol_table.free_symbols.clone();
458                let scoped_instructions = self.leave_scope();
459                for x in free_symbols.clone() {
460                    self.load_symbol(&x, &function_span);
461                }
462
463                let compiled_function = Rc::from(object::CompiledFunction {
464                    instructions: scoped_instructions.instructions.data,
465                    num_locals,
466                    num_parameters: f.params.len(),
467                });
468
469                let constant_index = self.add_constant(Object::CompiledFunction(compiled_function));
470                self.function_debug_info_mut()
471                    .insert(constant_index, scoped_instructions.debug_info);
472                let operands = vec![constant_index, free_symbols.len()];
473                self.emit_with_span(OpClosure, &operands, &function_span);
474            }
475            Expression::FunctionCall(fc) => {
476                self.compile_expr(&fc.callee)?;
477                for arg in fc.arguments.iter() {
478                    self.compile_expr(arg)?;
479                }
480                self.emit_with_span(OpCall, &vec![fc.arguments.len()], &fc.span);
481            }
482        }
483
484        return Ok(());
485    }
486
487    fn load_symbol(&mut self, symbol: &Rc<Symbol>, span: &Span) {
488        match symbol.scope {
489            SymbolScope::Global => {
490                self.emit_with_span(OpGetGlobal, &vec![symbol.index], span);
491            }
492            SymbolScope::LOCAL => {
493                self.emit_with_span(OpGetLocal, &vec![symbol.index], span);
494            }
495            SymbolScope::Builtin => {
496                self.emit_with_span(OpGetBuiltin, &vec![symbol.index], span);
497            }
498            SymbolScope::Free => {
499                self.emit_with_span(OpGetFree, &vec![symbol.index], span);
500            }
501            SymbolScope::Function => {
502                self.emit_with_span(OpCurrentClosure, &vec![], span);
503            }
504        }
505    }
506
507    pub fn bytecode(&self) -> Bytecode {
508        return Bytecode {
509            instructions: self.current_instruction().clone(),
510            constants: self.constants.clone(),
511            debug_info: self.current_debug_info().clone(),
512            function_debug_info: self.function_debug_info.clone(),
513        };
514    }
515
516    pub fn add_constant(&mut self, obj: Object) -> usize {
517        self.constants.push(Rc::new(obj));
518        return self.constants.len() - 1;
519    }
520
521    pub fn emit(&mut self, op: Opcode, operands: &Vec<usize>) -> usize {
522        let ins = make_instructions(op, operands);
523        let pos = self.add_instructions(&ins);
524        self.set_last_instruction(op, pos);
525
526        return pos;
527    }
528
529    pub fn emit_with_span(&mut self, op: Opcode, operands: &Vec<usize>, span: &Span) -> usize {
530        let pos = self.emit(op, operands);
531        self.add_pc_span(pos, span);
532        pos
533    }
534
535    fn compile_block_statement(
536        &mut self,
537        block_statement: &BlockStatement,
538    ) -> Result<(), CompileError> {
539        for stmt in &block_statement.body {
540            self.compile_stmt(stmt)?;
541        }
542        Ok(())
543    }
544
545    pub fn add_instructions(&mut self, ins: &Instructions) -> usize {
546        let pos = self.current_instruction().data.len();
547        let updated_ins = self.scopes[self.scope_index]
548            .instructions
549            .merge_instructions(ins);
550        self.scopes[self.scope_index].instructions = updated_ins;
551        return pos;
552    }
553
554    fn set_last_instruction(&mut self, op: Opcode, pos: usize) {
555        let previous_instruction = self.scopes[self.scope_index].last_instruction.clone();
556        let last_instruction = EmittedInstruction {
557            opcode: op,
558            position: pos,
559        };
560        self.scopes[self.scope_index].last_instruction = last_instruction;
561        self.scopes[self.scope_index].previous_instruction = previous_instruction;
562    }
563
564    fn last_instruction_is(&self, op: Opcode) -> bool {
565        if self.current_instruction().data.len() == 0 {
566            return false;
567        }
568        return self.scopes[self.scope_index].last_instruction.opcode == op;
569    }
570
571    fn remove_last_pop(&mut self) {
572        let last = self.scopes[self.scope_index].last_instruction.clone();
573        let previous = self.scopes[self.scope_index].previous_instruction.clone();
574
575        let old = self.current_instruction().data.clone();
576        let new = old[..last.position].to_vec();
577
578        self.scopes[self.scope_index].instructions.data = new;
579        self.scopes[self.scope_index]
580            .debug_info
581            .truncate_from_pc(last.position);
582        self.scopes[self.scope_index].last_instruction = previous;
583    }
584
585    fn replace_instruction(&mut self, pos: usize, new_instruction: &Instructions) {
586        let ins = &mut self.scopes[self.scope_index].instructions;
587        for i in 0..new_instruction.data.len() {
588            ins.data[pos + i] = new_instruction.data[i];
589        }
590    }
591
592    fn replace_last_pop_with_return(&mut self) {
593        let last_pos = self.scopes[self.scope_index].last_instruction.position;
594        self.replace_instruction(last_pos, &make_instructions(OpReturnValue, &vec![]));
595        self.scopes[self.scope_index].last_instruction.opcode = OpReturnValue;
596    }
597
598    fn change_operand(&mut self, pos: usize, operand: usize) {
599        let op = cast_u8_to_opcode(self.current_instruction().data[pos]);
600        let ins = make_instructions(op, &vec![operand]);
601        self.replace_instruction(pos, &ins);
602    }
603
604    fn current_instruction(&self) -> &Instructions {
605        return &self.scopes[self.scope_index].instructions;
606    }
607
608    fn current_debug_info(&self) -> &DebugInfo {
609        return &self.scopes[self.scope_index].debug_info;
610    }
611
612    fn function_debug_info_mut(&mut self) -> &mut HashMap<usize, DebugInfo> {
613        return &mut self.function_debug_info;
614    }
615
616    fn add_pc_span(&mut self, pc: usize, span: &Span) {
617        self.scopes[self.scope_index]
618            .debug_info
619            .add_pc_span(pc, span);
620    }
621
622    fn enter_scope(&mut self) {
623        let scope = CompilationScope {
624            instructions: Instructions {
625                data: vec![],
626            },
627            last_instruction: EmittedInstruction {
628                opcode: OpNull,
629                position: 0,
630            },
631            previous_instruction: EmittedInstruction {
632                opcode: OpNull,
633                position: 0,
634            },
635            debug_info: DebugInfo::default(),
636        };
637        self.scopes.push(scope);
638        self.scope_index += 1;
639        self.symbol_table = SymbolTable::new_enclosed_symbol_table(self.symbol_table.clone());
640    }
641
642    fn leave_scope(&mut self) -> ScopedInstructions {
643        let instructions = self.current_instruction().clone();
644        let debug_info = self.current_debug_info().clone();
645        self.scopes.pop();
646        self.scope_index -= 1;
647        let s = self.symbol_table.outer.as_ref().unwrap().as_ref().clone();
648        self.symbol_table = s;
649        return ScopedInstructions {
650            instructions,
651            debug_info,
652        };
653    }
654}
655
656fn expression_span(expression: &Expression) -> &Span {
657    match expression {
658        Expression::IDENTIFIER(identifier) => &identifier.span,
659        Expression::LITERAL(literal) => literal_span(literal),
660        Expression::PREFIX(prefix) => &prefix.span,
661        Expression::INFIX(infix) => &infix.span,
662        Expression::IF(if_expression) => &if_expression.span,
663        Expression::FUNCTION(function) => &function.span,
664        Expression::FunctionCall(function_call) => &function_call.span,
665        Expression::Index(index) => &index.span,
666    }
667}
668
669fn literal_span(literal: &Literal) -> &Span {
670    match literal {
671        Literal::Integer(integer) => &integer.span,
672        Literal::Boolean(boolean) => &boolean.span,
673        Literal::String(string) => &string.span,
674        Literal::Array(array) => &array.span,
675        Literal::Hash(hash) => &hash.span,
676    }
677}