commit 13c3840d62053133907be574f3ae27726d4509fe
Author: Altareos <8584797+Altareos@users.noreply.github.com>
Date: Tue Feb 22 17:24:36 2022 +0100
basic functionality
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ac7dfac
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,25 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "plxml"
+version = "0.1.0"
+dependencies = [
+ "roxmltree",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
+dependencies = [
+ "xmlparser",
+]
+
+[[package]]
+name = "xmlparser"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..55eba73
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "plxml"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+roxmltree = "0.14.1"
\ No newline at end of file
diff --git a/arrays.pl.xml b/arrays.pl.xml
new file mode 100644
index 0000000..8beebcc
--- /dev/null
+++ b/arrays.pl.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/if.pl.xml b/if.pl.xml
new file mode 100644
index 0000000..a081857
--- /dev/null
+++ b/if.pl.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..82a4eeb
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,745 @@
+use roxmltree::{Document, Node};
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::error::Error;
+use std::fs;
+use std::rc::Rc;
+
+mod util {
+ use super::{Error, Node};
+ pub fn tag_name(node: &Node) -> String {
+ node.tag_name().name().to_lowercase()
+ }
+
+ pub fn find_node<'a>(
+ node: &'a Node<'a, 'a>,
+ tag: &str,
+ ) -> Result, Box> {
+ Ok(node
+ .children()
+ .find(|n| tag_name(n) == tag)
+ .ok_or(format!("node '{}' not found", tag))?)
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum Instruction {
+ Value(String),
+ Assign(String, Box),
+ Integer(String),
+ Float(String),
+ String(String),
+ Array(Vec),
+ Add(Vec),
+ Subtract(Vec),
+ Multiply(Vec),
+ Divide(Vec),
+ And(Vec),
+ Or(Vec),
+ Not(Box),
+ Call(Box, Vec),
+ CallNamed(String, Vec),
+ Return(Box),
+ If(Box, Vec),
+ IfElse(Box, Vec, Vec),
+ For {
+ variable: String,
+ from: Box,
+ to: Box,
+ step: Box,
+ body: Vec,
+ },
+ Each(String, Box, Vec),
+ While(Box, Vec),
+ Print(Box),
+ AssignArray(Box, Box, Box),
+ InsertArray(Box, Box),
+}
+
+impl Instruction {
+ pub fn new(node: Node) -> Result> {
+ // println!("parsing '{}'", util::tag_name(&node));
+ Ok(match util::tag_name(&node).as_str() {
+ "value" => Instruction::Value(
+ node.attribute("variable")
+ .and_then(|a| Some(String::from(a)))
+ .ok_or("missing 'variable' attribute on 'value' tag")?,
+ ),
+ "assign" => Instruction::Assign(
+ String::from(
+ node.attribute("variable")
+ .ok_or("missing 'variable' attribute on 'assign' tag")?,
+ ),
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing child on 'assign' tag")?,
+ )?),
+ ),
+ "integer" => Instruction::Integer(
+ node.attribute("value")
+ .ok_or("missing 'value' attribute on 'integer' tag")?
+ .parse()?,
+ ),
+ "float" => Instruction::Float(
+ node.attribute("value")
+ .ok_or("missing 'value' attribute on 'float' tag")?
+ .parse()?,
+ ),
+ "string" => Instruction::String(String::from(
+ node.attribute("value")
+ .ok_or("missing 'value' attribute on 'string' tag")?,
+ )),
+ "array" => Instruction::Array(Instruction::from_children(node)?),
+ "add" => Instruction::Add(Instruction::from_children(node)?),
+ "subtract" => Instruction::Subtract(Instruction::from_children(node)?),
+ "multiply" => Instruction::Multiply(Instruction::from_children(node)?),
+ "divide" => Instruction::Divide(Instruction::from_children(node)?),
+ "and" => Instruction::And(Instruction::from_children(node)?),
+ "or" => Instruction::Or(Instruction::from_children(node)?),
+ "not" => Instruction::Not(Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing value child element in 'not' tag")?,
+ )?)),
+ "call" => {
+ if let Some(function) = node.attribute("function") {
+ Instruction::CallNamed(
+ String::from(function),
+ Instruction::from_children(util::find_node(&node, "arguments")?)?,
+ )
+ } else {
+ Instruction::Call(
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing function child element in 'call' tag")?,
+ )?),
+ Instruction::from_children(util::find_node(&node, "arguments")?)?,
+ )
+ }
+ }
+ "return" => Instruction::Return(Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing value child element in 'return' tag")?,
+ )?)),
+ "if" => {
+ if let Some(else_node) = node.children().find(|n| util::tag_name(n) == "else") {
+ Instruction::IfElse(
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing condition child element in 'if' tag")?,
+ )?),
+ Instruction::from_children(util::find_node(&node, "then")?)?,
+ Instruction::from_children(else_node)?,
+ )
+ } else {
+ Instruction::If(
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing condition child element in 'if' tag")?,
+ )?),
+ Instruction::from_children(util::find_node(&node, "then")?)?,
+ )
+ }
+ }
+ "for" => Instruction::For {
+ variable: String::from(
+ node.attribute("variable")
+ .ok_or("missing 'variable' attribute on 'for' tag")?,
+ ),
+ from: Box::new(Instruction::new(
+ util::find_node(&node, "from")?
+ .first_element_child()
+ .ok_or("missing 'from' child in 'for' tag")?,
+ )?),
+ to: Box::new(Instruction::new(
+ util::find_node(&node, "to")?
+ .first_element_child()
+ .ok_or("missing 'to' child in 'for' tag")?,
+ )?),
+ step: Box::new(Instruction::new(
+ util::find_node(&node, "step")?
+ .first_element_child()
+ .ok_or("missing 'step' child in 'for' tag")?,
+ )?),
+ body: Instruction::from_children(util::find_node(&node, "do")?)?,
+ },
+ "each" => Instruction::Each(
+ String::from(
+ node.attribute("variable")
+ .ok_or("missing 'variable' attribute on 'each' tag")?,
+ ),
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing array child element in 'for' tag")?,
+ )?),
+ Instruction::from_children(util::find_node(&node, "do")?)?,
+ ),
+ "while" => Instruction::While(
+ Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing condition child element in 'while' tag")?,
+ )?),
+ Instruction::from_children(util::find_node(&node, "do")?)?,
+ ),
+ "print" => Instruction::Print(Box::new(Instruction::new(
+ node.first_element_child()
+ .ok_or("missing value child element in 'print' tag")?,
+ )?)),
+ "assign-array" => Instruction::AssignArray(
+ Box::new(Instruction::new(
+ util::find_node(&node, "array")?
+ .first_element_child()
+ .ok_or("missing 'array' in 'assign-array' tag")?,
+ )?),
+ Box::new(Instruction::new(
+ util::find_node(&node, "index")?
+ .first_element_child()
+ .ok_or("missing 'index' in 'assign-array' tag")?,
+ )?),
+ Box::new(Instruction::new(
+ util::find_node(&node, "value")?
+ .first_element_child()
+ .ok_or("missing 'value' in 'assign-array' tag")?,
+ )?),
+ ),
+ "insert-array" => Instruction::InsertArray(
+ Box::new(Instruction::new(
+ util::find_node(&node, "array")?
+ .first_element_child()
+ .ok_or("missing 'array' in 'insert-array' tag")?,
+ )?),
+ Box::new(Instruction::new(
+ util::find_node(&node, "value")?
+ .first_element_child()
+ .ok_or("missing 'value' in 'insert-array' tag")?,
+ )?),
+ ),
+ tag => Err(format!("unknown tag '{}'", tag))?,
+ })
+ }
+
+ pub fn from_children(node: Node) -> Result, Box> {
+ node.children()
+ .filter(Node::is_element)
+ .map(Instruction::new)
+ .collect()
+ }
+
+ fn add(vals: Vec) -> Result> {
+ if vals.iter().all(|v| matches!(v, Value::Integer(_))) {
+ Ok(Value::Integer(
+ vals.iter()
+ .map(|v| {
+ if let Value::Integer(i) = v {
+ Ok(i)
+ } else {
+ Err("invalid value")?
+ }
+ })
+ .sum::>>()?,
+ ))
+ } else if vals
+ .iter()
+ .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
+ {
+ Ok(Value::Float(
+ vals.iter()
+ .map(|v| {
+ if let Value::Integer(i) = v {
+ Ok(*i as f64)
+ } else if let Value::Float(f) = v {
+ Ok(*f)
+ } else {
+ Err("invalid value")?
+ }
+ })
+ .sum::>>()?,
+ ))
+ } else if vals.iter().all(|v| {
+ matches!(v, Value::Integer(_))
+ || matches!(v, Value::Float(_))
+ || matches!(v, Value::String(_))
+ }) {
+ Ok(Value::String(
+ vals.iter()
+ .map(|v| {
+ Ok(if let Value::String(s) = v {
+ s.to_string()
+ } else if let Value::Integer(i) = v {
+ i.to_string()
+ } else if let Value::Float(f) = v {
+ f.to_string()
+ } else {
+ Err("invalid value")?
+ })
+ })
+ .collect::, Box>>()?
+ .join(""),
+ ))
+ } else {
+ Err("invalid value")?
+ }
+ }
+
+ fn subtract(vals: Vec) -> Result> {
+ Ok(if vals.iter().all(|v| matches!(v, Value::Integer(_))) {
+ let first = match vals.first().ok_or("missing values in 'subtract' tag")? {
+ Value::Integer(i) => i,
+ _ => Err("invalid value")?,
+ };
+ Value::Integer(
+ *first
+ - vals
+ .iter()
+ .skip(1)
+ .map(|v| {
+ if let Value::Integer(i) = v {
+ Ok(i)
+ } else {
+ Err("invalid value")?
+ }
+ })
+ .sum::>>()?,
+ )
+ } else if vals
+ .iter()
+ .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
+ {
+ let first = match vals.first().ok_or("not enough values in 'subtract' tag")? {
+ Value::Integer(v) => *v as f64,
+ Value::Float(v) => *v,
+ _ => Err("invalid value")?,
+ };
+ Value::Float(
+ first
+ - vals
+ .iter()
+ .skip(1)
+ .map(|val| {
+ Ok(match val {
+ Value::Integer(v) => *v as f64,
+ Value::Float(v) => *v,
+ _ => Err("invalid value")?,
+ })
+ })
+ .sum::>>()?,
+ )
+ } else {
+ Err("invalid value")?
+ })
+ }
+
+ fn multiply(vals: Vec) -> Result> {
+ if vals.iter().all(|v| matches!(v, Value::Integer(_))) {
+ Ok(Value::Integer(
+ vals.iter()
+ .map(|v| {
+ if let Value::Integer(i) = v {
+ Ok(i)
+ } else {
+ Err("invalid value")?
+ }
+ })
+ .product::>>()?,
+ ))
+ } else if vals
+ .iter()
+ .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
+ {
+ Ok(Value::Float(
+ vals.iter()
+ .map(|val| {
+ Ok(match val {
+ Value::Integer(v) => *v as f64,
+ Value::Float(v) => *v,
+ _ => Err("invalid value")?,
+ })
+ })
+ .product::>>()?,
+ ))
+ } else {
+ Err("invalid value")?
+ }
+ }
+
+ fn divide(vals: Vec) -> Result> {
+ if vals
+ .iter()
+ .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Float(_)))
+ {
+ let first = match vals.first().ok_or("not enough values in 'divide' tag")? {
+ Value::Integer(v) => *v as f64,
+ Value::Float(v) => *v,
+ _ => Err("invalid value")?,
+ };
+ Ok(Value::Float(
+ first
+ * vals
+ .iter()
+ .skip(1)
+ .map(|val| {
+ Ok(match val {
+ Value::Integer(v) => 1.0 / (*v as f64),
+ Value::Float(v) => 1.0 / *v,
+ _ => Err("invalid value")?,
+ })
+ })
+ .product::>>()?,
+ ))
+ } else {
+ Err("invalid value")?
+ }
+ }
+
+ fn and(vals: Vec) -> Value {
+ Value::Integer(if vals.iter().all(Value::to_bool) {
+ 1
+ } else {
+ 0
+ })
+ }
+
+ fn or(vals: Vec) -> Value {
+ Value::Integer(if vals.iter().any(Value::to_bool) {
+ 1
+ } else {
+ 0
+ })
+ }
+
+ fn run_all(
+ ins: &Vec,
+ ctx: &mut Context,
+ ) -> Result