calx_vm/
vm.rs

1mod block_data;
2pub mod frame;
3pub mod func;
4pub mod instr;
5
6use std::collections::hash_map::HashMap;
7use std::ops::Rem;
8use std::rc::Rc;
9use std::{fmt, vec};
10
11use crate::calx::Calx;
12use crate::syntax::CalxSyntax;
13use crate::vm::block_data::BlockStack;
14
15use self::block_data::BlockData;
16use self::frame::CalxFrame;
17use self::func::CalxFunc;
18use self::instr::CalxInstr;
19
20pub type CalxImportsDict = HashMap<String, (fn(xs: Vec<Calx>) -> Result<Calx, CalxError>, usize)>;
21
22/// Virtual Machine for Calx
23/// code is evaluated in a several steps:
24/// 1. parse into `CalxSyntax`
25/// 2. preprocess `CalxSyntax` into instructions(`CalxInstr`)
26/// 3. run instructions
27///
28/// `CalxSyntax` contains some richer info than `CalxInstr`.
29#[derive(Clone)]
30pub struct CalxVM {
31  pub stack: Vec<Calx>,
32  pub globals: Vec<Calx>,
33  pub funcs: Vec<CalxFunc>,
34  pub frames: Vec<CalxFrame>,
35  pub top_frame: CalxFrame,
36  pub imports: CalxImportsDict,
37  /// extra status to tracking runnnig finished
38  pub finished: bool,
39  pub return_value: Calx,
40}
41
42impl std::fmt::Debug for CalxVM {
43  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44    f.write_str("CalxVM Instance")
45  }
46}
47
48impl CalxVM {
49  pub fn new(fns: Vec<CalxFunc>, globals: Vec<Calx>, imports: CalxImportsDict) -> Self {
50    let main_func = fns.iter().find(|x| *x.name == "main").expect("main function is required");
51    let main_frame = CalxFrame {
52      name: main_func.name.to_owned(),
53      initial_stack_size: 0,
54      // use empty instrs, will be replaced by preprocess
55      instrs: Rc::new(vec![]),
56      pointer: 0,
57      locals: vec![],
58      ret_types: main_func.ret_types.clone(),
59    };
60    CalxVM {
61      stack: vec![],
62      globals,
63      funcs: fns,
64      frames: vec![],
65      top_frame: main_frame,
66      imports,
67      return_value: Calx::Nil,
68      finished: false,
69    }
70  }
71
72  pub fn setup_top_frame(&mut self) -> Result<(), String> {
73    self.top_frame.instrs = match self.find_func("main") {
74      Some(f) => match f.instrs.to_owned() {
75        Some(x) => x,
76        None => return Err("main function must have instrs".to_owned()),
77      },
78      None => return Err("main function is required".to_owned()),
79    };
80
81    Ok(())
82  }
83
84  pub fn make_return(&mut self, v: Calx) {
85    self.return_value = v;
86    self.finished = true;
87  }
88
89  pub fn inspect_display(&self, indent_size: u8) -> String {
90    let mut output = String::new();
91    let indent = "\n".to_owned() + &" ".repeat(indent_size as usize);
92    fmt::write(
93      &mut output,
94      format_args!(
95        "{indent}Internal frames: {:?}",
96        self.frames.iter().map(|x| x.name.to_owned()).collect::<Vec<_>>()
97      ),
98    )
99    .expect("inspect display");
100
101    fmt::write(&mut output, format_args!("{indent}Top frame: {}", self.top_frame.name)).expect("inspect display");
102    fmt::write(&mut output, format_args!("{indent}Locals: {:?}", self.top_frame.locals)).expect("inspect display");
103    fmt::write(&mut output, format_args!("{indent}Stack({}): {:?}", self.stack.len(), self.stack)).expect("inspect display");
104    fmt::write(
105      &mut output,
106      format_args!(
107        "{indent}Sizes: {} + {}",
108        self.top_frame.initial_stack_size,
109        self.top_frame.ret_types.len()
110      ),
111    )
112    .expect("inspect display");
113    fmt::write(&mut output, format_args!("{indent}Pointer: {}", self.top_frame.pointer)).expect("inspect display");
114    output
115  }
116
117  pub fn run(&mut self, args: Vec<Calx>) -> Result<Calx, CalxError> {
118    // assign function parameters
119    self.top_frame.locals = args;
120    self.stack.clear();
121    loop {
122      // println!("Stack {:?}", self.stack);
123      // println!("-- op {} {:?}", self.stack.len(), instr);
124
125      if self.finished {
126        return Ok(self.return_value.to_owned());
127      }
128
129      let quick_continue = self.step()?;
130      if quick_continue {
131        continue;
132      }
133
134      self.top_frame.pointer += 1;
135    }
136  }
137
138  /// run one step, return true if continuing
139  #[inline(always)]
140  pub fn step(&mut self) -> Result<bool, CalxError> {
141    if self.top_frame.pointer >= self.top_frame.instrs.len() {
142      // println!("status {:?} {}", self.stack, self.top_frame);
143      self.check_func_return()?;
144      if self.frames.is_empty() {
145        let v = self.stack.pop().unwrap_or(Calx::Nil);
146        self.make_return(v);
147        return Ok(false);
148      } else {
149        // let prev_frame = self.top_frame;
150        self.top_frame = self.frames.pop().unwrap();
151      }
152      self.top_frame.pointer += 1;
153      return Ok(true);
154    }
155    let instrs = self.top_frame.instrs.to_owned();
156
157    use instr::CalxInstr::*;
158
159    match &instrs[self.top_frame.pointer] {
160      Jmp(line) => {
161        self.top_frame.pointer = line.to_owned();
162        return Ok(true); // point reset, goto next loop
163      }
164      JmpOffset(l) => {
165        self.top_frame.pointer = (self.top_frame.pointer as i32 + l) as usize;
166        return Ok(true); // point reset, goto next loop
167      }
168      JmpIf(line) => {
169        let v = self.stack_pop()?;
170        if v == Calx::Bool(true) || v == Calx::I64(1) {
171          self.top_frame.pointer = line.to_owned();
172          return Ok(true); // point reset, goto next loop
173        }
174      }
175      JmpOffsetIf(l) => {
176        let v = self.stack_pop()?;
177        if v == Calx::Bool(true) || v == Calx::I64(1) {
178          self.top_frame.pointer = (self.top_frame.pointer as i32 + l) as usize;
179          return Ok(true); // point reset, goto next loop
180        }
181      }
182      LocalSet(idx) => {
183        let v = self.stack_pop()?;
184        if *idx >= self.top_frame.locals.len() {
185          return Err(self.gen_err(format!("out of bound in local.set {} for {:?}", idx, self.top_frame.locals)));
186        } else {
187          self.top_frame.locals[*idx] = v
188        }
189      }
190      LocalTee(idx) => {
191        let v = self.stack_pop()?;
192        if *idx >= self.top_frame.locals.len() {
193          return Err(self.gen_err(format!("out of bound in local.tee {}", idx)));
194        } else {
195          self.top_frame.locals[*idx] = v.to_owned()
196        }
197        self.stack_push(v);
198      }
199      LocalGet(idx) => {
200        if *idx < self.top_frame.locals.len() {
201          self.stack_push(self.top_frame.locals[*idx].to_owned())
202        } else {
203          return Err(self.gen_err(format!("invalid index for local.get {}", idx)));
204        }
205      }
206      Return => {
207        // return values are moved to a temp space
208        let mut ret_stack: Vec<Calx> = vec![];
209
210        let ret_size = self.top_frame.ret_types.len();
211        for _ in 0..ret_size {
212          let v = self.stack_pop()?;
213          ret_stack.insert(0, v);
214        }
215
216        self.check_func_return()?;
217
218        if self.frames.is_empty() {
219          // top frame return, just return value
220          return match ret_stack.first() {
221            Some(x) => {
222              self.make_return(x.to_owned());
223              Ok(false)
224            }
225            None => Err(self.gen_err("return without value".to_owned())),
226          };
227        } else {
228          // let prev_frame = self.top_frame;
229          self.top_frame = self.frames.pop().unwrap();
230          // push return values back
231          for v in ret_stack {
232            self.stack_push(v);
233          }
234        }
235      }
236      LocalNew => self.top_frame.locals.push(Calx::Nil),
237      GlobalSet(idx) => {
238        let v = self.stack_pop()?;
239        if self.globals.len() >= *idx {
240          return Err(self.gen_err(format!("out of bound in global.set {}", idx)));
241        } else {
242          self.globals[*idx] = v
243        }
244      }
245      GlobalGet(idx) => {
246        if *idx < self.globals.len() {
247          self.stack_push(self.globals[*idx].to_owned())
248        } else {
249          return Err(self.gen_err(format!("invalid index for global.get {}", idx)));
250        }
251      }
252      GlobalNew => self.globals.push(Calx::Nil),
253      Const(v) => self.stack_push(v.to_owned()),
254      Dup => {
255        self.stack_push(self.stack[self.stack.len() - 1].to_owned());
256      }
257      Drop => {
258        let _ = self.stack_pop()?;
259      }
260      IntAdd => {
261        // reversed order
262        let v2 = self.stack_pop()?;
263        let last_idx = self.stack.len() - 1;
264        match (&(self.stack[last_idx]), &v2) {
265          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 + n2),
266          (_, _) => return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", self.stack[last_idx], v2))),
267        }
268      }
269      IntMul => {
270        // reversed order
271        let v2 = self.stack_pop()?;
272        let last_idx = self.stack.len() - 1;
273        match (&self.stack[last_idx], &v2) {
274          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 * n2),
275          (_, _) => return Err(self.gen_err(format!("expected 2 integers to multiply, {:?} {:?}", self.stack[last_idx], v2))),
276        }
277      }
278      IntDiv => {
279        // reversed order
280        let v2 = self.stack_pop()?;
281        let last_idx = self.stack.len() - 1;
282        match (&self.stack[last_idx], &v2) {
283          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 / n2),
284          (_, _) => return Err(self.gen_err(format!("expected 2 integers to divide, {:?} {:?}", self.stack[last_idx], v2))),
285        }
286      }
287      IntRem => {
288        // reversed order
289        let v2 = self.stack_pop()?;
290        let last_idx = self.stack.len() - 1;
291        match (&self.stack[last_idx], &v2) {
292          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64((*n1).rem(n2)),
293          (_, _) => return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", self.stack[last_idx], v2))),
294        }
295      }
296      IntNeg => {
297        let last_idx = self.stack.len() - 1;
298        if let Calx::I64(n) = self.stack[last_idx] {
299          self.stack[last_idx] = Calx::I64(-n)
300        } else {
301          return Err(self.gen_err(format!("expected int, got {}", self.stack[last_idx])));
302        }
303      }
304      IntShr => {
305        let bits = self.stack_pop()?;
306        let last_idx = self.stack.len() - 1;
307        match (&self.stack[last_idx], &bits) {
308          (Calx::I64(n), Calx::I64(b)) => self.stack[last_idx] = Calx::I64(n.checked_shr(*b as u32).unwrap()),
309          (_, _) => return Err(self.gen_err(format!("invalid number for SHR, {:?} {:?}", self.stack[last_idx], bits))),
310        }
311      }
312      IntShl => {
313        let bits = self.stack_pop()?;
314        let last_idx = self.stack.len() - 1;
315        match (&self.stack[last_idx], &bits) {
316          (Calx::I64(n), Calx::I64(b)) => self.stack[last_idx] = Calx::I64(n.checked_shl(*b as u32).unwrap()),
317          (_, _) => return Err(self.gen_err(format!("invalid number for SHL, {:?} {:?}", self.stack[last_idx], bits))),
318        }
319      }
320      IntEq => {
321        // reversed order
322        let v2 = self.stack_pop()?;
323        let last_idx = self.stack.len() - 1;
324
325        match (&self.stack[last_idx], &v2) {
326          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 == n2),
327          (_, _) => return Err(self.gen_err(format!("expected 2 integers to eq compare, {:?} {:?}", self.stack[last_idx], v2))),
328        }
329      }
330
331      IntNe => {
332        // reversed order
333        let v2 = self.stack_pop()?;
334        let last_idx = self.stack.len() - 1;
335        match (&self.stack[last_idx], &v2) {
336          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 != n2),
337          (_, _) => return Err(self.gen_err(format!("expected 2 integers to ne compare, {:?} {:?}", self.stack[last_idx], v2))),
338        }
339      }
340      IntLt => {
341        // reversed order
342        let v2 = self.stack_pop()?;
343        let last_idx = self.stack.len() - 1;
344        match (&self.stack[last_idx], &v2) {
345          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 < n2),
346          (_, _) => return Err(self.gen_err(format!("expected 2 integers to le compare, {:?} {:?}", self.stack[last_idx], v2))),
347        }
348      }
349      IntLe => {
350        // reversed order
351        let v2 = self.stack_pop()?;
352        let last_idx = self.stack.len() - 1;
353        match (&self.stack[last_idx], &v2) {
354          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 <= n2),
355          (_, _) => return Err(self.gen_err(format!("expected 2 integers to le compare, {:?} {:?}", self.stack[last_idx], v2))),
356        }
357      }
358      IntGt => {
359        // reversed order
360        let v2 = self.stack_pop()?;
361        let last_idx = self.stack.len() - 1;
362
363        match (&self.stack[last_idx], &v2) {
364          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 > n2),
365          (_, _) => return Err(self.gen_err(format!("expected 2 integers to gt compare, {:?} {:?}", self.stack[last_idx], v2))),
366        }
367      }
368      IntGe => {
369        // reversed order
370        let v2 = self.stack_pop()?;
371        let last_idx = self.stack.len() - 1;
372
373        match (&self.stack[last_idx], &v2) {
374          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 >= n2),
375          (_, _) => return Err(self.gen_err(format!("expected 2 integers to ge compare, {:?} {:?}", self.stack[last_idx], v2))),
376        }
377      }
378      Add => {
379        // reversed order
380        let v2 = self.stack_pop()?;
381        let last_idx = self.stack.len() - 1;
382
383        match (&self.stack[last_idx], &v2) {
384          (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 + n2),
385          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 + n2),
386          (_, _) => return Err(self.gen_err(format!("expected 2 numbers to +, {:?} {:?}", self.stack[last_idx], v2))),
387        }
388      }
389      Mul => {
390        // reversed order
391        let v2 = self.stack_pop()?;
392        let last_idx = self.stack.len() - 1;
393
394        match (&self.stack[last_idx], &v2) {
395          (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 * n2),
396          (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 * n2),
397          (_, _) => return Err(self.gen_err(format!("expected 2 numbers to multiply, {:?} {:?}", self.stack[last_idx], v2))),
398        }
399      }
400      Div => {
401        // reversed order
402        let v2 = self.stack_pop()?;
403        let last_idx = self.stack.len() - 1;
404
405        match (&self.stack[last_idx], &v2) {
406          (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 / n2),
407          (_, _) => return Err(self.gen_err(format!("expected 2 numbers to divide, {:?} {:?}", self.stack[last_idx], v2))),
408        }
409      }
410      Neg => {
411        let last_idx = self.stack.len() - 1;
412        if let Calx::F64(n) = self.stack[last_idx] {
413          self.stack[last_idx] = Calx::F64(-n)
414        } else {
415          return Err(self.gen_err(format!("expected float, got {}", self.stack[last_idx])));
416        }
417      }
418      NewList => {
419        todo!()
420      }
421      ListGet => {
422        todo!()
423      }
424      ListSet => {
425        todo!()
426      }
427      NewLink => {
428        todo!()
429      }
430      And => {
431        todo!()
432      }
433      Or => {
434        todo!()
435      }
436      Not => {
437        todo!()
438      }
439      Call(f_name) => {
440        // println!("frame size: {}", self.frames.len());
441        match self.find_func(f_name) {
442          Some(f) => {
443            let instrs = f.instrs.to_owned();
444            let ret_types = f.ret_types.to_owned();
445            let f_name = f.name.to_owned();
446            let mut locals: Vec<Calx> = vec![];
447            for _ in 0..f.params_types.len() {
448              let v = self.stack_pop()?;
449              locals.insert(0, v);
450            }
451            self.frames.push(self.top_frame.to_owned());
452            self.top_frame = CalxFrame {
453              name: f_name,
454              initial_stack_size: self.stack.len(),
455              locals,
456              pointer: 0,
457              instrs: match instrs {
458                Some(x) => x.to_owned(),
459                None => unreachable!("function must have instrs"),
460              },
461              ret_types,
462            };
463
464            // start in new frame
465            return Ok(true);
466          }
467          None => return Err(self.gen_err(format!("cannot find function named: {}", f_name))),
468        }
469      }
470      ReturnCall(f_name) => {
471        // println!("frame size: {}", self.frames.len());
472        match self.find_func(f_name) {
473          Some(f) => {
474            // println!("examine stack: {:?}", self.stack);
475            let instrs = f.instrs.to_owned();
476            let ret_types = f.ret_types.to_owned();
477            let f_name = f.name.to_owned();
478            let mut locals: Vec<Calx> = vec![];
479            for _ in 0..f.params_types.len() {
480              let v = self.stack_pop()?;
481              locals.insert(0, v);
482            }
483            let prev_frame = &self.top_frame;
484            if prev_frame.initial_stack_size != self.stack.len() {
485              return Err(self.gen_err(format!(
486                "expected constant initial stack size: {}, got: {}",
487                prev_frame.initial_stack_size,
488                self.stack.len()
489              )));
490            }
491            self.top_frame = CalxFrame {
492              name: f_name,
493              initial_stack_size: self.stack.len(),
494              locals,
495              pointer: 0,
496              instrs: match instrs {
497                Some(x) => x.to_owned(),
498                None => panic!("function must have instrs"),
499              },
500              ret_types,
501            };
502
503            // start in new frame
504            return Ok(true);
505          }
506          None => return Err(self.gen_err(format!("cannot find function named: {}", f_name))),
507        }
508      }
509      CallImport(f_name) => match self.imports.to_owned().get(f_name) {
510        None => return Err(self.gen_err(format!("missing imported function {}", f_name))),
511        Some((f, size)) => {
512          if self.stack.len() < *size {
513            return Err(self.gen_err(format!(
514              "imported function {} expected {} arguemtns, found {} on stack",
515              f_name,
516              size,
517              self.stack.len()
518            )));
519          }
520          let mut args: Vec<Calx> = vec![];
521          for _ in 0..*size {
522            let item = self.stack_pop()?;
523            args.insert(0, item);
524          }
525          let v = f(args.to_owned())?;
526          self.stack_push(v);
527        }
528      },
529      Unreachable => {
530        unreachable!("Unexpected from op")
531      }
532      Nop => {
533        // Noop
534      }
535      Quit(code) => std::process::exit(*code as i32),
536      Echo => {
537        let v = self.stack_pop()?;
538        println!("{}", v);
539      }
540      Assert(message) => {
541        let v = self.stack_pop()?;
542        if v == Calx::Bool(true) || v == Calx::I64(1) {
543          // Ok
544        } else {
545          return Err(self.gen_err(format!("Failed assertion: {}", message)));
546        }
547      }
548      Inspect => {
549        println!("[ ----------------{}", self.inspect_display(2));
550        println!("  -------------- ]");
551      }
552    }
553
554    Ok(false)
555  }
556
557  pub fn preprocess(&mut self, verbose: bool) -> Result<(), String> {
558    for i in 0..self.funcs.len() {
559      let mut stack_size = 0;
560      let mut ops: Vec<CalxInstr> = vec![];
561      let mut blocks_track = BlockStack::new();
562
563      let f = &self.funcs[i];
564
565      if verbose {
566        println!(
567          "\nFUNC {}\n  initial stack size: {}\n  ret_size {}",
568          f.name,
569          stack_size,
570          f.ret_types.len()
571        );
572      }
573
574      for j in 0..self.funcs[i].syntax.len() {
575        if verbose {
576          println!("{} * {:?}", stack_size, self.funcs[i].syntax[j].to_owned());
577        }
578        let syntax = &self.funcs[i].syntax;
579        match &syntax[j] {
580          CalxSyntax::Block {
581            looped,
582            params_types,
583            ret_types,
584            from,
585            to,
586          } => {
587            if stack_size < params_types.len() {
588              return Err(format!("insufficient params {} for block: {:?}", stack_size, params_types));
589            }
590            if *looped {
591              blocks_track.push(BlockData::Loop {
592                params_types: params_types.to_owned(),
593                ret_types: ret_types.to_owned(),
594                from: from.to_owned(),
595                to: to.to_owned(),
596                initial_stack_size: stack_size,
597              });
598            } else {
599              blocks_track.push(BlockData::Block {
600                params_types: params_types.to_owned(),
601                ret_types: ret_types.to_owned(),
602                to: to.to_owned(),
603                initial_stack_size: stack_size,
604              });
605            }
606            ops.push(CalxInstr::Nop);
607          }
608          CalxSyntax::Br(size) => {
609            if *size > blocks_track.len() {
610              return Err(format!("br {} too large", size));
611            }
612
613            let target_block = blocks_track.peek_block_level(*size)?;
614            let expected_size = target_block.expected_finish_size();
615            if stack_size != expected_size {
616              return Err(format!("br({size}) expected size {expected_size}, got {stack_size}"));
617            }
618
619            match target_block {
620              BlockData::Loop { from, .. } => ops.push(CalxInstr::Jmp(from.to_owned())),
621              BlockData::Block { to, .. } => ops.push(CalxInstr::Jmp(to.to_owned())),
622              _ => unreachable!("br target must be block or loop"),
623            }
624          }
625          CalxSyntax::BrIf(size) => {
626            if blocks_track.is_empty() {
627              return Err(format!("cannot branch with no blocks, {}", size));
628            }
629            if *size > blocks_track.len() {
630              return Err(format!("br {} too large", size));
631            }
632
633            let target_block = blocks_track.peek_block_level(*size)?;
634
635            match target_block {
636              BlockData::Loop { from, .. } => ops.push(CalxInstr::JmpIf(from.to_owned())),
637              BlockData::Block { to, .. } => ops.push(CalxInstr::JmpIf(to.to_owned())),
638              _ => unreachable!("br target must be block or loop"),
639            }
640            stack_size -= 1;
641
642            let expected_size = target_block.expected_finish_size();
643            if stack_size != expected_size {
644              return Err(format!("brIf({size}) expected size {expected_size}, got {stack_size}"));
645            }
646          }
647          CalxSyntax::BlockEnd(looped) => {
648            // println!("checking: {:?}", blocks_track);
649            if blocks_track.is_empty() {
650              return Err(format!("invalid block end, {:?}", blocks_track));
651            }
652
653            let prev_block = blocks_track.pop_block()?;
654            if *looped {
655              // nothing, branched during runtime
656            } else if stack_size != prev_block.expected_finish_size() {
657              return Err(format!("size mismatch for block end: {} {:?}", stack_size, prev_block));
658            }
659
660            ops.push(CalxInstr::Nop)
661          }
662          CalxSyntax::Call(f_name) => match self.find_func(f_name) {
663            Some(f) => {
664              if stack_size < f.params_types.len() {
665                return Err(format!("insufficient size to call: {} {:?}", stack_size, f.params_types));
666              }
667              stack_size = stack_size - f.params_types.len() + f.ret_types.len();
668              ops.push(CalxInstr::Call(f_name.to_owned()))
669            }
670            None => return Err(format!("cannot find function named: {}", f_name)),
671          },
672          CalxSyntax::ReturnCall(f_name) => match self.find_func(f_name) {
673            Some(f) => {
674              if stack_size < f.params_types.len() {
675                return Err(format!("insufficient size to call: {} {:?}", stack_size, f.params_types));
676              }
677              stack_size = stack_size - f.params_types.len() + f.ret_types.len();
678              ops.push(CalxInstr::ReturnCall(f_name.to_owned()))
679            }
680            None => return Err(format!("cannot find function named: {}", f_name)),
681          },
682          CalxSyntax::CallImport(f_name) => match &self.imports.get(f_name) {
683            Some((_f, size)) => {
684              if stack_size < *size {
685                return Err(format!("insufficient size to call import: {} {:?}", stack_size, size));
686              }
687              stack_size = stack_size - size + 1;
688              ops.push(CalxInstr::CallImport(f_name.to_owned()))
689            }
690            None => return Err(format!("missing imported function {}", f_name)),
691          },
692          CalxSyntax::Return => {
693            let ret_size = self.funcs[i].ret_types.len();
694            stack_size -= ret_size;
695            if stack_size != 0 {
696              return Err(format!(
697                "invalid return size {} for {:?} in {}",
698                stack_size, self.funcs[i].ret_types, self.funcs[i].name
699              ));
700            }
701            ops.push(CalxInstr::Return);
702          }
703          CalxSyntax::If { ret_types, else_at, to } => {
704            if stack_size < 1 {
705              return Err(format!("insufficient stack {} to branch", stack_size));
706            }
707
708            blocks_track.push(BlockData::If {
709              ret_types: ret_types.to_owned(),
710              else_to: else_at.to_owned(),
711              to: to.to_owned(),
712              initial_stack_size: stack_size,
713            });
714
715            stack_size -= 1;
716            ops.push(CalxInstr::JmpIf(else_at.to_owned()));
717          }
718          CalxSyntax::ElseEnd => {
719            if blocks_track.is_empty() {
720              return Err(format!("invalid else end, {:?}", blocks_track));
721            }
722
723            let prev_block = blocks_track.peek_if()?;
724
725            if stack_size != prev_block.expected_finish_size() {
726              return Err(format!("size mismatch for else-end: {} {:?}", stack_size, prev_block));
727            }
728
729            match prev_block {
730              BlockData::If { to, .. } => ops.push(CalxInstr::Jmp(to.to_owned())),
731              _ => unreachable!("end inside if"),
732            }
733          }
734          CalxSyntax::ThenEnd => {
735            if blocks_track.is_empty() {
736              return Err(format!("invalid else end, {:?}", blocks_track));
737            }
738
739            let prev_block = blocks_track.pop_if()?;
740            if stack_size != prev_block.expected_finish_size() {
741              return Err(format!("size mismatch for then-end: {} {:?}", stack_size, prev_block));
742            }
743
744            match prev_block {
745              BlockData::If { to, .. } => ops.push(CalxInstr::Jmp(to.to_owned())),
746              _ => unreachable!("end inside if"),
747            }
748          }
749          a => {
750            let instr: CalxInstr = a.try_into()?;
751            // checks
752            let (params_size, ret_size) = instr.stack_arity();
753            if stack_size < params_size {
754              return Err(format!("insufficient stack {} to call {:?} of {}", stack_size, a, params_size));
755            }
756            stack_size = stack_size - params_size + ret_size;
757            // println!(
758            //   "  sizes: {:?} {} {} -> {}",
759            //   a, params_size, ret_size, stack_size
760            // );
761            ops.push(instr.to_owned());
762          }
763        }
764      }
765      if stack_size != 0 {
766        return Err(format!(
767          "invalid final size {} of {:?} in {}",
768          stack_size, self.funcs[i].ret_types, self.funcs[i].name
769        ));
770      }
771
772      self.funcs[i].instrs = Some(Rc::new(ops));
773    }
774
775    Ok(())
776  }
777
778  #[inline(always)]
779  fn check_func_return(&self) -> Result<(), CalxError> {
780    if self.stack.len() != self.top_frame.initial_stack_size {
781      return Err(self.gen_err(format!(
782        "stack size {} does not fit initial size {} plus {:?}",
783        self.stack.len(),
784        self.top_frame.initial_stack_size,
785        self.top_frame.ret_types
786      )));
787    }
788
789    Ok(())
790  }
791
792  #[inline(always)]
793  fn stack_pop(&mut self) -> Result<Calx, CalxError> {
794    if self.stack.is_empty() {
795      Err(self.gen_err(String::from("cannot pop from empty stack")))
796    } else if self.stack.len() <= self.top_frame.initial_stack_size {
797      Err(self.gen_err(String::from("cannot pop from parent stack")))
798    } else {
799      let v = self.stack.pop().unwrap();
800      Ok(v)
801    }
802  }
803
804  #[inline(always)]
805  fn stack_push(&mut self, x: Calx) {
806    self.stack.push(x)
807  }
808
809  fn gen_err(&self, s: String) -> CalxError {
810    CalxError {
811      message: s,
812      top_frame: self.top_frame.to_owned(),
813      stack: self.stack.to_owned(),
814      globals: self.globals.to_owned(),
815    }
816  }
817
818  fn find_func(&self, name: &str) -> Option<&CalxFunc> {
819    self.funcs.iter().find(|x| *x.name == name)
820  }
821}
822
823#[derive(Debug, Clone, PartialEq, PartialOrd)]
824pub struct CalxError {
825  pub message: String,
826  pub stack: Vec<Calx>,
827  pub top_frame: CalxFrame,
828  pub globals: Vec<Calx>,
829}
830
831impl fmt::Display for CalxError {
832  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
833    write!(f, "{}\n{:?}\n{}", self.message, self.stack, self.top_frame)
834  }
835}
836
837impl CalxError {
838  pub fn new_raw(s: String) -> Self {
839    CalxError {
840      message: s,
841      stack: vec![],
842      top_frame: CalxFrame::default(),
843      globals: vec![],
844    }
845  }
846}