Implement functio declaration & calling

This commit is contained in:
Alex Bethel 2021-06-12 22:07:58 -05:00
parent a1aa4edc30
commit e709f398f7
5 changed files with 102 additions and 59 deletions

View file

@ -60,7 +60,7 @@ pub enum StmtKind {
Functio {
iden: Iden,
args: Vec<Iden>,
params: Vec<Iden>,
body: Block,
},
BfFunctio {

View file

@ -20,6 +20,7 @@ pub enum ErrorKind {
TopLevelBreak,
ArithmeticError,
BfInterpretError(InterpretError),
MismatchedArgumentError,
MissingLhs,
}

View file

@ -76,7 +76,7 @@ impl ExecEnv {
// It's an error to issue a `break` outside of a
// `loop` statement.
kind: ErrorKind::TopLevelBreak,
span: span,
span,
}),
}
}
@ -203,11 +203,13 @@ impl ExecEnv {
self.decl_var(&iden.iden, init);
}
StmtKind::Functio {
iden: _,
args: _,
body: _,
} => todo!(),
StmtKind::Functio { iden, params, body } => self.decl_var(
&iden.iden,
Value::Functio(Functio::AbleFunctio {
params: params.iter().map(|iden| iden.iden.to_string()).collect(),
body: body.block.to_owned(),
}),
),
StmtKind::BfFunctio {
iden,
tape_len,
@ -235,49 +237,19 @@ impl ExecEnv {
}
StmtKind::Call { iden, args } => {
let func = self.get_var(&iden)?;
match func {
Value::Functio(func) => {
match func {
Functio::BfFunctio {
instructions,
tape_len,
} => {
let mut input: Vec<u8> = vec![];
for arg in args {
self.eval_expr(arg)?.bf_write(&mut input);
}
println!("input = {:?}", input);
let mut output = vec![];
crate::brian::Interpreter::from_ascii_with_tape_limit(
&instructions,
&input as &[_],
tape_len,
)
.interpret_with_output(&mut output)
.map_err(|e| Error {
kind: ErrorKind::BfInterpretError(e),
span: stmt.span.clone(),
})?;
let args = args
.iter()
.map(|arg| self.eval_expr(arg))
.collect::<Result<_, _>>()?;
// I guess Brainfuck functions write
// output to stdout? It's not quite
// clear to me what else to do. ~~Alex
stdout()
.write_all(&output)
.expect("Failed to write to stdout");
}
Functio::AbleFunctio(_) => {
todo!()
}
}
}
_ => {
if let Value::Functio(func) = func {
self.fn_call(func, args, &stmt.span)?;
} else {
return Err(Error {
kind: ErrorKind::TypeError(iden.iden.to_owned()),
span: stmt.span.clone(),
})
}
});
}
}
StmtKind::Loop { body } => loop {
@ -313,6 +285,65 @@ impl ExecEnv {
Ok(HaltStatus::Finished)
}
/// Call a function with the given arguments (i.e., actual
/// parameters). If the function invocation fails for some reason,
/// report the error at `span`.
fn fn_call(
&mut self,
func: Functio,
args: Vec<Value>,
span: &Range<usize>,
) -> Result<(), Error> {
match func {
Functio::BfFunctio {
instructions,
tape_len,
} => {
let mut input: Vec<u8> = vec![];
for arg in args {
arg.bf_write(&mut input);
}
println!("input = {:?}", input);
let mut output = vec![];
crate::brian::Interpreter::from_ascii_with_tape_limit(
&instructions,
&input as &[_],
tape_len,
)
.interpret_with_output(&mut output)
.map_err(|e| Error {
kind: ErrorKind::BfInterpretError(e),
span: span.to_owned(),
})?;
stdout()
.write_all(&output)
.expect("Failed to write to stdout");
}
Functio::AbleFunctio { params, body } => {
if params.len() != args.len() {
return Err(Error {
kind: ErrorKind::MismatchedArgumentError,
span: span.to_owned(),
});
}
self.stack.push(Default::default());
for (param, arg) in params.iter().zip(args.iter()) {
self.decl_var(param, arg.to_owned());
}
let res = self.eval_stmts_hs(&body, false);
self.stack.pop();
res?;
}
}
Ok(())
}
/// Get the value of a variable. Throw an error if the variable is
/// inaccessible or banned.
fn get_var(&self, name: &Iden) -> Result<Value, Error> {

View file

@ -347,7 +347,7 @@ impl<'source> Parser<'source> {
self.require(Token::LeftParen)?;
let mut args = vec![];
let mut params = vec![];
loop {
match self
.lexer
@ -356,7 +356,7 @@ impl<'source> Parser<'source> {
{
Token::RightParen => break,
Token::Identifier(i) => {
args.push(Iden::new(i, self.lexer.span()));
params.push(Iden::new(i, self.lexer.span()));
// Require comma (next) or right paren (end) after identifier
match self
@ -380,7 +380,7 @@ impl<'source> Parser<'source> {
let body = self.get_block()?;
Ok(StmtKind::Functio { iden, args, body })
Ok(StmtKind::Functio { iden, params, body })
}
/// Parse BF function declaration

View file

@ -40,7 +40,10 @@ pub enum Functio {
instructions: Vec<u8>,
tape_len: usize,
},
AbleFunctio(Vec<Stmt>),
AbleFunctio {
params: Vec<String>,
body: Vec<Stmt>,
},
}
#[derive(Debug, PartialEq, Clone)]
@ -76,7 +79,7 @@ impl Value {
/// break a significant amount of AbleScript code. If more types
/// are added in the future, they should be assigned the remaining
/// discriminant bytes from 06..FF.
pub fn bf_write(&mut self, stream: &mut impl Write) {
pub fn bf_write(&self, stream: &mut impl Write) {
match self {
Value::Nul => stream.write_all(&[0]),
Value::Str(s) => stream
@ -108,7 +111,7 @@ impl Value {
.and_then(|_| stream.write_all(&(instructions.len() as u32).to_le_bytes()))
.and_then(|_| stream.write_all(&instructions))
}
Functio::AbleFunctio(_) => {
Functio::AbleFunctio { params: _, body: _ } => {
todo!()
}
}),
@ -158,7 +161,10 @@ impl Display for Value {
Value::Bool(v) => write!(f, "{}", v),
Value::Abool(v) => write!(f, "{}", v),
Value::Functio(v) => match v {
Functio::BfFunctio { instructions, tape_len } => {
Functio::BfFunctio {
instructions,
tape_len,
} => {
write!(
f,
"({}) {}",
@ -167,10 +173,15 @@ impl Display for Value {
.expect("Brainfuck functio source should be UTF-8")
)
}
Functio::AbleFunctio(source) => {
// TODO: what's the proper way to display an
// AbleScript functio?
write!(f, "{:?}", source)
Functio::AbleFunctio { params, body } => {
write!(
f,
"({}) -> {:?}",
params.join(", "),
// Maybe we should have a pretty-printer for
// statement blocks at some point?
body,
)
}
},
}