error handling
This commit is contained in:
116
src/error.rs
Normal file
116
src/error.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MissingChild(pub &'static str, pub &'static str);
|
||||||
|
|
||||||
|
impl fmt::Display for MissingChild {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "missing '{}' child in '{}' node", self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MissingChild {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MissingAttribute(pub &'static str, pub &'static str);
|
||||||
|
|
||||||
|
impl fmt::Display for MissingAttribute {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "missing '{}' child in '{}' node", self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MissingAttribute {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BadArgumentCount(pub &'static str, pub usize);
|
||||||
|
|
||||||
|
impl fmt::Display for BadArgumentCount {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "bad argument count ({}) in call to '{}'", self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for BadArgumentCount {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BadChildCount(pub &'static str, pub usize);
|
||||||
|
|
||||||
|
impl fmt::Display for BadChildCount {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "bad child count ({}) in '{}' tag", self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for BadChildCount {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct InvalidArgument(pub &'static str, pub &'static str);
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidArgument {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"invalid value for argument '{}' in call to '{}'",
|
||||||
|
self.1, self.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InvalidArgument {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct InvalidValue(pub &'static str);
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "invalid value in '{}' tag", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InvalidValue {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct InvalidProgram;
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidProgram {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "invalid program structure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InvalidProgram {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct IncompatibleValues;
|
||||||
|
|
||||||
|
impl fmt::Display for IncompatibleValues {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "incompatible comparison values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for IncompatibleValues {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Unnamed(pub &'static str);
|
||||||
|
|
||||||
|
impl fmt::Display for Unnamed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "unnamed '{}'", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for Unnamed {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct UnknownVariable(pub String);
|
||||||
|
|
||||||
|
impl fmt::Display for UnknownVariable {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "unknown variable '{}'", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for UnknownVariable {}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
use super::error::{
|
||||||
|
BadChildCount, IncompatibleValues, InvalidValue, MissingAttribute, MissingChild,
|
||||||
|
UnknownVariable,
|
||||||
|
};
|
||||||
use super::{util, Context, Value};
|
use super::{util, Context, Value};
|
||||||
use roxmltree::Node;
|
use roxmltree::Node;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@@ -47,16 +51,16 @@ impl Instruction {
|
|||||||
"value" => Instruction::Value(
|
"value" => Instruction::Value(
|
||||||
node.attribute("variable")
|
node.attribute("variable")
|
||||||
.and_then(|a| Some(String::from(a)))
|
.and_then(|a| Some(String::from(a)))
|
||||||
.ok_or("missing 'variable' attribute on 'value' tag")?,
|
.ok_or(MissingAttribute("value", "variable"))?,
|
||||||
),
|
),
|
||||||
"assign" => Instruction::Assign(
|
"assign" => Instruction::Assign(
|
||||||
String::from(
|
String::from(
|
||||||
node.attribute("variable")
|
node.attribute("variable")
|
||||||
.ok_or("missing 'variable' attribute on 'assign' tag")?,
|
.ok_or(MissingAttribute("assign", "variable"))?,
|
||||||
),
|
),
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing child on 'assign' tag")?,
|
.ok_or(MissingChild("assign", "value"))?,
|
||||||
)?),
|
)?),
|
||||||
),
|
),
|
||||||
"integer" => {
|
"integer" => {
|
||||||
@@ -65,7 +69,7 @@ impl Instruction {
|
|||||||
} else if let Some(n) = node.first_element_child() {
|
} else if let Some(n) = node.first_element_child() {
|
||||||
Instruction::IntegerCast(Box::new(Instruction::new(n)?))
|
Instruction::IntegerCast(Box::new(Instruction::new(n)?))
|
||||||
} else {
|
} else {
|
||||||
Err("missing 'value' attribute or child in 'integer' tag")?
|
Err(MissingAttribute("integer", "value"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"float" => {
|
"float" => {
|
||||||
@@ -74,7 +78,7 @@ impl Instruction {
|
|||||||
} else if let Some(n) = node.first_element_child() {
|
} else if let Some(n) = node.first_element_child() {
|
||||||
Instruction::FloatCast(Box::new(Instruction::new(n)?))
|
Instruction::FloatCast(Box::new(Instruction::new(n)?))
|
||||||
} else {
|
} else {
|
||||||
Err("missing 'value' attribute or child in 'float' tag")?
|
Err(MissingAttribute("float", "value"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"string" => {
|
"string" => {
|
||||||
@@ -83,7 +87,7 @@ impl Instruction {
|
|||||||
} else if let Some(n) = node.first_element_child() {
|
} else if let Some(n) = node.first_element_child() {
|
||||||
Instruction::StringCast(Box::new(Instruction::new(n)?))
|
Instruction::StringCast(Box::new(Instruction::new(n)?))
|
||||||
} else {
|
} else {
|
||||||
Err("missing 'value' attribute or child in 'string' tag")?
|
Err(MissingAttribute("string", "value"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"array" => Instruction::Array(Instruction::from_children(node)?),
|
"array" => Instruction::Array(Instruction::from_children(node)?),
|
||||||
@@ -95,7 +99,7 @@ impl Instruction {
|
|||||||
"or" => Instruction::Or(Instruction::from_children(node)?),
|
"or" => Instruction::Or(Instruction::from_children(node)?),
|
||||||
"not" => Instruction::Not(Box::new(Instruction::new(
|
"not" => Instruction::Not(Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing value child element in 'not' tag")?,
|
.ok_or(MissingAttribute("not", "value"))?,
|
||||||
)?)),
|
)?)),
|
||||||
"equal" => {
|
"equal" => {
|
||||||
let children: Vec<Node> = node.children().filter(Node::is_element).collect();
|
let children: Vec<Node> = node.children().filter(Node::is_element).collect();
|
||||||
@@ -105,7 +109,7 @@ impl Instruction {
|
|||||||
Box::new(Instruction::new(children[1])?),
|
Box::new(Instruction::new(children[1])?),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err("bad child count in 'equal' tag")?
|
Err(BadChildCount("equal", children.len()))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"greater" => {
|
"greater" => {
|
||||||
@@ -116,7 +120,7 @@ impl Instruction {
|
|||||||
Box::new(Instruction::new(children[1])?),
|
Box::new(Instruction::new(children[1])?),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err("bad child count in 'greater' tag")?
|
Err(BadChildCount("greater", children.len()))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"lower" => {
|
"lower" => {
|
||||||
@@ -127,7 +131,7 @@ impl Instruction {
|
|||||||
Box::new(Instruction::new(children[1])?),
|
Box::new(Instruction::new(children[1])?),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err("bad child count in 'lower' tag")?
|
Err(BadChildCount("lower", children.len()))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"call" => {
|
"call" => {
|
||||||
@@ -136,36 +140,35 @@ impl Instruction {
|
|||||||
String::from(function),
|
String::from(function),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "arguments")
|
util::find_node(&node, "arguments")
|
||||||
.ok_or("missing 'arguments' block in 'call' tag")?,
|
.ok_or(MissingChild("call", "arguments"))?,
|
||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Instruction::Call(
|
Instruction::Call(
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing function child element in 'call' tag")?,
|
.ok_or(MissingChild("call", "function"))?,
|
||||||
)?),
|
)?),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "arguments")
|
util::find_node(&node, "arguments")
|
||||||
.ok_or("missing 'arguments' block in 'call' tag")?,
|
.ok_or(MissingChild("call", "arguments"))?,
|
||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"return" => Instruction::Return(Box::new(Instruction::new(
|
"return" => Instruction::Return(Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing value child element in 'return' tag")?,
|
.ok_or(MissingChild("return", "value"))?,
|
||||||
)?)),
|
)?)),
|
||||||
"if" => {
|
"if" => {
|
||||||
if let Some(else_node) = node.children().find(|n| util::tag_name(n) == "else") {
|
if let Some(else_node) = node.children().find(|n| util::tag_name(n) == "else") {
|
||||||
Instruction::IfElse(
|
Instruction::IfElse(
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing condition child element in 'if' tag")?,
|
.ok_or(MissingChild("if", "condition"))?,
|
||||||
)?),
|
)?),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "then")
|
util::find_node(&node, "then").ok_or(MissingChild("if", "then"))?,
|
||||||
.ok_or("missing 'then' block in 'if' tag")?,
|
|
||||||
)?,
|
)?,
|
||||||
Instruction::from_children(else_node)?,
|
Instruction::from_children(else_node)?,
|
||||||
)
|
)
|
||||||
@@ -173,11 +176,10 @@ impl Instruction {
|
|||||||
Instruction::If(
|
Instruction::If(
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing condition child element in 'if' tag")?,
|
.ok_or(MissingChild("if", "condition"))?,
|
||||||
)?),
|
)?),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "then")
|
util::find_node(&node, "then").ok_or(MissingChild("if", "then"))?,
|
||||||
.ok_or("missing 'then' block in 'if' tag")?,
|
|
||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -185,47 +187,47 @@ impl Instruction {
|
|||||||
"for" => Instruction::For {
|
"for" => Instruction::For {
|
||||||
variable: String::from(
|
variable: String::from(
|
||||||
node.attribute("variable")
|
node.attribute("variable")
|
||||||
.ok_or("missing 'variable' attribute on 'for' tag")?,
|
.ok_or(MissingAttribute("for", "variable"))?,
|
||||||
),
|
),
|
||||||
from: Box::new(Instruction::new(
|
from: Box::new(Instruction::new(
|
||||||
util::find_node(&node, "from")
|
util::find_node(&node, "from")
|
||||||
.and_then(|n| n.first_element_child())
|
.and_then(|n| n.first_element_child())
|
||||||
.ok_or("missing 'from' child in 'for' tag")?,
|
.ok_or(MissingChild("for", "from"))?,
|
||||||
)?),
|
)?),
|
||||||
to: Box::new(Instruction::new(
|
to: Box::new(Instruction::new(
|
||||||
util::find_node(&node, "to")
|
util::find_node(&node, "to")
|
||||||
.and_then(|n| n.first_element_child())
|
.and_then(|n| n.first_element_child())
|
||||||
.ok_or("missing 'to' child in 'for' tag")?,
|
.ok_or(MissingChild("for", "to"))?,
|
||||||
)?),
|
)?),
|
||||||
step: Box::new(Instruction::new(
|
step: Box::new(Instruction::new(
|
||||||
util::find_node(&node, "step")
|
util::find_node(&node, "step")
|
||||||
.and_then(|n| n.first_element_child())
|
.and_then(|n| n.first_element_child())
|
||||||
.ok_or("missing 'step' child in 'for' tag")?,
|
.ok_or(MissingChild("for", "step"))?,
|
||||||
)?),
|
)?),
|
||||||
body: Instruction::from_children(
|
body: Instruction::from_children(
|
||||||
util::find_node(&node, "do").ok_or("missing 'do' block in 'for' tag")?,
|
util::find_node(&node, "do").ok_or(MissingChild("for", "do"))?,
|
||||||
)?,
|
)?,
|
||||||
},
|
},
|
||||||
"each" => Instruction::Each(
|
"each" => Instruction::Each(
|
||||||
String::from(
|
String::from(
|
||||||
node.attribute("variable")
|
node.attribute("variable")
|
||||||
.ok_or("missing 'variable' attribute on 'each' tag")?,
|
.ok_or(MissingAttribute("each", "variable"))?,
|
||||||
),
|
),
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing array child element in 'for' tag")?,
|
.ok_or(MissingChild("each", "array"))?,
|
||||||
)?),
|
)?),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "do").ok_or("missing 'do' block in 'each' tag")?,
|
util::find_node(&node, "do").ok_or(MissingChild("each", "from"))?,
|
||||||
)?,
|
)?,
|
||||||
),
|
),
|
||||||
"while" => Instruction::While(
|
"while" => Instruction::While(
|
||||||
Box::new(Instruction::new(
|
Box::new(Instruction::new(
|
||||||
node.first_element_child()
|
node.first_element_child()
|
||||||
.ok_or("missing condition child element in 'while' tag")?,
|
.ok_or(MissingChild("while", "condition"))?,
|
||||||
)?),
|
)?),
|
||||||
Instruction::from_children(
|
Instruction::from_children(
|
||||||
util::find_node(&node, "do").ok_or("missing 'do' block in 'while' tag")?,
|
util::find_node(&node, "do").ok_or(MissingChild("while", "from"))?,
|
||||||
)?,
|
)?,
|
||||||
),
|
),
|
||||||
tag => Err(format!("unknown tag '{}'", tag))?,
|
tag => Err(format!("unknown tag '{}'", tag))?,
|
||||||
@@ -247,7 +249,7 @@ impl Instruction {
|
|||||||
if let Value::Integer(i) = v {
|
if let Value::Integer(i) = v {
|
||||||
Ok(i)
|
Ok(i)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("add"))?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sum::<Result<i64, Box<dyn Error>>>()?,
|
.sum::<Result<i64, Box<dyn Error>>>()?,
|
||||||
@@ -264,7 +266,7 @@ impl Instruction {
|
|||||||
} else if let Value::Float(f) = v {
|
} else if let Value::Float(f) = v {
|
||||||
Ok(*f)
|
Ok(*f)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("add"))?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sum::<Result<f64, Box<dyn Error>>>()?,
|
.sum::<Result<f64, Box<dyn Error>>>()?,
|
||||||
@@ -284,22 +286,22 @@ impl Instruction {
|
|||||||
} else if let Value::Float(f) = v {
|
} else if let Value::Float(f) = v {
|
||||||
f.to_string()
|
f.to_string()
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("add"))?
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<String>, Box<dyn Error>>>()?
|
.collect::<Result<Vec<String>, Box<dyn Error>>>()?
|
||||||
.join(""),
|
.join(""),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("add"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtract(vals: Vec<Value>) -> Result<Value, Box<dyn Error>> {
|
fn subtract(vals: Vec<Value>) -> Result<Value, Box<dyn Error>> {
|
||||||
Ok(if vals.iter().all(|v| matches!(v, Value::Integer(_))) {
|
Ok(if vals.iter().all(|v| matches!(v, Value::Integer(_))) {
|
||||||
let first = match vals.first().ok_or("missing values in 'subtract' tag")? {
|
let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? {
|
||||||
Value::Integer(i) => i,
|
Value::Integer(i) => i,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("subtract"))?,
|
||||||
};
|
};
|
||||||
Value::Integer(
|
Value::Integer(
|
||||||
*first
|
*first
|
||||||
@@ -310,7 +312,7 @@ impl Instruction {
|
|||||||
if let Value::Integer(i) = v {
|
if let Value::Integer(i) = v {
|
||||||
Ok(i)
|
Ok(i)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("subtract"))?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sum::<Result<i64, Box<dyn Error>>>()?,
|
.sum::<Result<i64, Box<dyn Error>>>()?,
|
||||||
@@ -319,10 +321,10 @@ impl Instruction {
|
|||||||
.iter()
|
.iter()
|
||||||
.all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
|
.all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
|
||||||
{
|
{
|
||||||
let first = match vals.first().ok_or("not enough values in 'subtract' tag")? {
|
let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? {
|
||||||
Value::Integer(v) => *v as f64,
|
Value::Integer(v) => *v as f64,
|
||||||
Value::Float(v) => *v,
|
Value::Float(v) => *v,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("subtract"))?,
|
||||||
};
|
};
|
||||||
Value::Float(
|
Value::Float(
|
||||||
first
|
first
|
||||||
@@ -333,13 +335,13 @@ impl Instruction {
|
|||||||
Ok(match val {
|
Ok(match val {
|
||||||
Value::Integer(v) => *v as f64,
|
Value::Integer(v) => *v as f64,
|
||||||
Value::Float(v) => *v,
|
Value::Float(v) => *v,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("subtract"))?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.sum::<Result<f64, Box<dyn Error>>>()?,
|
.sum::<Result<f64, Box<dyn Error>>>()?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("subtract"))?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,7 +353,7 @@ impl Instruction {
|
|||||||
if let Value::Integer(i) = v {
|
if let Value::Integer(i) = v {
|
||||||
Ok(i)
|
Ok(i)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("multiply"))?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.product::<Result<i64, Box<dyn Error>>>()?,
|
.product::<Result<i64, Box<dyn Error>>>()?,
|
||||||
@@ -366,13 +368,13 @@ impl Instruction {
|
|||||||
Ok(match val {
|
Ok(match val {
|
||||||
Value::Integer(v) => *v as f64,
|
Value::Integer(v) => *v as f64,
|
||||||
Value::Float(v) => *v,
|
Value::Float(v) => *v,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("multiply"))?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.product::<Result<f64, Box<dyn Error>>>()?,
|
.product::<Result<f64, Box<dyn Error>>>()?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("multiply"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,10 +383,10 @@ impl Instruction {
|
|||||||
.iter()
|
.iter()
|
||||||
.all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
|
.all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
|
||||||
{
|
{
|
||||||
let first = match vals.first().ok_or("not enough values in 'divide' tag")? {
|
let first = match vals.first().ok_or(BadChildCount("divide", 0))? {
|
||||||
Value::Integer(v) => *v as f64,
|
Value::Integer(v) => *v as f64,
|
||||||
Value::Float(v) => *v,
|
Value::Float(v) => *v,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("divide"))?,
|
||||||
};
|
};
|
||||||
Ok(Value::Float(
|
Ok(Value::Float(
|
||||||
first
|
first
|
||||||
@@ -395,13 +397,13 @@ impl Instruction {
|
|||||||
Ok(match val {
|
Ok(match val {
|
||||||
Value::Integer(v) => 1.0 / (*v as f64),
|
Value::Integer(v) => 1.0 / (*v as f64),
|
||||||
Value::Float(v) => 1.0 / *v,
|
Value::Float(v) => 1.0 / *v,
|
||||||
_ => Err("invalid value")?,
|
_ => Err(InvalidValue("divide"))?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.product::<Result<f64, Box<dyn Error>>>()?,
|
.product::<Result<f64, Box<dyn Error>>>()?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid value")?
|
Err(InvalidValue("divide"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,39 +429,28 @@ impl Instruction {
|
|||||||
Value::Integer(i1) => match v2 {
|
Value::Integer(i1) => match v2 {
|
||||||
Value::Integer(i2) => Ok(i1 - i2),
|
Value::Integer(i2) => Ok(i1 - i2),
|
||||||
Value::Float(f2) => Ok(
|
Value::Float(f2) => Ok(
|
||||||
match (i1 as f64)
|
match (i1 as f64).partial_cmp(&f2).ok_or(IncompatibleValues)? {
|
||||||
.partial_cmp(&f2)
|
|
||||||
.ok_or("incompatible comparison values")?
|
|
||||||
{
|
|
||||||
Ordering::Less => -1,
|
Ordering::Less => -1,
|
||||||
Ordering::Equal => 0,
|
Ordering::Equal => 0,
|
||||||
Ordering::Greater => 1,
|
Ordering::Greater => 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_ => Err("incompatible comparison values")?,
|
_ => Err(IncompatibleValues)?,
|
||||||
},
|
},
|
||||||
Value::Float(f1) => match v2 {
|
Value::Float(f1) => match v2 {
|
||||||
Value::Integer(i2) => Ok(
|
Value::Integer(i2) => Ok(
|
||||||
match f1
|
match f1.partial_cmp(&(i2 as f64)).ok_or(IncompatibleValues)? {
|
||||||
.partial_cmp(&(i2 as f64))
|
|
||||||
.ok_or("incompatible comparison values")?
|
|
||||||
{
|
|
||||||
Ordering::Less => -1,
|
Ordering::Less => -1,
|
||||||
Ordering::Equal => 0,
|
Ordering::Equal => 0,
|
||||||
Ordering::Greater => 1,
|
Ordering::Greater => 1,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Value::Float(f2) => Ok(
|
Value::Float(f2) => Ok(match f1.partial_cmp(&f2).ok_or(IncompatibleValues)? {
|
||||||
match f1
|
|
||||||
.partial_cmp(&f2)
|
|
||||||
.ok_or("incompatible comparison values")?
|
|
||||||
{
|
|
||||||
Ordering::Less => -1,
|
Ordering::Less => -1,
|
||||||
Ordering::Equal => 0,
|
Ordering::Equal => 0,
|
||||||
Ordering::Greater => 1,
|
Ordering::Greater => 1,
|
||||||
},
|
}),
|
||||||
),
|
_ => Err(IncompatibleValues)?,
|
||||||
_ => Err("incompatible comparison values")?,
|
|
||||||
},
|
},
|
||||||
Value::String(s1) => {
|
Value::String(s1) => {
|
||||||
if let Value::String(s2) = v2 {
|
if let Value::String(s2) = v2 {
|
||||||
@@ -469,10 +460,10 @@ impl Instruction {
|
|||||||
Ordering::Greater => 1,
|
Ordering::Greater => 1,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err("incompatible comparison values")?
|
Err(IncompatibleValues)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err("incompatible comparison values")?,
|
_ => Err(IncompatibleValues)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,87 +477,73 @@ impl Instruction {
|
|||||||
pub fn run(&self, ctx: &mut Context) -> Result<Option<Value>, Box<dyn Error>> {
|
pub fn run(&self, ctx: &mut Context) -> Result<Option<Value>, Box<dyn Error>> {
|
||||||
Ok(if let None = ctx.value(&String::from("__return")) {
|
Ok(if let None = ctx.value(&String::from("__return")) {
|
||||||
match self {
|
match self {
|
||||||
Instruction::Value(key) => Some(
|
Instruction::Value(key) => {
|
||||||
match ctx
|
Some(match ctx.value(key).ok_or(UnknownVariable(key.clone()))? {
|
||||||
.value(key)
|
|
||||||
.ok_or(format!("unknown variable '{}'", key))?
|
|
||||||
{
|
|
||||||
Value::Array(vecrc) => Value::Array(Rc::clone(vecrc)),
|
Value::Array(vecrc) => Value::Array(Rc::clone(vecrc)),
|
||||||
val => val.clone(),
|
val => val.clone(),
|
||||||
},
|
})
|
||||||
),
|
}
|
||||||
Instruction::Assign(key, ins) => {
|
Instruction::Assign(key, ins) => {
|
||||||
let v = ins.run(ctx)?.ok_or("invalid child value in 'assign' tag")?;
|
let v = ins.run(ctx)?.ok_or(InvalidValue("assign"))?;
|
||||||
ctx.assign(key.clone(), v);
|
ctx.assign(key.clone(), v);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Instruction::Integer(val) => Some(Value::Integer(val.parse()?)),
|
Instruction::Integer(val) => Some(Value::Integer(val.parse()?)),
|
||||||
Instruction::IntegerCast(ins) => Some(Value::Integer(
|
Instruction::IntegerCast(ins) => Some(Value::Integer(
|
||||||
match ins.run(ctx)?.ok_or("no value to be cast to 'integer'")? {
|
match ins.run(ctx)?.ok_or(InvalidValue("integer"))? {
|
||||||
Value::Integer(i) => i,
|
Value::Integer(i) => i,
|
||||||
Value::Float(f) => f as i64,
|
Value::Float(f) => f as i64,
|
||||||
Value::String(s) => s.parse()?,
|
Value::String(s) => s.parse()?,
|
||||||
_ => Err("value cannot be cast to 'integer'")?,
|
_ => Err(InvalidValue("integer"))?,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Instruction::Float(val) => Some(Value::Float(val.parse()?)),
|
Instruction::Float(val) => Some(Value::Float(val.parse()?)),
|
||||||
Instruction::FloatCast(ins) => Some(Value::Float(
|
Instruction::FloatCast(ins) => Some(Value::Float(
|
||||||
match ins.run(ctx)?.ok_or("no value to be cast to 'float'")? {
|
match ins.run(ctx)?.ok_or(InvalidValue("float"))? {
|
||||||
Value::Integer(i) => i as f64,
|
Value::Integer(i) => i as f64,
|
||||||
Value::Float(f) => f,
|
Value::Float(f) => f,
|
||||||
Value::String(s) => s.parse()?,
|
Value::String(s) => s.parse()?,
|
||||||
_ => Err("value cannot be cast to 'float'")?,
|
_ => Err(InvalidValue("float"))?,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Instruction::String(val) => Some(Value::String(val.clone())),
|
Instruction::String(val) => Some(Value::String(val.clone())),
|
||||||
Instruction::StringCast(ins) => Some(Value::String(
|
Instruction::StringCast(ins) => Some(Value::String(
|
||||||
match ins.run(ctx)?.ok_or("no value to be cast to 'string'")? {
|
match ins.run(ctx)?.ok_or(InvalidValue("string"))? {
|
||||||
Value::Integer(i) => i.to_string(),
|
Value::Integer(i) => i.to_string(),
|
||||||
Value::Float(f) => f.to_string(),
|
Value::Float(f) => f.to_string(),
|
||||||
Value::String(s) => s,
|
Value::String(s) => s,
|
||||||
_ => Err("value cannot be cast to 'string'")?,
|
_ => Err(InvalidValue("string"))?,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Instruction::Array(args) => Some(Value::Array(Rc::new(RefCell::new(
|
Instruction::Array(args) => Some(Value::Array(Rc::new(RefCell::new(
|
||||||
Instruction::run_all(args, ctx)?
|
Instruction::run_all(args, ctx)?.ok_or(InvalidValue("array"))?,
|
||||||
.ok_or("invalid child values in 'array' tag")?,
|
|
||||||
)))),
|
)))),
|
||||||
Instruction::Add(args) => {
|
Instruction::Add(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("add"))?;
|
||||||
.ok_or("invalid child values in 'add' tag")?;
|
|
||||||
Some(Instruction::add(vals)?)
|
Some(Instruction::add(vals)?)
|
||||||
}
|
}
|
||||||
Instruction::Subtract(args) => {
|
Instruction::Subtract(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("subtract"))?;
|
||||||
.ok_or("invalid child values in 'subtract' tag")?;
|
|
||||||
Some(Instruction::subtract(vals)?)
|
Some(Instruction::subtract(vals)?)
|
||||||
}
|
}
|
||||||
Instruction::Multiply(args) => {
|
Instruction::Multiply(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("multiply"))?;
|
||||||
.ok_or("invalid child values in 'multiply' tag")?;
|
|
||||||
Some(Instruction::multiply(vals)?)
|
Some(Instruction::multiply(vals)?)
|
||||||
}
|
}
|
||||||
Instruction::Divide(args) => {
|
Instruction::Divide(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("divide"))?;
|
||||||
.ok_or("invalid child values in 'divide' tag")?;
|
|
||||||
Some(Instruction::divide(vals)?)
|
Some(Instruction::divide(vals)?)
|
||||||
}
|
}
|
||||||
Instruction::And(args) => {
|
Instruction::And(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("and"))?;
|
||||||
.ok_or("invalid child values in 'and' tag")?;
|
|
||||||
Some(Instruction::and(vals))
|
Some(Instruction::and(vals))
|
||||||
}
|
}
|
||||||
Instruction::Or(args) => {
|
Instruction::Or(args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("or"))?;
|
||||||
.ok_or("invalid child values in 'or' tag")?;
|
|
||||||
Some(Instruction::or(vals))
|
Some(Instruction::or(vals))
|
||||||
}
|
}
|
||||||
Instruction::Not(arg) => Some(Value::Integer(
|
Instruction::Not(arg) => Some(Value::Integer(
|
||||||
if arg
|
if arg.run(ctx)?.ok_or(InvalidValue("not"))?.to_bool() {
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid child value in 'not' tag")?
|
|
||||||
.to_bool()
|
|
||||||
{
|
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
1
|
1
|
||||||
@@ -574,8 +551,8 @@ impl Instruction {
|
|||||||
)),
|
)),
|
||||||
Instruction::Equal(v1, v2) => Some(Value::Integer(
|
Instruction::Equal(v1, v2) => Some(Value::Integer(
|
||||||
if Instruction::compare(
|
if Instruction::compare(
|
||||||
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v1.run(ctx)?.ok_or(InvalidValue("equal"))?,
|
||||||
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v2.run(ctx)?.ok_or(InvalidValue("equal"))?,
|
||||||
)? == 0
|
)? == 0
|
||||||
{
|
{
|
||||||
1
|
1
|
||||||
@@ -585,8 +562,8 @@ impl Instruction {
|
|||||||
)),
|
)),
|
||||||
Instruction::Greater(v1, v2) => Some(Value::Integer(
|
Instruction::Greater(v1, v2) => Some(Value::Integer(
|
||||||
if Instruction::compare(
|
if Instruction::compare(
|
||||||
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v1.run(ctx)?.ok_or(InvalidValue("greater"))?,
|
||||||
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v2.run(ctx)?.ok_or(InvalidValue("greater"))?,
|
||||||
)? > 0
|
)? > 0
|
||||||
{
|
{
|
||||||
1
|
1
|
||||||
@@ -596,8 +573,8 @@ impl Instruction {
|
|||||||
)),
|
)),
|
||||||
Instruction::Lower(v1, v2) => Some(Value::Integer(
|
Instruction::Lower(v1, v2) => Some(Value::Integer(
|
||||||
if Instruction::compare(
|
if Instruction::compare(
|
||||||
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v1.run(ctx)?.ok_or(InvalidValue("lower"))?,
|
||||||
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
|
v2.run(ctx)?.ok_or(InvalidValue("lower"))?,
|
||||||
)? < 0
|
)? < 0
|
||||||
{
|
{
|
||||||
1
|
1
|
||||||
@@ -606,43 +583,38 @@ impl Instruction {
|
|||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Instruction::Call(fct_ins, args) => {
|
Instruction::Call(fct_ins, args) => {
|
||||||
let vals = Instruction::run_all(args, ctx)?
|
let vals = Instruction::run_all(args, ctx)?.ok_or(InvalidValue("call"))?;
|
||||||
.ok_or("invalid argument values in 'call' tag")?;
|
let fct_val = fct_ins.run(ctx)?.ok_or(InvalidValue("call"))?;
|
||||||
let fct_val = fct_ins
|
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid child function in 'call' tag")?;
|
|
||||||
if let Value::Function(f) = fct_val {
|
if let Value::Function(f) = fct_val {
|
||||||
f.run(vals, ctx)?
|
f.run(vals, ctx)?
|
||||||
} else if let Value::StdFunction(f) = fct_val {
|
} else if let Value::StdFunction(f) = fct_val {
|
||||||
f(vals)?
|
f(vals)?
|
||||||
} else {
|
} else {
|
||||||
Err("invalid function")?
|
Err(InvalidValue("call"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::CallNamed(fct_name, args) => {
|
Instruction::CallNamed(fct_name, args) => {
|
||||||
let vals: Vec<Value> = Instruction::run_all(args, ctx)?
|
let vals: Vec<Value> =
|
||||||
.ok_or("invalid argument values in 'or' tag")?;
|
Instruction::run_all(args, ctx)?.ok_or(InvalidValue("call"))?;
|
||||||
let fct_val = ctx.value(&fct_name).ok_or("unknown function")?;
|
let fct_val = ctx
|
||||||
|
.value(&fct_name)
|
||||||
|
.ok_or(UnknownVariable(fct_name.clone()))?;
|
||||||
if let Value::Function(f) = fct_val {
|
if let Value::Function(f) = fct_val {
|
||||||
let mut local = ctx.clone();
|
let mut local = ctx.clone();
|
||||||
f.run(vals, &mut local)?
|
f.run(vals, &mut local)?
|
||||||
} else if let Value::StdFunction(f) = fct_val {
|
} else if let Value::StdFunction(f) = fct_val {
|
||||||
f(vals)?
|
f(vals)?
|
||||||
} else {
|
} else {
|
||||||
Err("invalid function")?
|
Err(InvalidValue("call"))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::Return(ins) => {
|
Instruction::Return(ins) => {
|
||||||
let v = ins.run(ctx)?.ok_or("invalid child value in 'return' tag")?;
|
let v = ins.run(ctx)?.ok_or(InvalidValue("return"))?;
|
||||||
ctx.assign(String::from("__return"), v);
|
ctx.assign(String::from("__return"), v);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Instruction::If(cond, then) => {
|
Instruction::If(cond, then) => {
|
||||||
if cond
|
if cond.run(ctx)?.ok_or(InvalidValue("if"))?.to_bool() {
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid condition value in 'if' tag")?
|
|
||||||
.to_bool()
|
|
||||||
{
|
|
||||||
for i in then {
|
for i in then {
|
||||||
i.run(ctx)?;
|
i.run(ctx)?;
|
||||||
}
|
}
|
||||||
@@ -650,11 +622,7 @@ impl Instruction {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
Instruction::IfElse(cond, then, els) => {
|
Instruction::IfElse(cond, then, els) => {
|
||||||
if cond
|
if cond.run(ctx)?.ok_or(InvalidValue("if"))?.to_bool() {
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid condition value in 'if' tag")?
|
|
||||||
.to_bool()
|
|
||||||
{
|
|
||||||
for i in then {
|
for i in then {
|
||||||
i.run(ctx)?;
|
i.run(ctx)?;
|
||||||
}
|
}
|
||||||
@@ -672,15 +640,9 @@ impl Instruction {
|
|||||||
step,
|
step,
|
||||||
body,
|
body,
|
||||||
} => {
|
} => {
|
||||||
if let Value::Integer(f) =
|
if let Value::Integer(f) = from.run(ctx)?.ok_or(InvalidValue("for"))? {
|
||||||
from.run(ctx)?.ok_or("invalid 'from' value in 'for' tag")?
|
if let Value::Integer(t) = to.run(ctx)?.ok_or(InvalidValue("for"))? {
|
||||||
{
|
if let Value::Integer(s) = step.run(ctx)?.ok_or(InvalidValue("for"))? {
|
||||||
if let Value::Integer(t) =
|
|
||||||
to.run(ctx)?.ok_or("invalid 'to' value in 'for' tag")?
|
|
||||||
{
|
|
||||||
if let Value::Integer(s) =
|
|
||||||
step.run(ctx)?.ok_or("invalid 'from' value in 'for' tag")?
|
|
||||||
{
|
|
||||||
for i in (f..t).step_by(s.try_into()?) {
|
for i in (f..t).step_by(s.try_into()?) {
|
||||||
ctx.assign(variable.clone(), Value::Integer(i));
|
ctx.assign(variable.clone(), Value::Integer(i));
|
||||||
for ins in body {
|
for ins in body {
|
||||||
@@ -693,10 +655,7 @@ impl Instruction {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
Instruction::Each(variable, array_ins, body) => {
|
Instruction::Each(variable, array_ins, body) => {
|
||||||
if let Value::Array(v) = array_ins
|
if let Value::Array(v) = array_ins.run(ctx)?.ok_or(InvalidValue("each"))? {
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid array value in 'each' tag")?
|
|
||||||
{
|
|
||||||
for i in v.borrow().iter() {
|
for i in v.borrow().iter() {
|
||||||
ctx.assign(variable.clone(), i.clone());
|
ctx.assign(variable.clone(), i.clone());
|
||||||
for ins in body {
|
for ins in body {
|
||||||
@@ -704,16 +663,12 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array")?
|
Err(InvalidValue("each"))?
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Instruction::While(cond, body) => {
|
Instruction::While(cond, body) => {
|
||||||
while cond
|
while cond.run(ctx)?.ok_or(InvalidValue("while"))?.to_bool() {
|
||||||
.run(ctx)?
|
|
||||||
.ok_or("invalid condition value in 'while' tag")?
|
|
||||||
.to_bool()
|
|
||||||
{
|
|
||||||
for ins in body {
|
for ins in body {
|
||||||
ins.run(ctx)?;
|
ins.run(ctx)?;
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/lib.rs
25
src/lib.rs
@@ -1,15 +1,17 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use roxmltree::{Document, Node};
|
use roxmltree::Document;
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
|
mod error;
|
||||||
mod instruction;
|
mod instruction;
|
||||||
mod stl;
|
mod stl;
|
||||||
mod util;
|
mod util;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use context::Context;
|
use context::Context;
|
||||||
|
use error::{InvalidProgram, MissingChild, Unnamed};
|
||||||
use instruction::Instruction;
|
use instruction::Instruction;
|
||||||
use value::{Function, Value};
|
use value::{Function, Value};
|
||||||
|
|
||||||
@@ -23,33 +25,22 @@ pub fn run(filename: &str) -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let main = root
|
let main = root
|
||||||
.first_element_child()
|
.first_element_child()
|
||||||
.ok_or("invalid program structure")?
|
.ok_or(InvalidProgram)?
|
||||||
.children()
|
.children()
|
||||||
.find(|node| util::tag_name(&node) == "main")
|
.find(|node| util::tag_name(&node) == "main")
|
||||||
.ok_or("No 'main' block")?;
|
.ok_or(MissingChild("program", "main"))?;
|
||||||
let main_ast = Instruction::from_children(main)?;
|
let main_ast = Instruction::from_children(main)?;
|
||||||
|
|
||||||
let functions = root
|
let functions = root
|
||||||
.first_element_child()
|
.first_element_child()
|
||||||
.ok_or("invalid program structure")?
|
.ok_or(InvalidProgram)?
|
||||||
.children()
|
.children()
|
||||||
.filter(|node| node.tag_name().name() == String::from("function"));
|
.filter(|node| node.tag_name().name() == String::from("function"));
|
||||||
|
|
||||||
for fun in functions {
|
for fun in functions {
|
||||||
ctx.assign(
|
ctx.assign(
|
||||||
String::from(fun.attribute("name").ok_or("unnamed function")?),
|
String::from(fun.attribute("name").ok_or(Unnamed("function"))?),
|
||||||
Value::Function(Function {
|
Value::Function(Function::from(&fun)?),
|
||||||
args: util::find_node(&fun, "arguments")
|
|
||||||
.ok_or("missing 'arguments' block in 'call' tag")?
|
|
||||||
.children()
|
|
||||||
.filter(Node::is_element)
|
|
||||||
.map(|n| n.attribute("name").and_then(|s| Some(String::from(s))))
|
|
||||||
.collect::<Option<Vec<String>>>()
|
|
||||||
.ok_or("unnamed argument")?,
|
|
||||||
ins: Instruction::from_children(
|
|
||||||
util::find_node(&fun, "body").ok_or("missing 'body' block in 'call' tag")?,
|
|
||||||
)?,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/stl.rs
57
src/stl.rs
@@ -1,3 +1,4 @@
|
|||||||
|
use super::error::{BadArgumentCount, InvalidArgument};
|
||||||
use super::{Context, Value};
|
use super::{Context, Value};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@@ -34,7 +35,7 @@ fn print(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
};
|
};
|
||||||
Ok(Some(vals[0].clone()))
|
Ok(Some(vals[0].clone()))
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'print'")?
|
Err(BadArgumentCount("print", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ fn input(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
line.pop();
|
line.pop();
|
||||||
Ok(Some(Value::String(line)))
|
Ok(Some(Value::String(line)))
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'input'")?
|
Err(BadArgumentCount("input", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +62,13 @@ fn string_split(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
v.pop();
|
v.pop();
|
||||||
Ok(Some(Value::Array(Rc::new(RefCell::new(v)))))
|
Ok(Some(Value::Array(Rc::new(RefCell::new(v)))))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid delimiter string in call to 'string-split'")?
|
Err(InvalidArgument("string-split", "delimiter").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid target string in call to 'string-split'")?
|
Err(InvalidArgument("string-split", "target").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'string-split'")?
|
Err(BadArgumentCount("string-split", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +81,16 @@ fn array_set(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
v.borrow_mut()[index] = vals[2].clone();
|
v.borrow_mut()[index] = vals[2].clone();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err("index out of array in call to 'array-set'")?
|
Err(InvalidArgument("array-set", "index").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid index in call to 'array-set'")?
|
Err(InvalidArgument("array-set", "index").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array in call to 'array-set'")?
|
Err(InvalidArgument("array-set", "array").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'array-set'")?
|
Err(BadArgumentCount("array-set", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +100,10 @@ fn array_push(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
v.borrow_mut().push(vals[1].clone());
|
v.borrow_mut().push(vals[1].clone());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array in call to 'array-push'")?
|
Err(InvalidArgument("array-push", "array").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'array-push'")?
|
Err(BadArgumentCount("array-push", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,13 +113,13 @@ fn array_pop(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
Ok(Some(
|
Ok(Some(
|
||||||
v.borrow_mut()
|
v.borrow_mut()
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or("empty array in call to 'array-pop'")?,
|
.ok_or(InvalidArgument("array-pop", "array"))?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array in call to 'array-pop'")?
|
Err(InvalidArgument("array-pop", "array").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'array-pop'")?
|
Err(BadArgumentCount("array-pop", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,16 +131,16 @@ fn array_get(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
if v.borrow().len() > index {
|
if v.borrow().len() > index {
|
||||||
Ok(Some(v.borrow_mut()[index].clone()))
|
Ok(Some(v.borrow_mut()[index].clone()))
|
||||||
} else {
|
} else {
|
||||||
Err("index out of array in call to 'array-get'")?
|
Err(InvalidArgument("array-get", "index").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid index in call to 'array-get'")?
|
Err(InvalidArgument("array-get", "index").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array in call to 'array-get'")?
|
Err(InvalidArgument("array-set", "array").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'array-get'")?
|
Err(BadArgumentCount("array-get", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,26 +149,26 @@ fn array_length(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
if let Value::Array(v) = &vals[0] {
|
if let Value::Array(v) = &vals[0] {
|
||||||
Ok(Some(Value::Integer(v.borrow().len() as i64)))
|
Ok(Some(Value::Integer(v.borrow().len() as i64)))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid array in call to 'array-length'")?
|
Err(InvalidArgument("array-length", "array").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'array-length'")?
|
Err(BadArgumentCount("array-length", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_ascii(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
fn to_ascii(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
||||||
if vals.len() == 1 {
|
if vals.len() == 1 {
|
||||||
if let Value::Integer(i) = &vals[0] {
|
if let Value::Integer(i) = &vals[0] {
|
||||||
if i <= &255 {
|
if &0 <= i && i <= &255 {
|
||||||
Ok(Some(Value::String(String::from_utf8(vec![*i as u8])?)))
|
Ok(Some(Value::String(String::from_utf8(vec![*i as u8])?)))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid integer in call to 'to-ascii'")?
|
Err(InvalidArgument("to-ascii", "integer").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid argument in call to 'to-ascii'")?
|
Err(InvalidArgument("to-ascii", "integer").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'to-ascii'")?
|
Err(BadArgumentCount("to-ascii", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,13 +178,13 @@ fn from_ascii(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
if s.len() == 1 {
|
if s.len() == 1 {
|
||||||
Ok(Some(Value::Integer(s.as_bytes()[0] as i64)))
|
Ok(Some(Value::Integer(s.as_bytes()[0] as i64)))
|
||||||
} else {
|
} else {
|
||||||
Err("invalid string in call to 'from-ascii'")?
|
Err(InvalidArgument("from-ascii", "string").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("invalid argument in call to 'from-ascii'")?
|
Err(InvalidArgument("from-ascii", "string").into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'from-ascii'")?
|
Err(BadArgumentCount("from-ascii", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +197,6 @@ fn get_args(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)))))
|
)))))
|
||||||
} else {
|
} else {
|
||||||
Err("bad argument count in call to 'get-args'")?
|
Err(BadArgumentCount("get-args", vals.len()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/value.rs
21
src/value.rs
@@ -1,4 +1,6 @@
|
|||||||
use super::{Context, Instruction};
|
use super::error::{BadArgumentCount, MissingChild, Unnamed};
|
||||||
|
use super::{util, Context, Instruction};
|
||||||
|
use roxmltree::Node;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -16,7 +18,7 @@ impl Function {
|
|||||||
ctx: &mut Context,
|
ctx: &mut Context,
|
||||||
) -> Result<Option<Value>, Box<dyn Error>> {
|
) -> Result<Option<Value>, Box<dyn Error>> {
|
||||||
if args.len() != self.args.len() {
|
if args.len() != self.args.len() {
|
||||||
Err("not enough arguments")?;
|
Err(BadArgumentCount("function", args.len()))?
|
||||||
}
|
}
|
||||||
self.args
|
self.args
|
||||||
.iter()
|
.iter()
|
||||||
@@ -27,6 +29,21 @@ impl Function {
|
|||||||
}
|
}
|
||||||
Ok(ctx.take(&String::from("__return")))
|
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").and_then(|s| Some(String::from(s))))
|
||||||
|
.collect::<Option<Vec<String>>>()
|
||||||
|
.ok_or(Unnamed("argument"))?,
|
||||||
|
ins: Instruction::from_children(
|
||||||
|
util::find_node(&fun, "body").ok_or(MissingChild("call", "body"))?,
|
||||||
|
)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
Reference in New Issue
Block a user