Implement functio declaration & calling

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

View file

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

View file

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

View file

@ -76,7 +76,7 @@ impl ExecEnv {
// It's an error to issue a `break` outside of a // It's an error to issue a `break` outside of a
// `loop` statement. // `loop` statement.
kind: ErrorKind::TopLevelBreak, kind: ErrorKind::TopLevelBreak,
span: span, span,
}), }),
} }
} }
@ -203,11 +203,13 @@ impl ExecEnv {
self.decl_var(&iden.iden, init); self.decl_var(&iden.iden, init);
} }
StmtKind::Functio { StmtKind::Functio { iden, params, body } => self.decl_var(
iden: _, &iden.iden,
args: _, Value::Functio(Functio::AbleFunctio {
body: _, params: params.iter().map(|iden| iden.iden.to_string()).collect(),
} => todo!(), body: body.block.to_owned(),
}),
),
StmtKind::BfFunctio { StmtKind::BfFunctio {
iden, iden,
tape_len, tape_len,
@ -235,49 +237,19 @@ impl ExecEnv {
} }
StmtKind::Call { iden, args } => { StmtKind::Call { iden, args } => {
let func = self.get_var(&iden)?; 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( let args = args
&instructions, .iter()
&input as &[_], .map(|arg| self.eval_expr(arg))
tape_len, .collect::<Result<_, _>>()?;
)
.interpret_with_output(&mut output)
.map_err(|e| Error {
kind: ErrorKind::BfInterpretError(e),
span: stmt.span.clone(),
})?;
// I guess Brainfuck functions write if let Value::Functio(func) = func {
// output to stdout? It's not quite self.fn_call(func, args, &stmt.span)?;
// clear to me what else to do. ~~Alex } else {
stdout() return Err(Error {
.write_all(&output) kind: ErrorKind::TypeError(iden.iden.to_owned()),
.expect("Failed to write to stdout"); span: stmt.span.clone(),
} });
Functio::AbleFunctio(_) => {
todo!()
}
}
}
_ => {
return Err(Error {
kind: ErrorKind::TypeError(iden.iden.to_owned()),
span: stmt.span.clone(),
})
}
} }
} }
StmtKind::Loop { body } => loop { StmtKind::Loop { body } => loop {
@ -313,6 +285,65 @@ impl ExecEnv {
Ok(HaltStatus::Finished) 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 /// 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> {

View file

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

View file

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