77 lines
2.0 KiB
Rust
77 lines
2.0 KiB
Rust
use roxmltree::Node;
|
|
use std::cell::RefCell;
|
|
use std::error::Error;
|
|
use std::rc::Rc;
|
|
|
|
use crate::error::{BadArgumentCount, MissingChild, Unnamed};
|
|
use crate::instruction::Instruction;
|
|
use crate::interpreter::context::Context;
|
|
use crate::interpreter::run::Run;
|
|
use crate::util;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Function {
|
|
pub args: Vec<String>,
|
|
pub ins: Vec<Instruction>,
|
|
}
|
|
|
|
impl Function {
|
|
pub fn run(
|
|
&self,
|
|
args: Vec<Value>,
|
|
ctx: &mut Context,
|
|
globals: &Context,
|
|
) -> Result<Option<Value>, Box<dyn Error>> {
|
|
if args.len() != self.args.len() {
|
|
Err(BadArgumentCount("function", args.len(), self.args.len()))?
|
|
}
|
|
self.args
|
|
.iter()
|
|
.zip(args)
|
|
.for_each(|(p, a)| ctx.assign(p.clone(), a));
|
|
for i in self.ins.iter() {
|
|
i.run(ctx, globals)?;
|
|
}
|
|
Ok(ctx.take(&String::from("__return")))
|
|
}
|
|
|
|
pub fn from(fun: &Node<'_, '_>) -> Result<Function, Box<dyn Error>> {
|
|
Ok(Function {
|
|
args: util::find_node(fun, "arguments")
|
|
.ok_or(MissingChild("call", "arguments"))?
|
|
.children()
|
|
.filter(Node::is_element)
|
|
.map(|n| n.attribute("name").map(String::from))
|
|
.collect::<Option<Vec<String>>>()
|
|
.ok_or(Unnamed("argument"))?,
|
|
ins: Instruction::from_children(
|
|
util::find_node(fun, "body").ok_or(MissingChild("call", "body"))?,
|
|
)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
type StdFn = fn(Vec<Value>) -> Result<Option<Value>, Box<dyn Error>>;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum Value {
|
|
Integer(i64),
|
|
Real(f64),
|
|
String(String),
|
|
Array(Rc<RefCell<Vec<Value>>>),
|
|
Function(Function),
|
|
StdFunction(StdFn),
|
|
}
|
|
|
|
impl Value {
|
|
pub fn to_bool(&self) -> bool {
|
|
match self {
|
|
Value::Integer(i) => *i != 0,
|
|
Value::Real(f) => *f != 0.0,
|
|
Value::String(s) => !s.is_empty(),
|
|
Value::Array(v) => v.borrow().len() != 0,
|
|
_ => true,
|
|
}
|
|
}
|
|
}
|