
After go1.16, go will use module mode by default, even when the repository is checked out under GOPATH or in a one-off directory. Add go.mod, go.sum to keep this repo buildable without opting out of the module mode. > go mod init github.com/mmcgrana/gobyexample > go mod tidy > go mod vendor In module mode, the 'vendor' directory is special and its contents will be actively maintained by the go command. pygments aren't the dependency the go will know about, so it will delete the contents from vendor directory. Move it to `third_party` directory now. And, vendor the blackfriday package. Note: the tutorial contents are not affected by the change in go1.16 because all the examples in this tutorial ask users to run the go command with the explicit list of files to be compiled (e.g. `go run hello-world.go` or `go build command-line-arguments.go`). When the source list is provided, the go command does not have to compute the build list and whether it's running in GOPATH mode or module mode becomes irrelevant.
607 lines
23 KiB
Rust
607 lines
23 KiB
Rust
// -------------------------------------------------------------------------------------------------
|
|
// Rick, a Rust intercal compiler. Save your souls!
|
|
//
|
|
// Copyright (c) 2015 Georg Brandl
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
// GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
|
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with this program;
|
|
// if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
// -------------------------------------------------------------------------------------------------
|
|
|
|
/// Interprets INTERCAL source.
|
|
///
|
|
/// The evaluator is used when rick is called with `-i`, or when the compiler generates
|
|
/// the output while compiling (in the constant-output case).
|
|
|
|
use std::fmt::{ Debug, Display };
|
|
use std::io::Write;
|
|
use std::u16;
|
|
|
|
use err::{ Res, IE123, IE129, IE252, IE275, IE555, IE633, IE774, IE994 };
|
|
use ast::{ self, Program, Stmt, StmtBody, ComeFrom, Expr, Var, VType };
|
|
use stdops::{ Bind, Array, write_number, read_number, check_chance, check_ovf, pop_jumps,
|
|
get_random_seed, mingle, select, and_16, and_32, or_16, or_32, xor_16, xor_32 };
|
|
|
|
|
|
/// Represents a value (either 16-bit or 32-bit) at runtime.
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
pub enum Val {
|
|
I16(u16),
|
|
I32(u32),
|
|
}
|
|
|
|
impl Val {
|
|
/// Cast as a 16-bit value; returns an error if 32-bit and too big.
|
|
pub fn as_u16(&self) -> Res<u16> {
|
|
match *self {
|
|
Val::I16(v) => Ok(v),
|
|
Val::I32(v) => {
|
|
if v > (u16::MAX as u32) {
|
|
return IE275.err();
|
|
}
|
|
Ok(v as u16)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Cast as a 32-bit value; always succeeds.
|
|
pub fn as_u32(&self) -> u32 {
|
|
match *self {
|
|
Val::I16(v) => v as u32,
|
|
Val::I32(v) => v
|
|
}
|
|
}
|
|
|
|
/// Cast as an usize value; always succeeds.
|
|
pub fn as_usize(&self) -> usize {
|
|
self.as_u32() as usize
|
|
}
|
|
|
|
/// Create from a 32-bit value; will select the smallest possible type.
|
|
pub fn from_u32(v: u32) -> Val {
|
|
if v & 0xFFFF == v {
|
|
Val::I16(v as u16)
|
|
} else {
|
|
Val::I32(v)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The state of the interpreter's evaluator.
|
|
pub struct Eval<'a> {
|
|
/// Program to execute.
|
|
program: &'a Program,
|
|
/// Stream to use for printing output.
|
|
stdout: &'a mut Write,
|
|
/// Whether to print debugging output during execution.
|
|
debug: bool,
|
|
/// Variable bindings for the four types of variables.
|
|
spot: Vec<Bind<u16>>,
|
|
twospot: Vec<Bind<u32>>,
|
|
tail: Vec<Bind<Array<u16>>>,
|
|
hybrid: Vec<Bind<Array<u32>>>,
|
|
/// The infamous NEXT stack, capable of holding 80 elements.
|
|
jumps: Vec<ast::LogLine>,
|
|
/// Abstain counter for each statement.
|
|
abstain: Vec<u32>,
|
|
/// Binary I/O "tape" state.
|
|
last_in: u8,
|
|
last_out: u8,
|
|
/// Random number generator state.
|
|
rand_st: u32,
|
|
/// Counts the number of executed statements.
|
|
stmt_ctr: usize,
|
|
}
|
|
|
|
/// Represents the control flow effect of an executed statement.
|
|
enum StmtRes {
|
|
/// normal execution, next statement
|
|
Next,
|
|
/// jump around, from DO ... NEXT
|
|
Jump(usize),
|
|
/// jump back, from RESUME
|
|
Back(usize),
|
|
/// start from the first statement, from TRY AGAIN
|
|
FromTop,
|
|
/// end the program, from GIVE UP
|
|
End,
|
|
}
|
|
|
|
impl<'a> Eval<'a> {
|
|
/// Construct a new evaluator.
|
|
pub fn new(program: &'a Program, stdout: &'a mut Write, debug: bool,
|
|
random: bool) -> Eval<'a> {
|
|
let abs = program.stmts.iter().map(|stmt| stmt.props.disabled as u32).collect();
|
|
let nvars = (program.var_info.0.len(),
|
|
program.var_info.1.len(),
|
|
program.var_info.2.len(),
|
|
program.var_info.3.len());
|
|
Eval {
|
|
program: program,
|
|
stdout: stdout,
|
|
debug: debug,
|
|
spot: vec![Bind::new(0); nvars.0],
|
|
twospot: vec![Bind::new(0); nvars.1],
|
|
tail: vec![Bind::new(Array::empty()); nvars.2],
|
|
hybrid: vec![Bind::new(Array::empty()); nvars.3],
|
|
jumps: Vec::with_capacity(80),
|
|
rand_st: if random { get_random_seed() } else { 0 },
|
|
abstain: abs,
|
|
last_in: 0,
|
|
last_out: 0,
|
|
stmt_ctr: 0,
|
|
}
|
|
}
|
|
|
|
/// Interpret the program. Returns either the number of executed statements,
|
|
/// or an error (RtError).
|
|
pub fn eval(&mut self) -> Res<usize> {
|
|
let mut pctr = 0; // index of current statement
|
|
let program = self.program.clone();
|
|
let nstmts = program.stmts.len();
|
|
loop {
|
|
// check for falling off the end
|
|
if pctr >= nstmts {
|
|
// if the last statement was a TRY AGAIN, falling off the end is fine
|
|
if let StmtBody::TryAgain = program.stmts[program.stmts.len() - 1].body {
|
|
break;
|
|
}
|
|
return IE633.err();
|
|
}
|
|
self.stmt_ctr += 1;
|
|
let stmt = &program.stmts[pctr];
|
|
// execute statement if not abstained
|
|
if self.abstain[pctr] == 0 {
|
|
// check execution chance
|
|
let (passed, rand_st) = check_chance(stmt.props.chance, self.rand_st);
|
|
self.rand_st = rand_st;
|
|
if passed {
|
|
// try to eval this statement
|
|
let res = match self.eval_stmt(stmt) {
|
|
// on error, set the correct line number and bubble up
|
|
Err(mut err) => {
|
|
err.set_line(stmt.props.onthewayto);
|
|
// special treatment for NEXT
|
|
if let StmtBody::DoNext(n) = stmt.body {
|
|
if let Some(i) = program.labels.get(&n) {
|
|
err.set_line(program.stmts[*i as usize].props.srcline);
|
|
}
|
|
}
|
|
return Err(err);
|
|
}
|
|
Ok(res) => res
|
|
};
|
|
// handle control flow effects
|
|
match res {
|
|
StmtRes::Next => { }
|
|
StmtRes::Jump(n) => {
|
|
self.jumps.push(pctr as u16); // push the line with the NEXT
|
|
pctr = n;
|
|
continue; // do not increment or check for COME FROMs
|
|
}
|
|
StmtRes::Back(n) => {
|
|
pctr = n; // will be incremented below after COME FROM check
|
|
}
|
|
StmtRes::FromTop => {
|
|
pctr = 0; // start from the beginning, do not push any stack
|
|
continue;
|
|
}
|
|
StmtRes::End => break,
|
|
}
|
|
}
|
|
}
|
|
// if we are on the line with the compiler bug, error out
|
|
if pctr == self.program.bugline as usize {
|
|
return IE774.err_with(None, stmt.props.onthewayto);
|
|
}
|
|
// try to determine if we have to go to a COME FROM statement
|
|
// (note: in general, program.stmts[pctr] != stmt)
|
|
//
|
|
// the static COME FROM is always a possibility
|
|
let mut maybe_next = program.stmts[pctr].comefrom;
|
|
// the complicated case: evaluate all computed-come-from expressions
|
|
let my_label = program.stmts[pctr].props.label;
|
|
if program.uses_complex_comefrom && my_label > 0 {
|
|
for (i, stmt) in program.stmts.iter().enumerate() {
|
|
if let StmtBody::ComeFrom(ComeFrom::Expr(ref e)) = stmt.body {
|
|
let v = try!(try!(self.eval_expr(e)).as_u16());
|
|
if v == my_label {
|
|
// as soon as we have multiple candidates, we can bail out
|
|
if maybe_next.is_some() {
|
|
return IE555.err();
|
|
}
|
|
maybe_next = Some(i as u16);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// check for COME FROMs from this line
|
|
if let Some(next) = maybe_next {
|
|
let next = next as usize;
|
|
// check for abstained COME FROM
|
|
if self.abstain[next] == 0 {
|
|
// the COME FROM can also have a % chance
|
|
let (passed, rand_st) = check_chance(program.stmts[next].props.chance,
|
|
self.rand_st);
|
|
self.rand_st = rand_st;
|
|
if passed {
|
|
pctr = next;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
// no COME FROM, normal execution
|
|
pctr += 1;
|
|
}
|
|
Ok(self.stmt_ctr)
|
|
}
|
|
|
|
/// Interpret a single statement.
|
|
fn eval_stmt(&mut self, stmt: &Stmt) -> Res<StmtRes> {
|
|
if self.debug {
|
|
println!("\nExecuting Stmt #{} (state before following)", self.stmt_ctr);
|
|
self.dump_state();
|
|
println!("{}", stmt);
|
|
}
|
|
match stmt.body {
|
|
StmtBody::Calc(ref var, ref expr) => {
|
|
let val = try!(self.eval_expr(expr));
|
|
try!(self.assign(var, val));
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Dim(ref var, ref exprs) => {
|
|
try!(self.array_dim(var, exprs));
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::DoNext(n) => {
|
|
match self.program.labels.get(&n) {
|
|
// too many jumps on stack already?
|
|
Some(_) if self.jumps.len() >= 80 => IE123.err(),
|
|
Some(i) => Ok(StmtRes::Jump(*i as usize)),
|
|
None => IE129.err(),
|
|
}
|
|
}
|
|
StmtBody::ComeFrom(_) => {
|
|
// nothing to do here at runtime
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Resume(ref expr) => {
|
|
let n = try!(self.eval_expr(expr)).as_u32();
|
|
// this expect() is safe: if the third arg is true, there will
|
|
// be no Ok(None) returns
|
|
let next = try!(pop_jumps(&mut self.jumps, n, true, 0))
|
|
.expect("https://xkcd.com/378/ ?!");
|
|
Ok(StmtRes::Back(next as usize))
|
|
}
|
|
StmtBody::Forget(ref expr) => {
|
|
let n = try!(self.eval_expr(expr)).as_u32();
|
|
try!(pop_jumps(&mut self.jumps, n, false, 0));
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Ignore(ref vars) => {
|
|
for var in vars {
|
|
self.set_rw(var, false);
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Remember(ref vars) => {
|
|
for var in vars {
|
|
self.set_rw(var, true);
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Stash(ref vars) => {
|
|
for var in vars {
|
|
self.stash(var);
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Retrieve(ref vars) => {
|
|
for var in vars {
|
|
try!(self.retrieve(var));
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Abstain(ref expr, ref whats) => {
|
|
let f: Box<Fn(u32) -> u32> = if let Some(ref e) = *expr {
|
|
let n = try!(self.eval_expr(e)).as_u32();
|
|
box move |v: u32| v.saturating_add(n)
|
|
} else {
|
|
box |_| 1
|
|
};
|
|
for what in whats {
|
|
self.abstain(what, &*f);
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::Reinstate(ref whats) => {
|
|
for what in whats {
|
|
self.abstain(what, &|v: u32| v.saturating_sub(1));
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::ReadOut(ref vars) => {
|
|
for var in vars {
|
|
match *var {
|
|
// read out whole array
|
|
Expr::Var(ref var) if var.is_dim() => {
|
|
try!(self.array_readout(var));
|
|
}
|
|
// read out single var or array element
|
|
Expr::Var(ref var) => {
|
|
let varval = try!(self.lookup(var));
|
|
try!(write_number(self.stdout, varval.as_u32(), 0));
|
|
}
|
|
// read out constant
|
|
Expr::Num(_, v) => try!(write_number(self.stdout, v, 0)),
|
|
// others will not be generated
|
|
_ => return IE994.err(),
|
|
};
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::WriteIn(ref vars) => {
|
|
for var in vars {
|
|
if var.is_dim() {
|
|
// write in whole array
|
|
try!(self.array_writein(var));
|
|
} else {
|
|
// write in single var or array element
|
|
let n = try!(read_number(0));
|
|
try!(self.assign(var, Val::from_u32(n)));
|
|
}
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
// this one is only generated by the constant-program optimizer
|
|
StmtBody::Print(ref s) => {
|
|
if let Err(_) = self.stdout.write(&s) {
|
|
return IE252.err();
|
|
}
|
|
Ok(StmtRes::Next)
|
|
}
|
|
StmtBody::TryAgain => Ok(StmtRes::FromTop),
|
|
StmtBody::GiveUp => Ok(StmtRes::End),
|
|
StmtBody::Error(ref e) => Err((*e).clone()),
|
|
}
|
|
}
|
|
|
|
/// Evaluate an expression to a value.
|
|
fn eval_expr(&self, expr: &Expr) -> Res<Val> {
|
|
match *expr {
|
|
Expr::Num(vtype, v) => match vtype {
|
|
VType::I16 => Ok(Val::I16(v as u16)),
|
|
VType::I32 => Ok(Val::I32(v)),
|
|
},
|
|
Expr::Var(ref var) => self.lookup(var),
|
|
Expr::Mingle(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx)).as_u32();
|
|
let w = try!(self.eval_expr(wx)).as_u32();
|
|
let v = try!(check_ovf(v, 0));
|
|
let w = try!(check_ovf(w, 0));
|
|
Ok(Val::I32(mingle(v, w)))
|
|
}
|
|
Expr::Select(vtype, ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
if vtype == VType::I16 {
|
|
Ok(Val::I16(select(v.as_u32(), try!(w.as_u16()) as u32) as u16))
|
|
} else {
|
|
Ok(Val::I32(select(v.as_u32(), w.as_u32())))
|
|
}
|
|
}
|
|
Expr::And(vtype, ref vx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
match vtype {
|
|
VType::I16 => Ok(Val::I16(and_16(try!(v.as_u16()) as u32) as u16)),
|
|
VType::I32 => Ok(Val::I32(and_32(v.as_u32()))),
|
|
}
|
|
}
|
|
Expr::Or(vtype, ref vx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
match vtype {
|
|
VType::I16 => Ok(Val::I16(or_16(try!(v.as_u16()) as u32) as u16)),
|
|
VType::I32 => Ok(Val::I32(or_32(v.as_u32()))),
|
|
}
|
|
}
|
|
Expr::Xor(vtype, ref vx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
match vtype {
|
|
VType::I16 => Ok(Val::I16(xor_16(try!(v.as_u16()) as u32) as u16)),
|
|
VType::I32 => Ok(Val::I32(xor_32(v.as_u32()))),
|
|
}
|
|
}
|
|
Expr::RsNot(ref vx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
Ok(Val::I32(!v.as_u32()))
|
|
}
|
|
Expr::RsAnd(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() & w.as_u32()))
|
|
}
|
|
Expr::RsOr(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() | w.as_u32()))
|
|
}
|
|
Expr::RsXor(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() ^ w.as_u32()))
|
|
}
|
|
Expr::RsRshift(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() >> w.as_u32()))
|
|
}
|
|
Expr::RsLshift(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() << w.as_u32()))
|
|
}
|
|
// Expr::RsEqual(ref vx, ref wx) => {
|
|
// let v = try!(self.eval_expr(vx));
|
|
// let w = try!(self.eval_expr(wx));
|
|
// Ok(Val::I32((v.as_u32() == w.as_u32()) as u32))
|
|
// }
|
|
Expr::RsNotEqual(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32((v.as_u32() != w.as_u32()) as u32))
|
|
}
|
|
Expr::RsPlus(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() + w.as_u32()))
|
|
}
|
|
Expr::RsMinus(ref vx, ref wx) => {
|
|
let v = try!(self.eval_expr(vx));
|
|
let w = try!(self.eval_expr(wx));
|
|
Ok(Val::I32(v.as_u32() - w.as_u32()))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn eval_subs(&self, subs: &Vec<Expr>) -> Res<Vec<usize>> {
|
|
subs.iter().map(|v| self.eval_expr(v).map(|w| w.as_usize())).collect()
|
|
}
|
|
|
|
/// Dimension an array.
|
|
fn array_dim(&mut self, var: &Var, dims: &Vec<Expr>) -> Res<()> {
|
|
let dims = try!(self.eval_subs(dims));
|
|
match *var {
|
|
Var::A16(n, _) => self.tail[n].dimension(dims, 0),
|
|
Var::A32(n, _) => self.hybrid[n].dimension(dims, 0),
|
|
_ => return IE994.err(),
|
|
}
|
|
}
|
|
|
|
/// Assign to a variable.
|
|
fn assign(&mut self, var: &Var, val: Val) -> Res<()> {
|
|
match *var {
|
|
Var::I16(n) => Ok(self.spot[n].assign(try!(val.as_u16()))),
|
|
Var::I32(n) => Ok(self.twospot[n].assign(val.as_u32())),
|
|
Var::A16(n, ref subs) => {
|
|
let subs = try!(self.eval_subs(subs));
|
|
self.tail[n].set_md(subs, try!(val.as_u16()), 0)
|
|
}
|
|
Var::A32(n, ref subs) => {
|
|
let subs = try!(self.eval_subs(subs));
|
|
self.hybrid[n].set_md(subs, val.as_u32(), 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Look up the value of a variable.
|
|
fn lookup(&self, var: &Var) -> Res<Val> {
|
|
match *var {
|
|
Var::I16(n) => Ok(Val::I16(self.spot[n].val)),
|
|
Var::I32(n) => Ok(Val::I32(self.twospot[n].val)),
|
|
Var::A16(n, ref subs) => {
|
|
let subs = try!(self.eval_subs(subs));
|
|
self.tail[n].get_md(subs, 0).map(Val::I16)
|
|
}
|
|
Var::A32(n, ref subs) => {
|
|
let subs = try!(self.eval_subs(subs));
|
|
self.hybrid[n].get_md(subs, 0).map(Val::I32)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Process a STASH statement.
|
|
fn stash(&mut self, var: &Var) {
|
|
match *var {
|
|
Var::I16(n) => self.spot[n].stash(),
|
|
Var::I32(n) => self.twospot[n].stash(),
|
|
Var::A16(n, _) => self.tail[n].stash(),
|
|
Var::A32(n, _) => self.hybrid[n].stash(),
|
|
}
|
|
}
|
|
|
|
/// Process a RETRIEVE statement.
|
|
fn retrieve(&mut self, var: &Var) -> Res<()> {
|
|
match *var {
|
|
Var::I16(n) => self.spot[n].retrieve(0),
|
|
Var::I32(n) => self.twospot[n].retrieve(0),
|
|
Var::A16(n, _) => self.tail[n].retrieve(0),
|
|
Var::A32(n, _) => self.hybrid[n].retrieve(0),
|
|
}
|
|
}
|
|
|
|
/// Process an IGNORE or REMEMBER statement. Cannot fail.
|
|
fn set_rw(&mut self, var: &Var, rw: bool) {
|
|
match *var {
|
|
Var::I16(n) => self.spot[n].rw = rw,
|
|
Var::I32(n) => self.twospot[n].rw = rw,
|
|
Var::A16(n, _) => self.tail[n].rw = rw,
|
|
Var::A32(n, _) => self.hybrid[n].rw = rw,
|
|
}
|
|
}
|
|
|
|
/// P()rocess an ABSTAIN or REINSTATE statement. Cannot fail.
|
|
fn abstain(&mut self, what: &ast::Abstain, f: &Fn(u32) -> u32) {
|
|
if let &ast::Abstain::Label(lbl) = what {
|
|
let idx = self.program.labels[&lbl] as usize;
|
|
if self.program.stmts[idx].body != StmtBody::GiveUp {
|
|
self.abstain[idx] = f(self.abstain[idx]);
|
|
}
|
|
} else {
|
|
for (i, stype) in self.program.stmt_types.iter().enumerate() {
|
|
if stype == what {
|
|
self.abstain[i] = f(self.abstain[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Array readout helper.
|
|
fn array_readout(&mut self, var: &Var) -> Res<()> {
|
|
let state = &mut self.last_out;
|
|
match *var {
|
|
Var::A16(n, _) => self.tail[n].readout(self.stdout, state, 0),
|
|
Var::A32(n, _) => self.hybrid[n].readout(self.stdout, state, 0),
|
|
_ => return IE994.err(),
|
|
}
|
|
}
|
|
|
|
/// Array writein helper.
|
|
fn array_writein(&mut self, var: &Var) -> Res<()> {
|
|
let state = &mut self.last_in;
|
|
match *var {
|
|
Var::A16(n, _) => self.tail[n].writein(state, 0),
|
|
Var::A32(n, _) => self.hybrid[n].writein(state, 0),
|
|
_ => return IE994.err(),
|
|
}
|
|
}
|
|
|
|
/// Debug helpers.
|
|
fn dump_state(&self) {
|
|
self.dump_state_one(&self.spot, ".");
|
|
self.dump_state_one(&self.twospot, ":");
|
|
self.dump_state_one(&self.tail, ",");
|
|
self.dump_state_one(&self.hybrid, ";");
|
|
if self.jumps.len() > 0 {
|
|
println!("Next stack: {:?}", self.jumps);
|
|
}
|
|
//println!("Abstained: {:?}", self.abstain);
|
|
}
|
|
|
|
fn dump_state_one<T: Debug + Display>(&self, vec: &Vec<Bind<T>>, sigil: &str) {
|
|
if vec.len() > 0 {
|
|
for (i, v) in vec.iter().enumerate() {
|
|
print!("{}{} = {}, ", sigil, i, v);
|
|
}
|
|
println!("");
|
|
}
|
|
}
|
|
}
|