1use crate::{analyzer::SemanticAnalyzer, error::RuntimeError};
2use rand::prelude::*;
3
4#[derive(Debug, PartialEq)]
6enum ExecutionControl {
7 Continue,
9 Jump(isize),
11 Terminate,
13}
14
15#[derive(Debug, Clone)]
16#[allow(dead_code)]
17enum Instruction {
18 LdcI4(u32), Stloc0, Stloc1, Stloc2, Ldloc0, Ldloc1, Ldloc2, Pop, Dup, Add, Sub, Mul, Div, Rem, Ceq, Cgt, Clt, Br(isize), Brtrue(isize), Brfalse(isize), Call(String), Ret, CallWriteLine, CallWrite, CallWriteStr(String), CallWriteLineErr, CallWriteStrErr(String), CallRandom, }
64
65type Bytecode = Vec<Instruction>;
66
67struct Compiler;
68impl Compiler {
69 pub fn compile(source: &str) -> Result<Bytecode, Box<dyn std::error::Error>> {
70 let mut bytecode = Vec::<Instruction>::new();
71 let mut analyzer = match SemanticAnalyzer::new(source) {
72 Ok(analyzer) => analyzer,
73 Err(e) => return Err(Box::new(e)),
74 };
75 let ast = match analyzer.analyze() {
76 Ok(ast) => ast,
77 Err(e) => return Err(Box::new(e)),
78 };
79 if let Some(stmt) = ast.statement {
80 match stmt.kind {
81 crate::ast::StatementKind::Expression { expr } => {
82 let crate::ast::ExpressionKind::Dice { count, faces } = expr.kind;
83
84 bytecode.push(Instruction::LdcI4(count)); bytecode.push(Instruction::Stloc0); bytecode.push(Instruction::LdcI4(faces)); bytecode.push(Instruction::Stloc1); bytecode.push(Instruction::LdcI4(0)); bytecode.push(Instruction::Stloc2); bytecode.push(Instruction::Ldloc0); bytecode.push(Instruction::LdcI4(0)); bytecode.push(Instruction::Cgt); let brfalse_index = bytecode.len(); bytecode.push(Instruction::Brfalse(0)); bytecode.push(Instruction::Ldloc1); bytecode.push(Instruction::CallRandom); bytecode.push(Instruction::Dup); bytecode.push(Instruction::CallWriteLine); bytecode.push(Instruction::Ldloc2); bytecode.push(Instruction::Add); bytecode.push(Instruction::Stloc2); bytecode.push(Instruction::Ldloc0); bytecode.push(Instruction::LdcI4(1)); bytecode.push(Instruction::Sub); bytecode.push(Instruction::Stloc0); const LOOP_START_PC: isize = 6;
124 const CURRENT_PC: isize = 21;
125 let br_back_offset = LOOP_START_PC - CURRENT_PC; bytecode.push(Instruction::Br(br_back_offset)); let loop_exit_pc = bytecode.len(); if count > 1 {
131 bytecode.push(Instruction::CallWriteStrErr("Total: ".to_string())); bytecode.push(Instruction::Ldloc2); bytecode.push(Instruction::CallWriteLineErr); }
135
136 let brfalse_offset = loop_exit_pc as isize - brfalse_index as isize;
138 bytecode[brfalse_index] = Instruction::Brfalse(brfalse_offset);
139 }
140 }
141 }
142 Ok(bytecode)
143 }
144}
145
146pub struct StackVm {
147 stack: Vec<u32>,
148 locals: [u32; 3], rng: ThreadRng,
150}
151
152impl Default for StackVm {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158impl StackVm {
159 pub fn new() -> Self {
160 Self {
161 stack: Vec::new(),
162 locals: [0; 3],
163 rng: ThreadRng::default(),
164 }
165 }
166
167 pub fn execute(&mut self, source: &str) -> Result<(), Box<dyn std::error::Error>> {
168 let bytecode = Compiler::compile(source)?;
169 let mut pc = 0;
170
171 while pc < bytecode.len() {
172 let instruction = &bytecode[pc];
173 let control = self.execute_instruction(instruction)?;
174
175 match control {
176 ExecutionControl::Terminate => {
177 break;
179 }
180 ExecutionControl::Continue => {
181 pc += 1;
182 }
183 ExecutionControl::Jump(offset) => {
184 let new_pc = (pc as isize) + offset;
186 if new_pc < 0 {
187 return Err(Box::new(RuntimeError::InvalidStackState));
188 } else if new_pc >= bytecode.len() as isize {
189 break;
191 } else {
192 pc = new_pc as usize;
193 }
194 }
195 }
196 }
197
198 Ok(())
200 }
201
202 fn execute_instruction(
203 &mut self,
204 instruction: &Instruction,
205 ) -> Result<ExecutionControl, Box<dyn std::error::Error>> {
206 match instruction {
207 Instruction::LdcI4(value) => {
209 self.stack.push(*value);
210 }
211
212 Instruction::Stloc0 => {
214 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
215 self.locals[0] = value;
216 }
217 Instruction::Stloc1 => {
218 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
219 self.locals[1] = value;
220 }
221 Instruction::Stloc2 => {
222 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
223 self.locals[2] = value;
224 }
225 Instruction::Ldloc0 => {
226 self.stack.push(self.locals[0]);
227 }
228 Instruction::Ldloc1 => {
229 self.stack.push(self.locals[1]);
230 }
231 Instruction::Ldloc2 => {
232 self.stack.push(self.locals[2]);
233 }
234
235 Instruction::Pop => {
237 self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
238 }
239 Instruction::Dup => {
240 let value = self
241 .stack
242 .last()
243 .copied()
244 .ok_or(RuntimeError::InvalidStackState)?;
245 self.stack.push(value);
246 }
247
248 Instruction::Add => {
250 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
251 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
252 self.stack.push(a.wrapping_add(b));
253 }
254 Instruction::Sub => {
255 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
256 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
257 self.stack.push(a.wrapping_sub(b));
258 }
259 Instruction::Mul => {
260 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
261 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
262 self.stack.push(a.wrapping_mul(b));
263 }
264 Instruction::Div => {
265 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
266 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
267 if b == 0 {
268 return Err(Box::new(RuntimeError::DivisionByZero));
269 }
270 self.stack.push(a / b);
271 }
272 Instruction::Rem => {
273 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
274 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
275 if b == 0 {
276 return Err(Box::new(RuntimeError::DivisionByZero));
277 }
278 self.stack.push(a % b);
279 }
280
281 Instruction::Ceq => {
283 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
284 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
285 self.stack.push(if a == b { 1 } else { 0 });
286 }
287 Instruction::Cgt => {
288 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
289 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
290 self.stack.push(if a > b { 1 } else { 0 });
291 }
292 Instruction::Clt => {
293 let b = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
294 let a = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
295 self.stack.push(if a < b { 1 } else { 0 });
296 }
297
298 Instruction::Br(offset) => {
300 return Ok(ExecutionControl::Jump(*offset));
301 }
302 Instruction::Brtrue(offset) => {
303 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
304 if value != 0 {
305 return Ok(ExecutionControl::Jump(*offset));
306 }
307 }
308 Instruction::Brfalse(offset) => {
309 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
310 if value == 0 {
311 return Ok(ExecutionControl::Jump(*offset));
312 }
313 }
314
315 Instruction::CallWriteLine => {
317 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
318 println!("{value}");
319 }
320 Instruction::CallWrite => {
321 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
322 print!("{value}");
323 }
324 Instruction::CallWriteStr(s) => {
325 print!("{s}");
326 }
327 Instruction::CallWriteLineErr => {
328 let value = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
329 eprintln!("{value}");
330 }
331 Instruction::CallWriteStrErr(s) => {
332 eprint!("{s}");
333 }
334
335 Instruction::CallRandom => {
337 let max = self.stack.pop().ok_or(RuntimeError::InvalidStackState)?;
338 if max == 0 {
339 self.stack.push(0);
340 } else {
341 let random_value = self.rng.random_range(1..=max);
343 self.stack.push(random_value);
344 }
345 }
346
347 Instruction::Call(_) => {
349 return Err(Box::new(RuntimeError::InvalidStackState));
350 }
351 Instruction::Ret => {
352 return Ok(ExecutionControl::Terminate);
354 }
355 };
356 Ok(ExecutionControl::Continue) }
358}