Implement read semantics in interpreter

Also added a test file `iotest.able` to just read data and print
it (the `cat` of AbleScript I guess).
This commit is contained in:
Alex Bethel 2021-06-18 16:05:50 -05:00
parent 2ec416db97
commit f7ac2a1a43
3 changed files with 70 additions and 14 deletions

View file

@ -0,0 +1,5 @@
var data;
loop {
data read;
data print;
}

View file

@ -1,14 +1,14 @@
use std::ops::Range; use std::{io, ops::Range};
use crate::{brian::InterpretError, lexer::Token}; use crate::{brian::InterpretError, lexer::Token};
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Error { pub struct Error {
pub kind: ErrorKind, pub kind: ErrorKind,
pub span: Range<usize>, pub span: Range<usize>,
} }
#[derive(Debug, Clone)] #[derive(Debug)]
pub enum ErrorKind { pub enum ErrorKind {
SyntaxError(String), SyntaxError(String),
UnexpectedEof, UnexpectedEof,
@ -22,6 +22,7 @@ pub enum ErrorKind {
BfInterpretError(InterpretError), BfInterpretError(InterpretError),
MismatchedArgumentError, MismatchedArgumentError,
MissingLhs, MissingLhs,
IOError(io::Error),
} }
impl Error { impl Error {
@ -35,3 +36,12 @@ impl Error {
Self::new(ErrorKind::UnexpectedEof, index..index) Self::new(ErrorKind::UnexpectedEof, index..index)
} }
} }
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Self {
kind: ErrorKind::IOError(e),
span: 0..0,
}
}
}

View file

@ -9,12 +9,11 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::{HashMap, VecDeque},
io::{stdout, Write}, io::{stdin, stdout, Read, Write},
ops::Range, ops::Range,
process::exit, process::exit,
rc::Rc, rc::Rc,
usize,
}; };
use rand::random; use rand::random;
@ -32,6 +31,13 @@ pub struct ExecEnv {
/// top-most (newest) stack frame, and `stack[0]` is the /// top-most (newest) stack frame, and `stack[0]` is the
/// bottom-most (oldest) stack frame. /// bottom-most (oldest) stack frame.
stack: Vec<Scope>, stack: Vec<Scope>,
/// The `read` statement maintains a buffer of up to 7 bits,
/// because input comes from the operating system 8 bits at a time
/// (via stdin) but gets delivered to AbleScript 3 bits at a time
/// (via the `read` statement). We store each of those bits as
/// booleans to facilitate easy manipulation.
read_buf: VecDeque<bool>,
} }
/// A set of visible variable and function definitions in a single /// A set of visible variable and function definitions in a single
@ -58,6 +64,10 @@ enum HaltStatus {
Hopback(Range<usize>), Hopback(Range<usize>),
} }
/// The number of bits the `read` statement reads at once from
/// standard input.
pub const READ_BITS: u8 = 3;
impl ExecEnv { impl ExecEnv {
/// Create a new Scope with no predefined variable definitions or /// Create a new Scope with no predefined variable definitions or
/// other information. /// other information.
@ -65,6 +75,7 @@ impl ExecEnv {
Self { Self {
// We always need at least one stackframe. // We always need at least one stackframe.
stack: vec![Default::default()], stack: vec![Default::default()],
read_buf: Default::default(),
} }
} }
@ -205,13 +216,15 @@ impl ExecEnv {
self.decl_var(&iden.iden, init); self.decl_var(&iden.iden, init);
} }
StmtKind::Functio { iden, params, body } => self.decl_var( StmtKind::Functio { iden, params, body } => {
self.decl_var(
&iden.iden, &iden.iden,
Value::Functio(Functio::AbleFunctio { Value::Functio(Functio::AbleFunctio {
params: params.iter().map(|iden| iden.iden.to_string()).collect(), params: params.iter().map(|iden| iden.iden.to_string()).collect(),
body: body.block.to_owned(), body: body.block.to_owned(),
}), }),
), );
}
StmtKind::BfFunctio { StmtKind::BfFunctio {
iden, iden,
tape_len, tape_len,
@ -280,7 +293,15 @@ impl ExecEnv {
.write_all(include_str!("rickroll").as_bytes()) .write_all(include_str!("rickroll").as_bytes())
.expect("Failed to write to stdout"); .expect("Failed to write to stdout");
} }
StmtKind::Read(_) => todo!(), StmtKind::Read(iden) => {
let mut value = 0;
for _ in 0..READ_BITS {
value <<= 1;
value += self.get_bit()? as i32;
}
self.get_var_mut(&iden)?.value.replace(Value::Int(value));
}
} }
Ok(HaltStatus::Finished) Ok(HaltStatus::Finished)
@ -356,6 +377,26 @@ impl ExecEnv {
Ok(()) Ok(())
} }
/// Get a single bit from the bit buffer, or refill it from
/// standard input if it is empty.
fn get_bit(&mut self) -> Result<bool, Error> {
const BITS_PER_BYTE: u8 = 8;
if self.read_buf.is_empty() {
let mut data = [0];
stdin().read_exact(&mut data)?;
for n in (0..BITS_PER_BYTE).rev() {
self.read_buf.push_back(((data[0] >> n) & 1) != 0);
}
}
Ok(self
.read_buf
.pop_front()
.expect("We just pushed to the buffer if it was empty"))
}
/// Get the value of a variable. Throw an error if the variable is /// Get the value of a variable. Throw an error if the variable is
/// inaccessible or banned. /// inaccessible or banned.
fn get_var(&self, name: &Iden) -> Result<Value, Error> { fn get_var(&self, name: &Iden) -> Result<Value, Error> {