repo_description: A programming language that's wise beyond its bytes!🌱🌿🪴
repo_topics: algebraic-data-types c compiler frontend mobile pattern-matching polymorphism portable rust structural-typing turing-tarpit wasm web x86-64
code: //! # Virtual Machine Module
  //!
  //! This module contains all things related to the virtual machine.
  //!
  //! ## What is this machine?
  //!
  //! This virtual machine is a simple turing tape machine.
  //! There is one register, a tape of cells, and a tape pointer. Cells are
  //! restricted to integers in the core variant of the machine code, but floats
  //! are supported in the standard variant.
  //!
  //! ## What should I know first?
  //!
  //! You should ***NOT*** use pointers (values used with the Deref, Refer, Where,
  //! Alloc, and Free operations) as if they are integers. Think about pointers
  //! and integers as two completely separate types.
  //!
  //! #### Why?
  //!
  //! This is because virtual machine **implementations are bound to vary**.
  //! For example: my C implementation uses *real pointers* (which are retrieved
  //! through virtual machine instructions `Where` and `Alloc`, and allows the
  //! implementation to be used with valgrind, gprof, a custom allocater, or
  //! potentially garbage collection!), but an implementation in a language
  //! like Python might use integer indices in a list instead.
  //!
  //! If the backend implementation uses pointers, *using `Inc` to move a pointer
  //! to the next cell **will not work***. This is because pointers need to be
  //! incremented by the size of the data type they point to. Because the virtual
  //! machine's cell size is undefined (purposely, to make it as portable as possible),
  //! ***you cannot know this size***. Therefore you cannot use `Inc` to move a pointer
  //! to the next cell unless *you want your code to be unportable*.
  //!
  //! ***DO NOT USE `Inc` AND `Dec` TO MOVE POINTERS! NAVIGATE THE POINTER TO THE
  //! DESIRED POSITION AND USE `Where` INSTEAD! OR YOUR CODE WILL NOT PORT TO ALL
  //! IMPLEMENTATIONS!!***
  //!
  //! ## What data can it use?
  //!
  //! ***The virtual machine uses cells of any arbitrary bit width >= 16. The tape
  //! must contain at least 4096 cells.*** The bit width is undefined, but **it must
  //! remain constant for every cells.** Additionally, **the floating point (and pointer)
  //! representation must be identical in size to the integer representation**.
  //!
  //! In this particular assembler, we assume that the bit width is 64. Supporting
  //! smaller or larger bit widths is supported just by using integers of the
  //! appropriate size, or using addition / multiplication to reach larger numbers.
  //!
  //! **An implementation of the virtual machine *should* be of any reasonable
  //! bit width like: 16, 32, 64 bits (the standard), or unbounded.** For each implementation, the bits
  //! of the integer and floats supported should be identical. The
  //! default implementation should be with 64 bit ints and floats, but
  //! 16 bit ints + no floats for a hardware implementation would suffice.
  //! Infinitely large ints and floats are also supported, but the implementation
  //! must be able to handle them.
  use crate::side_effects::{FFIBinding, Input, Output};
  use ::core::fmt::{Display, Formatter, Result as FmtResult};
  
  mod core;
  pub use self::core::*;
  
  mod std;
  pub use self::std::*;
  
  mod interpreter;
  pub use interpreter::*;
  
  /// An error generated by the virtual machine.
  #[derive(Clone, Debug, PartialEq, PartialOrd)]
  pub enum Error {
      /// When an instruction is unsupported for a given implementation
      /// of the virtual machine, this error is triggered.
      UnsupportedInstruction(StandardOp),
      /// When the virtual machine attempts to get the program as core,
      /// but finds standard instructions, this error is triggered.
      ExpectedCore(StandardOp),
  }
  
  impl Display for Error {
      fn fmt(&self, f: &mut Formatter) -> FmtResult {
          match self {
              Self::UnsupportedInstruction(op) => write!(f, "Unsupported instruction: {}", op),
              Self::ExpectedCore(op) => write!(f, "Expected core instruction, found: {}", op),
          }
      }
  }
  
  /// An interface to conveniently create virtual machine programs,
  /// of either the core or standard variant.
  pub trait VirtualMachineProgram {
      fn op(&mut self, op: CoreOp);
      fn std_op(&mut self, op: StandardOp) -> Result<(), Error>;
      fn code(&self) -> Result<CoreProgram, StandardProgram>;
  
      fn set_float_vector(&mut self, vector: Vec<f64>) -> Result<(), Error> {
          self.std_op(StandardOp::Set(vector))
      }
  
      fn set_vector(&mut self, vector: Vec<i64>) {
          self.op(CoreOp::Set(vector))
      }
  
      fn store_vector(&mut self, n: usize) {
          self.op(CoreOp::Store(n))
      }
  
      fn load_vector(&mut self, n: usize) {
          self.op(CoreOp::Load(n))
      }
  
      fn ffi_call(&mut self, ffi: FFIBinding) -> Result<(), Error> {
          self.std_op(StandardOp::Call(ffi))
      }
  
      fn poke(&mut self) -> Result<(), Error> {
          self.std_op(StandardOp::Poke)
      }
  
      fn peek(&mut self) -> Result<(), Error> {
          self.std_op(StandardOp::Peek)
      }
  
      fn comment(&mut self, comment: &str) {
          self.op(CoreOp::Comment(comment.to_string()));
      }
  
      fn restore(&mut self) {
          self.op(CoreOp::Load(1));
      }
  
      fn save(&mut self) {
          self.op(CoreOp::Store(1));
      }
  
      fn ret(&mut self) {
          self.op(CoreOp::Return);
      }
  
      fn where_is_pointer(&mut self) {
          self.op(CoreOp::Where);
      }
  
      fn deref(&mut self) {
          self.op(CoreOp::Deref);
      }
  
      fn refer(&mut self) {
          self.op(CoreOp::Refer);
      }
  
      fn move_pointer(&mut self, cells: isize) {
          if cells != 0 {
              self.op(CoreOp::Move(cells));
          }
      }
  
      fn set_register(&mut self, val: i64) {
          self.op(CoreOp::Set(vec![val]))
      }
  
      fn set_float_register(&mut self, val: f64) -> Result<(), Error> {
          self.std_op(StandardOp::Set(vec![val]))
      }
  
      fn begin_while(&mut self) {
          self.op(CoreOp::While)
      }
  
      fn begin_if(&mut self) {
          self.op(CoreOp::If)
      }
  
      fn begin_else(&mut self) {
          self.op(CoreOp::Else)
      }
  
      fn begin_function(&mut self) {
          self.op(CoreOp::Function)
      }
  
      fn end(&mut self) {
          self.op(CoreOp::End)
      }
  
      fn call(&mut self) {
          self.op(CoreOp::Call)
      }
  
      fn index(&mut self) {
          self.op(CoreOp::Index(1))
      }
  
      fn bitwise_nand(&mut self) {
          self.op(CoreOp::BitwiseNand(1))
      }
  
      fn get(&mut self, src: Input) {
          self.op(CoreOp::Get(src))
      }
  
      fn put(&mut self, dst: Output) {
          self.op(CoreOp::Put(dst))
      }
  
      fn is_non_negative(&mut self) {
          self.op(CoreOp::IsNonNegative(1))
      }
  }
  