1use sema_core::{Span, Value};
2
3use crate::chunk::Chunk;
4use crate::opcodes::Op;
5
6pub struct Emitter {
8 chunk: Chunk,
9}
10
11impl Emitter {
12 pub fn new() -> Self {
13 Emitter {
14 chunk: Chunk::new(),
15 }
16 }
17
18 pub fn emit_op(&mut self, op: Op) {
19 self.chunk.code.push(op as u8);
20 }
21
22 pub fn emit_u16(&mut self, val: u16) {
23 self.chunk.code.extend_from_slice(&val.to_le_bytes());
24 }
25
26 pub fn emit_u32(&mut self, val: u32) {
27 self.chunk.code.extend_from_slice(&val.to_le_bytes());
28 }
29
30 pub fn emit_i32(&mut self, val: i32) {
31 self.chunk.code.extend_from_slice(&val.to_le_bytes());
32 }
33
34 pub fn add_const(&mut self, val: Value) -> u16 {
37 for (i, existing) in self.chunk.consts.iter().enumerate() {
38 if *existing == val {
39 return i as u16;
40 }
41 }
42 let idx = self.chunk.consts.len();
43 self.chunk.consts.push(val);
44 idx as u16
45 }
46
47 pub fn emit_const(&mut self, val: Value) {
49 let idx = self.add_const(val);
50 self.emit_op(Op::Const);
51 self.emit_u16(idx);
52 }
53
54 pub fn emit_span(&mut self, span: Span) {
56 self.chunk.spans.push((self.current_pc(), span));
57 }
58
59 pub fn current_pc(&self) -> u32 {
61 self.chunk.code.len() as u32
62 }
63
64 pub fn emit_jump(&mut self, op: Op) -> u32 {
67 self.emit_op(op);
68 let placeholder_pc = self.current_pc();
69 self.emit_i32(0);
70 placeholder_pc
71 }
72
73 pub fn patch_jump(&mut self, placeholder_pc: u32) {
76 let jump_end = placeholder_pc + 4; let offset = self.current_pc() as i32 - jump_end as i32;
78 let bytes = offset.to_le_bytes();
79 let pc = placeholder_pc as usize;
80 self.chunk.code[pc] = bytes[0];
81 self.chunk.code[pc + 1] = bytes[1];
82 self.chunk.code[pc + 2] = bytes[2];
83 self.chunk.code[pc + 3] = bytes[3];
84 }
85
86 pub fn into_chunk(self) -> Chunk {
88 self.chunk
89 }
90}
91
92impl Default for Emitter {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_emit_const() {
104 let mut e = Emitter::new();
105 e.emit_const(Value::int(42));
106 e.emit_op(Op::Return);
107 let chunk = e.into_chunk();
108 assert_eq!(chunk.code[0], Op::Const as u8);
109 assert_eq!(chunk.code[1], 0);
110 assert_eq!(chunk.code[2], 0);
111 assert_eq!(chunk.code[3], Op::Return as u8);
112 assert_eq!(chunk.consts.len(), 1);
113 assert_eq!(chunk.consts[0], Value::int(42));
114 }
115
116 #[test]
117 fn test_emit_jump_and_patch() {
118 let mut e = Emitter::new();
119 e.emit_op(Op::Nil); let patch = e.emit_jump(Op::JumpIfFalse); e.emit_op(Op::True); e.emit_op(Op::Return); e.patch_jump(patch); e.emit_op(Op::False); e.emit_op(Op::Return); let chunk = e.into_chunk();
127 let offset = i32::from_le_bytes(chunk.code[2..6].try_into().unwrap());
128 assert_eq!(offset, 2);
130 }
131
132 #[test]
133 fn test_const_dedup() {
134 let mut e = Emitter::new();
135 let idx1 = e.add_const(Value::int(42));
136 let idx2 = e.add_const(Value::int(42));
137 assert_eq!(idx1, idx2);
138 assert_eq!(e.into_chunk().consts.len(), 1);
139 }
140
141 #[test]
142 fn test_emit_span() {
143 let mut e = Emitter::new();
144 e.emit_span(Span::point(1, 0));
145 e.emit_op(Op::Nil);
146 e.emit_span(Span::point(2, 4));
147 e.emit_op(Op::Return);
148 let chunk = e.into_chunk();
149 assert_eq!(chunk.spans.len(), 2);
150 assert_eq!(chunk.spans[0].0, 0);
151 assert_eq!(chunk.spans[0].1.line, 1);
152 assert_eq!(chunk.spans[0].1.col, 0);
153 assert_eq!(chunk.spans[1].0, 1);
154 assert_eq!(chunk.spans[1].1.line, 2);
155 assert_eq!(chunk.spans[1].1.col, 4);
156 }
157
158 #[test]
159 fn test_current_pc() {
160 let mut e = Emitter::new();
161 assert_eq!(e.current_pc(), 0);
162 e.emit_op(Op::Nil);
163 assert_eq!(e.current_pc(), 1);
164 e.emit_u16(42);
165 assert_eq!(e.current_pc(), 3);
166 e.emit_u32(100);
167 assert_eq!(e.current_pc(), 7);
168 }
169}