Implement functio
declaration & calling
This commit is contained in:
parent
a1aa4edc30
commit
e709f398f7
|
@ -60,7 +60,7 @@ pub enum StmtKind {
|
|||
|
||||
Functio {
|
||||
iden: Iden,
|
||||
args: Vec<Iden>,
|
||||
params: Vec<Iden>,
|
||||
body: Block,
|
||||
},
|
||||
BfFunctio {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub enum ErrorKind {
|
|||
TopLevelBreak,
|
||||
ArithmeticError,
|
||||
BfInterpretError(InterpretError),
|
||||
MismatchedArgumentError,
|
||||
MissingLhs,
|
||||
}
|
||||
|
||||
|
|
125
src/interpret.rs
125
src/interpret.rs
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(Error {
|
||||
kind: ErrorKind::TypeError(iden.iden.to_owned()),
|
||||
span: stmt.span.clone(),
|
||||
})
|
||||
}
|
||||
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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue