prexcl 0.0.10

Prexcl is an esoteric proof-of-concept programming language.
Documentation
use std;
use std::collections::HashMap;
use std::rc::Rc;

use super::{Command, Status};

#[derive(Debug)]
struct StackFrame {
  store_load: Vec<Status>, // TODO move out
  ops: *const Operation,
  len: usize,
  pos: usize,
}

struct FrameHolder<'a, 'b> where 'b: 'a {
  frame: Option<StackFrame>,
  _lt: std::marker::PhantomData<&'a [Operation]>,
  state: &'b mut State,
}

impl<'a, 'b> FrameHolder<'a, 'b> where 'b: 'a  {
  fn new(state: &'b mut State, ops: &'a [Operation]) -> FrameHolder<'a, 'b> where 'b: 'a {
    FrameHolder {
      frame: Some(StackFrame { store_load: Vec::new(), ops: ops.as_ptr(), len: ops.len(), pos: 0 }),
      _lt: std::marker::PhantomData,
      state: state
    }
  }

  fn with<F, R>(mut self, f: F) -> R where F: FnOnce(&mut State) -> R {
    let frame = self.frame.take().unwrap();
    self.state.frames.push(frame);
    f(self.state)
  }
}

impl<'a, 'b> Drop for FrameHolder<'a, 'b> where 'b: 'a  {
  fn drop(&mut self) {
    self.state.frames.pop();
  }
}

#[derive(Debug, Clone)]
pub struct Operation {
  trigger: [bool; 2],
  force: [bool; 2],
  cont: [bool; 4],
  op: String,
  args: Vec<String>
}

impl Operation {
  pub fn trigger(&self) -> [bool; 2] {
    self.trigger
  }
}

#[derive(Debug)]
pub struct State {
  frames: Vec<StackFrame>,
  commands: HashMap<&'static str, Rc<Command>>,
}

impl State {
  pub fn new() -> State {
    State { frames: Vec::new(), commands: HashMap::new() }
  }

  pub fn register_command<T: 'static + Command>(&mut self, command: T) {
    self.commands.insert(command.get_name(), Rc::new(command));
  }

  // TODO replace
  pub fn get_store_load<F, R>(&mut self, f: F) -> R where F: FnOnce(&mut Vec<Status>) -> R {
    f(&mut self.frames.last_mut().unwrap().store_load)
  }

  pub fn get_op<F, R>(&self, f: F) -> R where F: FnOnce(&Operation) -> R {
    unsafe {
      let frame = self.frames.last().unwrap();
      f(&std::slice::from_raw_parts(frame.ops, frame.len)[frame.pos])
    }
  }

  pub fn get_pos(&self) -> usize {
    self.frames.last().unwrap().pos
  }

  pub fn call(&mut self, prog: &[Operation]) -> Status {
    FrameHolder::new(self, prog).with(|x| x.run(0).no_return())
  }

  pub fn run(&mut self, from: usize) -> Status {
    let mut status = Status::Success;
    let oldpos = self.frames.last_mut().unwrap().pos;
    //let mut pos = from;
    self.frames.last_mut().unwrap().pos = from;
    while self.frames.last().unwrap().pos < self.frames.last().unwrap().len {
      status = if self.get_op(|op| op.trigger[*status]) {
        let newstatus = {
          if let Some(x) = self.get_op(|op| self.commands.get(op.op.as_str()).map(|x| x.clone())) {
            let args = self.get_op(|op| op.args.clone()); // TODO get rid of the clone
            x.run(self, status, &args)
          } else {
            Status::Failure
          }
        };
        if newstatus.is_return() {
          self.frames.last_mut().unwrap().pos = oldpos;
          return newstatus;
        }
        if self.get_op(|op| op.cont[*status * 2 + *newstatus]) {
          Status::Success
        } else {
          Status::Failure
        }
      } else if self.get_op(|op| op.force[*status]) {
        status
      } else {
        self.frames.last_mut().unwrap().pos = oldpos;
        return status;
      };
      self.frames.last_mut().unwrap().pos += 1;
    }
    self.frames.last_mut().unwrap().pos = oldpos;
    status
  }

  pub fn parse<I>(&mut self, program: I) -> Vec<Operation> where I: Iterator<Item=char> {
    program.chain("\n".chars()).scan(String::new(), |state, x| {
      if x == '\r' || x == '\n' {
        let s = state.clone();
        state.clear();
        Some(Some(s))
      } else {
        state.push(x);
        Some(None)
      }
    }).flat_map(|x| x) // flatten
      .filter(|x| !x.is_empty())
      .filter(|x| !x.starts_with("#"))
      .map(|x| {
        let mut itr = x.chars();
        let trigger = itr.next().and_then(|c| c.to_digit(16)).unwrap();
        let cont = itr.next().and_then(|c| c.to_digit(16)).unwrap();
        if itr.next().unwrap() != ' ' { panic!(); }
        let mut parts = itr.as_str().split(' ');
        let op = parts.next().unwrap().to_owned();
        let args = parts.map(|x| x.to_owned()).collect();
        Operation {
          trigger: [trigger & 1 != 0, trigger & 2 != 0],
          force: [trigger & 4 != 0, trigger & 8 != 0],
          cont: [cont & 1 != 0, cont & 2 != 0, cont & 4 != 0, cont & 8 != 0],
          op: op,
          args: args
        }
      })
      .collect()
  }
}