Skip to main content

mech_interpreter/
interpreter.rs

1use crate::*;
2use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3use std::collections::HashMap;
4use std::io::{Cursor, Read, Write};
5use std::panic::{catch_unwind, AssertUnwindSafe};
6use std::rc::Rc;
7use std::time::Duration;
8use std::time::Instant;
9
10// Interpreter
11// ----------------------------------------------------------------------------
12
13pub struct Interpreter {
14  pub id: u64,
15  pub profile: bool,
16  pub max_steps: usize,
17  #[cfg(feature = "trace")]
18  pub trace: bool,
19  #[cfg(feature = "trace")]
20  pub trace_to_stdout: bool,
21  #[cfg(feature = "trace")]
22  pub trace_events: Ref<Vec<TraceEvent>>,
23  ip: usize, // instruction pointer
24  pub state: Ref<ProgramState>,
25  #[cfg(feature = "functions")]
26  pub stack: Vec<Frame>,
27  registers: Vec<Value>,
28  constants: Vec<Value>,
29  #[cfg(feature = "compiler")]
30  pub context: Option<CompileCtx>,
31  pub code: Vec<MechSourceCode>,
32  pub out: Value,
33  pub out_values: Ref<HashMap<u64, Value>>,
34  #[cfg(feature = "state_machines")]
35  pub user_state_machines: Ref<HashMap<u64, FsmImplementation>>,
36  pub sub_interpreters: Ref<HashMap<u64, Box<Interpreter>>>,
37}
38
39impl Clone for Interpreter {
40  fn clone(&self) -> Self {
41    Self {
42      id: self.id,
43      ip: self.ip,
44      profile: false,
45      max_steps: self.max_steps,
46      #[cfg(feature = "trace")]
47      trace: self.trace,
48      #[cfg(feature = "trace")]
49      trace_to_stdout: self.trace_to_stdout,
50      #[cfg(feature = "trace")]
51      trace_events: self.trace_events.clone(),
52      state: Ref::new(self.state.borrow().clone()),
53      #[cfg(feature = "functions")]
54      stack: self.stack.clone(),
55      registers: self.registers.clone(),
56      constants: self.constants.clone(),
57      #[cfg(feature = "compiler")]
58      context: None,
59      code: self.code.clone(),
60      out: self.out.clone(),
61      out_values: self.out_values.clone(),
62      #[cfg(feature = "state_machines")]
63      user_state_machines: self.user_state_machines.clone(),
64      sub_interpreters: self.sub_interpreters.clone(),
65    }
66  }
67}
68
69impl Interpreter {
70  pub fn new(id: u64) -> Self {
71    let mut state = ProgramState::new();
72    load_stdkinds(&mut state.kinds);
73    #[cfg(feature = "functions")]
74    load_stdlib(&mut state.functions.borrow_mut());
75    Self {
76      id,
77      ip: 0,
78      profile: false,
79      max_steps: 10_00000, // Default maximum steps
80      #[cfg(feature = "trace")]
81      trace: false,
82      #[cfg(feature = "trace")]
83      trace_to_stdout: true,
84      #[cfg(feature = "trace")]
85      trace_events: Ref::new(Vec::new()),
86      state: Ref::new(state),
87      #[cfg(feature = "functions")]
88      stack: Vec::new(),
89      registers: Vec::new(),
90      constants: Vec::new(),
91      out: Value::Empty,
92      sub_interpreters: Ref::new(HashMap::new()),
93      out_values: Ref::new(HashMap::new()),
94      #[cfg(feature = "state_machines")]
95      user_state_machines: Ref::new(HashMap::new()),
96      code: Vec::new(),
97      #[cfg(feature = "compiler")]
98      context: None,
99    }
100  }
101
102  #[cfg(feature = "symbol_table")]
103  pub fn set_environment(&mut self, env: SymbolTableRef) {
104    self.state.borrow_mut().environment = Some(env);
105  }
106
107  #[cfg(feature = "functions")]
108  pub fn clear_plan(&mut self) {
109    self.state.borrow_mut().plan.borrow_mut().clear();
110  }
111
112  #[cfg(feature = "pretty_print")]
113  pub fn pretty_print(&self) -> String {
114    let mut output = String::new();
115    output.push_str(&format!("Interpreter ID: {}\n", self.id));
116    // print state
117    output.push_str(&self.state.borrow().pretty_print());
118
119    output.push_str("Registers:\n");
120    for (i, reg) in self.registers.iter().enumerate() {
121      output.push_str(&format!("  R{}: {}\n", i, reg));
122    }
123    output.push_str("Constants:\n");
124    for (i, constant) in self.constants.iter().enumerate() {
125      output.push_str(&format!("  C{}: {}\n", i, constant));
126    }
127    output.push_str(&format!("Output Value: {}\n", self.out));
128    output.push_str(&format!(
129      "Number of Sub-Interpreters: {}\n",
130      self.sub_interpreters.borrow().len()
131    ));
132    output.push_str("Output Values:\n");
133    for (key, value) in self.out_values.borrow().iter() {
134      output.push_str(&format!("  {}: {}\n", key, value));
135    }
136    output.push_str(&format!("Code Length: {}\n", self.code.len()));
137    #[cfg(feature = "compiler")]
138    if let Some(context) = &self.context {
139      output.push_str("Context: Exists\n");
140    } else {
141      output.push_str("Context: None\n");
142    }
143    output
144  }
145
146  pub fn clear(&mut self) {
147    let id = self.id;
148    *self = Interpreter::new(id);
149  }
150
151  pub fn set_trace_enabled(&mut self, enabled: bool) {
152    #[cfg(feature = "trace")]
153    {
154      self.trace = enabled;
155    }
156    #[cfg(not(feature = "trace"))]
157    {
158      let _ = enabled;
159    }
160  }
161
162  #[cfg(feature = "trace")]
163  pub fn set_trace_to_stdout(&mut self, enabled: bool) {
164    self.trace_to_stdout = enabled;
165  }
166
167  #[cfg(feature = "trace")]
168  pub fn clear_trace_events(&self) {
169    self.trace_events.borrow_mut().clear();
170  }
171
172  #[cfg(feature = "trace")]
173  pub fn trace_events(&self) -> Vec<TraceEvent> {
174    self.trace_events.borrow().clone()
175  }
176
177  #[cfg(feature = "trace")]
178  pub fn trace_events_to_json(&self) -> String {
179    let trace_events = self.trace_events.borrow();
180    trace_events_to_json(trace_events.as_slice())
181  }
182
183  #[cfg(feature = "trace")]
184  pub fn push_trace_line(&self, rendered: String) {
185    let (channel, label, message) = parse_trace_line(&rendered);
186    let mut trace_events = self.trace_events.borrow_mut();
187    let index = trace_events.len();
188    trace_events.push(TraceEvent {
189        index,
190        channel,
191        label,
192        message,
193        rendered,
194    });
195  }
196
197  #[cfg(all(feature = "trace", feature = "state_machines"))]
198  pub fn formatted_fsm_trace(&self) -> String {
199    format_fsm_trace_report(&self.trace_events())
200  }
201
202  #[cfg(feature = "pretty_print")]
203  pub fn pretty_print_symbols(&self) -> String {
204    let state_brrw = self.state.borrow();
205    let syms = state_brrw.symbol_table.borrow();
206    syms.pretty_print()
207  }
208
209  #[cfg(feature = "pretty_print")]
210  pub fn pretty_print_plan(&self) -> String {
211    let state_brrw = self.state.borrow();
212    let plan = state_brrw.plan.borrow();
213    let mut result = String::new();
214    for (i, step) in plan.iter().enumerate() {
215      result.push_str(&format!("Step {}:\n", i));
216      result.push_str(&format!("{}\n", step.to_string()));
217    }
218    result
219  }
220
221  #[cfg(feature = "functions")]
222  pub fn plan(&self) -> Plan {
223    self.state.borrow().plan.clone()
224  }
225
226  #[cfg(feature = "symbol_table")]
227  pub fn symbols(&self) -> SymbolTableRef {
228    self.state.borrow().symbol_table.clone()
229  }
230
231  pub fn dictionary(&self) -> Ref<Dictionary> {
232    self.state.borrow().dictionary.clone()
233  }
234
235  #[cfg(feature = "functions")]
236  pub fn functions(&self) -> FunctionsRef {
237    self.state.borrow().functions.clone()
238  }
239
240  #[cfg(feature = "functions")]
241  pub fn set_functions(&mut self, functions: FunctionsRef) {
242    self.state.borrow_mut().functions = functions;
243  }
244
245  #[cfg(feature = "functions")]
246  pub fn step(&mut self, step_id: usize, step_count: u64) -> MResult<Value> {
247    let state_brrw = self.state.borrow();
248    let mut plan_brrw = state_brrw.plan.borrow_mut(); // RefMut<Vec<Box<dyn MechFunction>>>
249
250    if plan_brrw.is_empty() {
251      return Err(MechError::new(NoStepsInPlanError, None).with_compiler_loc());
252    }
253
254    let len = plan_brrw.len();
255
256    // Case 1: step_id == 0, run entire plan step_count times
257    if step_id == 0 {
258      if self.profile {
259        // Initialize total durations per step
260        let mut total_durations = vec![Duration::ZERO; len];
261        for _ in 0..step_count {
262          for (idx, fxn) in plan_brrw.iter_mut().enumerate() {
263            let start = Instant::now();
264            fxn.solve();
265            total_durations[idx] += start.elapsed();
266          }
267        }
268        // Print histogram if profiling is enabled
269        if self.profile {
270          println!("\nStep timing summary and histogram:");
271          print_histogram(&total_durations);
272        }
273        return Ok(plan_brrw[len - 1].out().clone());
274      } else {
275        for _ in 0..step_count {
276          for (idx, fxn) in plan_brrw.iter_mut().enumerate() {
277            trace_println!(self, "{}", {
278              let fxn_header = fxn
279                .to_string()
280                .lines()
281                .next()
282                .unwrap_or("<unknown-step>")
283                .to_string();
284              format!("[trace][plan] step[{idx}] {fxn_header}")
285            });
286            fxn.solve();
287            trace_println!(self, "{}", {
288              let output = fxn.out().to_string();
289              let output = if output.chars().count() > 96 {
290                  format!("{}…", output.chars().take(96).collect::<String>())
291              } else {
292                  output
293              };
294              format!("[trace][plan] step[{idx}] out={output}")
295            });
296          }
297        }
298        return Ok(plan_brrw[len - 1].out().clone());
299      }
300    }
301
302    // Case 2: step a single function by index
303    let idx = step_id as usize;
304    if idx > len {
305      return Err(MechError::new(
306        StepIndexOutOfBoundsError {
307            step_id,
308            plan_length: len,
309        },
310        None,
311      )
312      .with_compiler_loc());
313    }
314
315    let fxn = &mut plan_brrw[idx - 1];
316
317    let fxn_str = fxn.to_string();
318    if fxn_str.lines().count() > 30 {
319      let lines: Vec<&str> = fxn_str.lines().collect();
320      println!("Stepping function:");
321      for line in &lines[0..10] {
322        println!("{}", line);
323      }
324      println!("...");
325      for line in &lines[lines.len() - 10..] {
326        println!("{}", line);
327      }
328    } else {
329      println!("Stepping function:\n{}", fxn_str);
330    }
331
332    for _ in 0..step_count {
333      fxn.solve();
334    }
335
336    Ok(fxn.out().clone())
337  }
338
339  #[cfg(feature = "functions")]
340  pub fn interpret(&mut self, tree: &Program) -> MResult<Value> {
341    self.code.push(MechSourceCode::Tree(tree.clone()));
342    catch_unwind(AssertUnwindSafe(|| {
343      let result = program(tree, &self);
344      match self.state.borrow().plan.borrow().last() {
345        Some(last_step) => self.out = last_step.out().clone(),
346        None => self.out = Value::Empty,
347      }
348      result
349    }))
350    .map_err(|err| {
351      match err.downcast_ref::<&'static str>() {
352         Some(raw_msg) => {
353          if raw_msg.contains("Index out of bounds") {
354              MechError::new(IndexOutOfBoundsError, None).with_compiler_loc()
355          } else if raw_msg.contains("attempt to subtract with overflow") {
356              MechError::new(OverflowSubtractionError, None).with_compiler_loc()
357          } else {
358            MechError::new(
359              UnknownPanicError {
360                details: raw_msg.to_string(),
361              },
362              None,
363            )
364            .with_compiler_loc()
365          }
366        } 
367        None => {
368          MechError::new(
369            UnknownPanicError {
370              details: "Non-string panic".to_string(),
371            },
372            None,
373          )
374          .with_compiler_loc()
375        }
376      }
377    })?
378  }
379    
380
381  #[cfg(feature = "program")]
382  pub fn run_program(&mut self, program: &ParsedProgram) -> MResult<Value> {
383    // Reset the instruction pointer
384    self.ip = 0;
385    // Resize the registers and constant table
386    self.registers = vec![Value::Empty; program.header.reg_count as usize];
387    self.constants = vec![Value::Empty; program.const_entries.len()];
388    // Load the constants
389    self.constants = program.decode_const_entries()?;
390    // Load the symbol table
391    {
392      let mut state_brrw = self.state.borrow_mut();
393      let mut symbol_table = state_brrw.symbol_table.borrow_mut();
394      for (id, reg) in program.symbols.iter() {
395        let constant = self.constants[*reg as usize].clone();
396        self.out = constant.clone();
397        let mutable = program.mutable_symbols.contains(id);
398        symbol_table.insert(*id, constant, mutable);
399      }
400    }
401    // Load the instructions
402    {
403      let state_brrw = self.state.borrow();
404      let functions_table = state_brrw.functions.borrow();
405      while self.ip < program.instrs.len() {
406        let instr = &program.instrs[self.ip];
407        match instr {
408          DecodedInstr::ConstLoad { dst, const_id } => {
409            let value = self.constants[*const_id as usize].clone();
410            self.registers[*dst as usize] = value;
411          }
412          DecodedInstr::NullOp { fxn_id, dst } => {
413            match functions_table.functions.get(fxn_id) {
414              Some(fxn_factory) => {
415                let out = &self.registers[*dst as usize];
416                let fxn = fxn_factory(FunctionArgs::Nullary(out.clone()))?;
417                self.out = fxn.out().clone();
418                state_brrw.add_plan_step(fxn);
419              }
420              None => {
421                return Err(MechError::new(
422                  UnknownNullaryFunctionError { fxn_id: *fxn_id },
423                  None,
424                )
425                .with_compiler_loc());
426              }
427            }
428          }
429          DecodedInstr::UnOp { fxn_id, dst, src } => {
430            match functions_table.functions.get(fxn_id) {
431              Some(fxn_factory) => {
432                let src = &self.registers[*src as usize];
433                let out = &self.registers[*dst as usize];
434                let fxn =
435                    fxn_factory(FunctionArgs::Unary(out.clone(), src.clone()))?;
436                self.out = fxn.out().clone();
437                state_brrw.add_plan_step(fxn);
438              }
439              None => {
440                return Err(MechError::new(
441                  UnknownUnaryFunctionError { fxn_id: *fxn_id },
442                  None,
443                )
444                .with_compiler_loc());
445              }
446            }
447          }
448          DecodedInstr::BinOp { fxn_id, dst, lhs, rhs } => 
449          match functions_table.functions.get(fxn_id) {
450            Some(fxn_factory) => {
451              let lhs = &self.registers[*lhs as usize];
452              let rhs = &self.registers[*rhs as usize];
453              let out = &self.registers[*dst as usize];
454              let fxn = fxn_factory(FunctionArgs::Binary(out.clone(),lhs.clone(),rhs.clone()))?;
455              self.out = fxn.out().clone();
456              state_brrw.add_plan_step(fxn);
457            }
458            None => {
459              return Err(MechError::new(
460                UnknownBinaryFunctionError { fxn_id: *fxn_id },
461                None,
462              )
463              .with_compiler_loc());
464            }
465          },
466          DecodedInstr::TernOp {fxn_id,dst,a,b,c} => 
467          match functions_table.functions.get(fxn_id) {
468            Some(fxn_factory) => {
469              let arg1 = &self.registers[*a as usize];
470              let arg2 = &self.registers[*b as usize];
471              let arg3 = &self.registers[*c as usize];
472              let out = &self.registers[*dst as usize];
473              let fxn = fxn_factory(FunctionArgs::Ternary(
474                out.clone(),
475                arg1.clone(),
476                arg2.clone(),
477                arg3.clone(),
478              ))?;
479              self.out = fxn.out().clone();
480              state_brrw.add_plan_step(fxn);
481            }
482            None => {
483              return Err(MechError::new(
484                UnknownTernaryFunctionError { fxn_id: *fxn_id },
485                None,
486              )
487              .with_compiler_loc());
488            }
489          },
490          DecodedInstr::QuadOp {fxn_id,dst,a,b,c,d } => 
491            match functions_table.functions.get(fxn_id) {
492              Some(fxn_factory) => {
493                let arg1 = &self.registers[*a as usize];
494                let arg2 = &self.registers[*b as usize];
495                let arg3 = &self.registers[*c as usize];
496                let arg4 = &self.registers[*d as usize];
497                let out = &self.registers[*dst as usize];
498                let fxn = fxn_factory(FunctionArgs::Quaternary(
499                    out.clone(),
500                    arg1.clone(),
501                    arg2.clone(),
502                    arg3.clone(),
503                    arg4.clone(),
504                ))?;
505                self.out = fxn.out().clone();
506                state_brrw.add_plan_step(fxn);
507              }
508              None => {
509                return Err(MechError::new(
510                  UnknownQuadFunctionError { fxn_id: *fxn_id },
511                  None,
512                )
513                .with_compiler_loc());
514              }
515          },
516          DecodedInstr::VarArg { fxn_id, dst, args } => {
517            match functions_table.functions.get(fxn_id) {
518              Some(fxn_factory) => {
519                let arg_values: Vec<Value> = args
520                  .iter()
521                  .map(|r| self.registers[*r as usize].clone())
522                  .collect();
523                let out = &self.registers[*dst as usize];
524                let fxn = fxn_factory(FunctionArgs::Variadic(out.clone(), arg_values))?;
525                self.out = fxn.out().clone();
526                state_brrw.add_plan_step(fxn);
527              }
528              None => {
529                return Err(MechError::new(
530                  UnknownVariadicFunctionError { fxn_id: *fxn_id },
531                  None,
532                )
533                .with_compiler_loc());
534              }
535            }
536          }
537          DecodedInstr::Ret { src } => {
538            todo!();
539          }
540          x => {
541            return Err(MechError::new(
542              UnknownInstructionError {
543                instr: format!("{:?}", x),
544              },
545              None,
546            )
547            .with_compiler_loc());
548          }
549        }
550        self.ip += 1;
551      }
552    }
553    // Load the dictionary
554    {
555      let mut state_brrw = self.state.borrow_mut();
556      let mut symbol_table = state_brrw.symbol_table.borrow_mut();
557      for (id, name) in &program.dictionary {
558        symbol_table
559            .dictionary
560            .borrow_mut()
561            .insert(*id, name.clone());
562        state_brrw.dictionary.borrow_mut().insert(*id, name.clone());
563      }
564    }
565    Ok(self.out.clone())
566  }
567
568  #[cfg(feature = "compiler")]
569  pub fn compile(&mut self) -> MResult<Vec<u8>> {
570    let state_brrw = self.state.borrow();
571    let mut plan_brrw = state_brrw.plan.borrow_mut();
572    let mut ctx = CompileCtx::new();
573    for step in plan_brrw.iter() {
574        step.compile(&mut ctx)?;
575    }
576    let bytes = ctx.compile()?;
577    self.context = Some(ctx);
578    Ok(bytes)
579  }
580}
581
582// Interpreter Errors
583// ----------------------------------------------------------------------------
584
585#[derive(Debug, Clone)]
586pub struct UnknownInstructionError {
587  pub instr: String,
588}
589impl MechErrorKind for UnknownInstructionError {
590  fn name(&self) -> &str {
591    "UnknownInstruction"
592  }
593
594  fn message(&self) -> String {
595    format!("Unknown instruction: {}", self.instr)
596  }
597}
598
599#[derive(Debug, Clone)]
600pub struct UnknownVariadicFunctionError {
601  pub fxn_id: u64,
602}
603
604impl MechErrorKind for UnknownVariadicFunctionError {
605  fn name(&self) -> &str {
606    "UnknownVariadicFunction"
607  }
608  fn message(&self) -> String {
609    format!("Unknown variadic function ID: {}", self.fxn_id)
610  }
611}
612
613#[derive(Debug, Clone)]
614pub struct UnknownQuadFunctionError {
615  pub fxn_id: u64,
616}
617impl MechErrorKind for UnknownQuadFunctionError {
618  fn name(&self) -> &str {
619    "UnknownQuadFunction"
620  }
621  fn message(&self) -> String {
622    format!("Unknown quad function ID: {}", self.fxn_id)
623  }
624}
625
626#[derive(Debug, Clone)]
627pub struct UnknownTernaryFunctionError {
628  pub fxn_id: u64,
629}
630impl MechErrorKind for UnknownTernaryFunctionError {
631  fn name(&self) -> &str {
632    "UnknownTernaryFunction"
633  }
634  fn message(&self) -> String {
635    format!("Unknown ternary function ID: {}", self.fxn_id)
636  }
637}
638
639#[derive(Debug, Clone)]
640pub struct UnknownBinaryFunctionError {
641  pub fxn_id: u64,
642}
643impl MechErrorKind for UnknownBinaryFunctionError {
644  fn name(&self) -> &str {
645    "UnknownBinaryFunction"
646  }
647  fn message(&self) -> String {
648    format!("Unknown binary function ID: {}", self.fxn_id)
649  }
650}
651
652#[derive(Debug, Clone)]
653pub struct UnknownUnaryFunctionError {
654  pub fxn_id: u64,
655}
656impl MechErrorKind for UnknownUnaryFunctionError {
657  fn name(&self) -> &str {
658    "UnknownUnaryFunction"
659  }
660  fn message(&self) -> String {
661    format!("Unknown unary function ID: {}", self.fxn_id)
662  }
663}
664
665#[derive(Debug, Clone)]
666pub struct UnknownNullaryFunctionError {
667  pub fxn_id: u64,
668}
669impl MechErrorKind for UnknownNullaryFunctionError {
670  fn name(&self) -> &str {
671    "UnknownNullaryFunction"
672  }
673  fn message(&self) -> String {
674    format!("Unknown nullary function ID: {}", self.fxn_id)
675  }
676}
677
678#[derive(Debug, Clone)]
679pub struct IndexOutOfBoundsError;
680impl MechErrorKind for IndexOutOfBoundsError {
681  fn name(&self) -> &str {
682    "IndexOutOfBounds"
683  }
684  fn message(&self) -> String {
685    "Index out of bounds".to_string()
686  }
687}
688
689#[derive(Debug, Clone)]
690pub struct OverflowSubtractionError;
691impl MechErrorKind for OverflowSubtractionError {
692  fn name(&self) -> &str {
693    "OverflowSubtraction"
694  }
695  fn message(&self) -> String {
696    "Attempted subtraction overflow".to_string()
697  }
698}
699
700#[derive(Debug, Clone)]
701pub struct UnknownPanicError {
702  pub details: String,
703}
704impl MechErrorKind for UnknownPanicError {
705  fn name(&self) -> &str {
706    "UnknownPanic"
707  }
708  fn message(&self) -> String {
709    self.details.clone()
710  }
711}
712
713#[derive(Debug, Clone)]
714struct StepIndexOutOfBoundsError {
715  pub step_id: usize,
716  pub plan_length: usize,
717}
718impl MechErrorKind for StepIndexOutOfBoundsError {
719  fn name(&self) -> &str {
720    "StepIndexOutOfBounds"
721  }
722  fn message(&self) -> String {
723    format!(
724      "Step id {} out of range (plan has {} steps)",
725      self.step_id, self.plan_length
726    )
727  }
728}
729
730#[derive(Debug, Clone)]
731struct NoStepsInPlanError;
732impl MechErrorKind for NoStepsInPlanError {
733  fn name(&self) -> &str {
734    "NoStepsInPlan"
735  }
736  fn message(&self) -> String {
737    "Plan contains no steps. This program doesn't do anything.".to_string()
738  }
739}