forked from AbleScript/ablescript
Implement functio
declaration & calling
This commit is contained in:
parent
a1aa4edc30
commit
e709f398f7
|
@ -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 {
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub enum ErrorKind {
|
||||||
TopLevelBreak,
|
TopLevelBreak,
|
||||||
ArithmeticError,
|
ArithmeticError,
|
||||||
BfInterpretError(InterpretError),
|
BfInterpretError(InterpretError),
|
||||||
|
MismatchedArgumentError,
|
||||||
MissingLhs,
|
MissingLhs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
119
src/interpret.rs
119
src/interpret.rs
|
@ -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()
|
|
||||||
.write_all(&output)
|
|
||||||
.expect("Failed to write to stdout");
|
|
||||||
}
|
|
||||||
Functio::AbleFunctio(_) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
kind: ErrorKind::TypeError(iden.iden.to_owned()),
|
kind: ErrorKind::TypeError(iden.iden.to_owned()),
|
||||||
span: stmt.span.clone(),
|
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> {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue