error handling

This commit is contained in:
2022-03-05 17:26:52 +01:00
parent 75740beaf8
commit cb4fda6631
5 changed files with 276 additions and 196 deletions

116
src/error.rs Normal file
View 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 {}

View File

@@ -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 Ordering::Less => -1,
.partial_cmp(&f2) Ordering::Equal => 0,
.ok_or("incompatible comparison values")? Ordering::Greater => 1,
{ }),
Ordering::Less => -1, _ => Err(IncompatibleValues)?,
Ordering::Equal => 0,
Ordering::Greater => 1,
},
),
_ => 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)?;
} }

View File

@@ -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")?,
)?,
}),
); );
} }

View File

@@ -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())
} }
} }

View File

@@ -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)]