From 0e5a9acccfc0e8e39fd096b84c5982f32ada84c4 Mon Sep 17 00:00:00 2001 From: Altareos <8584797+Altareos@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:01:09 +0100 Subject: [PATCH] 0.1.1: structural reworks + dep version bump --- Cargo.lock | 15 +- Cargo.toml | 5 +- src/bin/compiler.rs | 6 + src/bin/interpreter.rs | 6 + src/compiler/compile.rs | 42 +++ src/compiler/mod.rs | 1 + src/instruction.rs | 484 +-------------------------- src/{ => interpreter}/context.rs | 2 +- src/interpreter/mod.rs | 4 + src/interpreter/run.rs | 538 +++++++++++++++++++++++++++++++ src/{ => interpreter}/stl.rs | 6 +- src/{ => interpreter}/value.rs | 8 +- src/lib.rs | 57 +--- src/main.rs | 9 - 14 files changed, 619 insertions(+), 564 deletions(-) create mode 100644 src/bin/compiler.rs create mode 100644 src/bin/interpreter.rs create mode 100644 src/compiler/compile.rs create mode 100644 src/compiler/mod.rs rename src/{ => interpreter}/context.rs (94%) create mode 100644 src/interpreter/mod.rs create mode 100644 src/interpreter/run.rs rename src/{ => interpreter}/stl.rs (98%) rename src/{ => interpreter}/value.rs (90%) delete mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ac7dfac..57c3c88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,22 +4,13 @@ version = 3 [[package]] name = "plxml" -version = "0.1.0" +version = "0.1.1" dependencies = [ "roxmltree", ] [[package]] name = "roxmltree" -version = "0.14.1" +version = "0.19.0" 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" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" diff --git a/Cargo.toml b/Cargo.toml index 55eba73..88b265e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "plxml" -version = "0.1.0" +version = "0.1.1" edition = "2021" +default-run = "interpreter" # 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 +roxmltree = "0.19" \ No newline at end of file diff --git a/src/bin/compiler.rs b/src/bin/compiler.rs new file mode 100644 index 0000000..fea0496 --- /dev/null +++ b/src/bin/compiler.rs @@ -0,0 +1,6 @@ +fn main() { + let args: Vec = std::env::args().collect(); + if let Err(e) = plxml::compile_file(&args[1]) { + eprintln!("Error occurred: {}", e); + } +} diff --git a/src/bin/interpreter.rs b/src/bin/interpreter.rs new file mode 100644 index 0000000..a7eccef --- /dev/null +++ b/src/bin/interpreter.rs @@ -0,0 +1,6 @@ +fn main() { + let args: Vec = std::env::args().collect(); + if let Err(e) = plxml::run_file(&args[1]) { + eprintln!("Error occurred: {}", e); + } +} diff --git a/src/compiler/compile.rs b/src/compiler/compile.rs new file mode 100644 index 0000000..828a291 --- /dev/null +++ b/src/compiler/compile.rs @@ -0,0 +1,42 @@ +use std::error::Error; +use std::fs; + +use roxmltree::Document; + +use crate::error::{InvalidProgram, MissingChild}; +use crate::instruction::Instruction; +use crate::util; + +pub fn compile_file(filename: &str) -> Result<(), Box> { + let contents = fs::read_to_string(filename)?; + compile(contents) +} + +trait Compile { + fn compile(&self); +} + +impl Compile for Instruction { + fn compile(&self) { + unimplemented!() + } +} + +pub fn compile(program: String) -> Result<(), Box> { + let doc = Document::parse(&program)?; + let root = doc.root(); + + let main = root + .first_element_child() + .ok_or(InvalidProgram)? + .children() + .find(|node| util::tag_name(&node) == "main") + .ok_or(MissingChild("program", "main"))?; + let main_ast = Instruction::from_children(main)?; + + for ins in main_ast { + ins.compile(); + } + + Ok(()) +} diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs new file mode 100644 index 0000000..0ac8652 --- /dev/null +++ b/src/compiler/mod.rs @@ -0,0 +1 @@ +pub mod compile; diff --git a/src/instruction.rs b/src/instruction.rs index f170654..42d5b30 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,12 +1,7 @@ -use super::error::{ - BadChildCount, IncompatibleValues, InvalidValue, MissingAttribute, MissingChild, - UnknownVariable, -}; -use super::{util, Context, Value}; +use super::error::{BadChildCount, MissingAttribute, MissingChild}; +use crate::util; use roxmltree::Node; -use std::cell::RefCell; use std::error::Error; -use std::rc::Rc; #[derive(Clone, Debug)] pub enum Instruction { @@ -254,479 +249,4 @@ impl Instruction { .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(InvalidValue("add"))? - } - }) - .sum::>>()?, - )) - } else if vals - .iter() - .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) - { - Ok(Value::Real( - vals.iter() - .map(|v| { - if let Value::Integer(i) = v { - Ok(*i as f64) - } else if let Value::Real(f) = v { - Ok(*f) - } else { - Err(InvalidValue("add"))? - } - }) - .sum::>>()?, - )) - } else if vals.iter().all(|v| { - matches!(v, Value::Integer(_)) - || matches!(v, Value::Real(_)) - || 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::Real(f) = v { - f.to_string() - } else { - Err(InvalidValue("add"))? - }) - }) - .collect::, Box>>()? - .join(""), - )) - } else { - Err(InvalidValue("add"))? - } - } - - fn subtract(vals: Vec) -> Result> { - Ok(if vals.iter().all(|v| matches!(v, Value::Integer(_))) { - let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? { - Value::Integer(i) => i, - _ => Err(InvalidValue("subtract"))?, - }; - Value::Integer( - *first - - vals - .iter() - .skip(1) - .map(|v| { - if let Value::Integer(i) = v { - Ok(i) - } else { - Err(InvalidValue("subtract"))? - } - }) - .sum::>>()?, - ) - } else if vals - .iter() - .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) - { - let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? { - Value::Integer(v) => *v as f64, - Value::Real(v) => *v, - _ => Err(InvalidValue("subtract"))?, - }; - Value::Real( - first - - vals - .iter() - .skip(1) - .map(|val| { - Ok(match val { - Value::Integer(v) => *v as f64, - Value::Real(v) => *v, - _ => Err(InvalidValue("subtract"))?, - }) - }) - .sum::>>()?, - ) - } else { - Err(InvalidValue("subtract"))? - }) - } - - 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(InvalidValue("multiply"))? - } - }) - .product::>>()?, - )) - } else if vals - .iter() - .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) - { - Ok(Value::Real( - vals.iter() - .map(|val| { - Ok(match val { - Value::Integer(v) => *v as f64, - Value::Real(v) => *v, - _ => Err(InvalidValue("multiply"))?, - }) - }) - .product::>>()?, - )) - } else { - Err(InvalidValue("multiply"))? - } - } - - fn divide(vals: Vec) -> Result> { - if vals - .iter() - .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) - { - let first = match vals.first().ok_or(BadChildCount("divide", 0))? { - Value::Integer(v) => *v as f64, - Value::Real(v) => *v, - _ => Err(InvalidValue("divide"))?, - }; - Ok(Value::Real( - first - * vals - .iter() - .skip(1) - .map(|val| { - Ok(match val { - Value::Integer(v) => 1.0 / (*v as f64), - Value::Real(v) => 1.0 / *v, - _ => Err(InvalidValue("divide"))?, - }) - }) - .product::>>()?, - )) - } else { - Err(InvalidValue("divide"))? - } - } - - 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 compare(v1: Value, v2: Value) -> Result> { - use std::cmp::Ordering; - match v1 { - Value::Integer(i1) => match v2 { - Value::Integer(i2) => Ok(i1 - i2), - Value::Real(f2) => Ok( - match (i1 as f64).partial_cmp(&f2).ok_or(IncompatibleValues)? { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - }, - ), - _ => Err(IncompatibleValues)?, - }, - Value::Real(f1) => match v2 { - Value::Integer(i2) => Ok( - match f1.partial_cmp(&(i2 as f64)).ok_or(IncompatibleValues)? { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - }, - ), - Value::Real(f2) => Ok(match f1.partial_cmp(&f2).ok_or(IncompatibleValues)? { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - }), - _ => Err(IncompatibleValues)?, - }, - Value::String(s1) => { - if let Value::String(s2) = v2 { - Ok(match s1.cmp(&s2) { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - }) - } else { - Err(IncompatibleValues)? - } - } - _ => Err(IncompatibleValues)?, - } - } - - fn run_all( - ins: &Vec, - ctx: &mut Context, - globals: &Context, - ) -> Result>, Box> { - ins.iter().map(|i| i.run(ctx, globals)).collect() - } - - pub fn run( - &self, - ctx: &mut Context, - globals: &Context, - ) -> Result, Box> { - Ok(if let None = ctx.value(&String::from("__return")) { - match self { - Instruction::Value(key) => { - Some(match ctx.value(key).ok_or(UnknownVariable(key.clone()))? { - Value::Array(vecrc) => Value::Array(Rc::clone(vecrc)), - val => val.clone(), - }) - } - Instruction::Assign(key, ins) => { - let v = ins.run(ctx, globals)?.ok_or(InvalidValue("assign"))?; - ctx.assign(key.clone(), v); - None - } - Instruction::Integer(val) => Some(Value::Integer(val.parse()?)), - Instruction::IntegerCast(ins) => Some(Value::Integer( - match ins.run(ctx, globals)?.ok_or(InvalidValue("integer"))? { - Value::Integer(i) => i, - Value::Real(f) => f as i64, - Value::String(s) => s.parse()?, - _ => Err(InvalidValue("integer"))?, - }, - )), - Instruction::Real(val) => Some(Value::Real(val.parse()?)), - Instruction::RealCast(ins) => Some(Value::Real( - match ins.run(ctx, globals)?.ok_or(InvalidValue("real"))? { - Value::Integer(i) => i as f64, - Value::Real(f) => f, - Value::String(s) => s.parse()?, - _ => Err(InvalidValue("real"))?, - }, - )), - Instruction::String(val) => Some(Value::String(val.clone())), - Instruction::StringCast(ins) => Some(Value::String( - match ins.run(ctx, globals)?.ok_or(InvalidValue("string"))? { - Value::Integer(i) => i.to_string(), - Value::Real(f) => f.to_string(), - Value::String(s) => s, - _ => Err(InvalidValue("string"))?, - }, - )), - Instruction::Array(args) => Some(Value::Array(Rc::new(RefCell::new( - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("array"))?, - )))), - Instruction::Add(args) => { - let vals = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("add"))?; - Some(Instruction::add(vals)?) - } - Instruction::Subtract(args) => { - let vals = Instruction::run_all(args, ctx, globals)? - .ok_or(InvalidValue("subtract"))?; - Some(Instruction::subtract(vals)?) - } - Instruction::Multiply(args) => { - let vals = Instruction::run_all(args, ctx, globals)? - .ok_or(InvalidValue("multiply"))?; - Some(Instruction::multiply(vals)?) - } - Instruction::Divide(args) => { - let vals = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("divide"))?; - Some(Instruction::divide(vals)?) - } - Instruction::And(args) => { - let vals = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("and"))?; - Some(Instruction::and(vals)) - } - Instruction::Or(args) => { - let vals = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("or"))?; - Some(Instruction::or(vals)) - } - Instruction::Not(arg) => Some(Value::Integer( - if arg.run(ctx, globals)?.ok_or(InvalidValue("not"))?.to_bool() { - 0 - } else { - 1 - }, - )), - Instruction::Equal(v1, v2) => Some(Value::Integer( - if Instruction::compare( - v1.run(ctx, globals)?.ok_or(InvalidValue("equal"))?, - v2.run(ctx, globals)?.ok_or(InvalidValue("equal"))?, - )? == 0 - { - 1 - } else { - 0 - }, - )), - Instruction::Greater(v1, v2) => Some(Value::Integer( - if Instruction::compare( - v1.run(ctx, globals)?.ok_or(InvalidValue("greater"))?, - v2.run(ctx, globals)?.ok_or(InvalidValue("greater"))?, - )? > 0 - { - 1 - } else { - 0 - }, - )), - Instruction::Lower(v1, v2) => Some(Value::Integer( - if Instruction::compare( - v1.run(ctx, globals)?.ok_or(InvalidValue("lower"))?, - v2.run(ctx, globals)?.ok_or(InvalidValue("lower"))?, - )? < 0 - { - 1 - } else { - 0 - }, - )), - Instruction::Call(fct_ins, args) => { - let vals = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("call"))?; - let fct_val = fct_ins.run(ctx, globals)?.ok_or(InvalidValue("call"))?; - if let Value::Function(f) = fct_val { - let mut fun_ctx = Context::new(Some(globals)); - f.run(vals, &mut fun_ctx, globals)? - } else if let Value::StdFunction(f) = fct_val { - f(vals)? - } else { - Err(InvalidValue("call"))? - } - } - Instruction::CallNamed(fct_name, args) => { - let vals: Vec = - Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("call"))?; - let fct_val = ctx - .value(&fct_name) - .ok_or(UnknownVariable(fct_name.clone()))?; - if let Value::Function(f) = fct_val { - let mut local = ctx.clone(); - f.run(vals, &mut local, globals)? - } else if let Value::StdFunction(f) = fct_val { - f(vals)? - } else { - Err(InvalidValue("call"))? - } - } - Instruction::Return(ins) => { - let v = ins.run(ctx, globals)?.ok_or(InvalidValue("return"))?; - ctx.assign(String::from("__return"), v); - None - } - Instruction::If(cond, then) => { - if cond.run(ctx, globals)?.ok_or(InvalidValue("if"))?.to_bool() { - for i in then { - i.run(ctx, globals)?; - } - } - None - } - Instruction::IfElse(cond, then, els) => { - if cond.run(ctx, globals)?.ok_or(InvalidValue("if"))?.to_bool() { - for i in then { - i.run(ctx, globals)?; - } - } else { - for i in els { - i.run(ctx, globals)?; - } - } - None - } - Instruction::For { - variable, - from, - to, - step, - body, - } => { - if let Value::Integer(f) = from.run(ctx, globals)?.ok_or(InvalidValue("for"))? { - if let Value::Integer(t) = - to.run(ctx, globals)?.ok_or(InvalidValue("for"))? - { - if let Value::Integer(s) = - step.run(ctx, globals)?.ok_or(InvalidValue("for"))? - { - for i in (f..t).step_by(s.try_into()?) { - ctx.assign(variable.clone(), Value::Integer(i)); - for ins in body { - ins.run(ctx, globals)?; - } - } - } - } - } - None - } - Instruction::Each(variable, array_ins, body) => { - if let Value::Array(v) = - array_ins.run(ctx, globals)?.ok_or(InvalidValue("each"))? - { - for i in v.borrow().iter() { - ctx.assign(variable.clone(), i.clone()); - for ins in body { - ins.run(ctx, globals)?; - } - } - } else { - Err(InvalidValue("each"))? - } - None - } - Instruction::While(cond, body) => { - while cond - .run(ctx, globals)? - .ok_or(InvalidValue("while"))? - .to_bool() - { - for ins in body { - ins.run(ctx, globals)?; - } - } - None - } - Instruction::Handle(try_block, catch_block, variable) => { - for ins in try_block { - if let Err(e) = ins.run(ctx, globals) { - ctx.assign(variable.clone(), Value::String(e.to_string())); - for ins in catch_block { - ins.run(ctx, globals)?; - } - break; - } - } - None - } - } - } else { - None - }) - } } diff --git a/src/context.rs b/src/interpreter/context.rs similarity index 94% rename from src/context.rs rename to src/interpreter/context.rs index 09ade12..e46f2f6 100644 --- a/src/context.rs +++ b/src/interpreter/context.rs @@ -1,4 +1,4 @@ -use super::value::Value; +use crate::interpreter::value::Value; use std::collections::HashMap; #[derive(Clone)] diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 0000000..bcbd4f1 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1,4 @@ +mod context; +pub mod run; +mod stl; +mod value; diff --git a/src/interpreter/run.rs b/src/interpreter/run.rs new file mode 100644 index 0000000..f04fe58 --- /dev/null +++ b/src/interpreter/run.rs @@ -0,0 +1,538 @@ +use std::{cell::RefCell, error::Error, fs, rc::Rc}; + +use roxmltree::Document; + +use crate::{ + error::{ + BadChildCount, IncompatibleValues, InvalidProgram, InvalidValue, MissingChild, + UnknownVariable, Unnamed, + }, + instruction::Instruction, + interpreter::context::Context, + interpreter::stl, + interpreter::value::{Function, Value}, + util, +}; + +trait RunUtil { + fn add(vals: Vec) -> Result>; + fn subtract(vals: Vec) -> Result>; + fn multiply(vals: Vec) -> Result>; + fn divide(vals: Vec) -> Result>; + fn compare(v1: Value, v2: Value) -> Result>; +} + +impl RunUtil for Instruction { + 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(InvalidValue("add"))? + } + }) + .sum::>>()?, + )) + } else if vals + .iter() + .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) + { + Ok(Value::Real( + vals.iter() + .map(|v| { + if let Value::Integer(i) = v { + Ok(*i as f64) + } else if let Value::Real(f) = v { + Ok(*f) + } else { + Err(InvalidValue("add"))? + } + }) + .sum::>>()?, + )) + } else if vals.iter().all(|v| { + matches!(v, Value::Integer(_)) + || matches!(v, Value::Real(_)) + || 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::Real(f) = v { + f.to_string() + } else { + Err(InvalidValue("add"))? + }) + }) + .collect::, Box>>()? + .join(""), + )) + } else { + Err(InvalidValue("add"))? + } + } + fn subtract(vals: Vec) -> Result> { + Ok(if vals.iter().all(|v| matches!(v, Value::Integer(_))) { + let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? { + Value::Integer(i) => i, + _ => Err(InvalidValue("subtract"))?, + }; + Value::Integer( + *first + - vals + .iter() + .skip(1) + .map(|v| { + if let Value::Integer(i) = v { + Ok(i) + } else { + Err(InvalidValue("subtract"))? + } + }) + .sum::>>()?, + ) + } else if vals + .iter() + .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) + { + let first = match vals.first().ok_or(BadChildCount("subtract", 0usize))? { + Value::Integer(v) => *v as f64, + Value::Real(v) => *v, + _ => Err(InvalidValue("subtract"))?, + }; + Value::Real( + first + - vals + .iter() + .skip(1) + .map(|val| { + Ok(match val { + Value::Integer(v) => *v as f64, + Value::Real(v) => *v, + _ => Err(InvalidValue("subtract"))?, + }) + }) + .sum::>>()?, + ) + } else { + Err(InvalidValue("subtract"))? + }) + } + 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(InvalidValue("multiply"))? + } + }) + .product::>>()?, + )) + } else if vals + .iter() + .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) + { + Ok(Value::Real( + vals.iter() + .map(|val| { + Ok(match val { + Value::Integer(v) => *v as f64, + Value::Real(v) => *v, + _ => Err(InvalidValue("multiply"))?, + }) + }) + .product::>>()?, + )) + } else { + Err(InvalidValue("multiply"))? + } + } + fn divide(vals: Vec) -> Result> { + if vals + .iter() + .all(|v| matches!(v, Value::Integer(_)) || matches!(v, Value::Real(_))) + { + let first = match vals.first().ok_or(BadChildCount("divide", 0))? { + Value::Integer(v) => *v as f64, + Value::Real(v) => *v, + _ => Err(InvalidValue("divide"))?, + }; + Ok(Value::Real( + first + * vals + .iter() + .skip(1) + .map(|val| { + Ok(match val { + Value::Integer(v) => 1.0 / (*v as f64), + Value::Real(v) => 1.0 / *v, + _ => Err(InvalidValue("divide"))?, + }) + }) + .product::>>()?, + )) + } else { + Err(InvalidValue("divide"))? + } + } + + fn compare(v1: Value, v2: Value) -> Result> { + use std::cmp::Ordering; + match v1 { + Value::Integer(i1) => match v2 { + Value::Integer(i2) => Ok(i1 - i2), + Value::Real(f2) => Ok( + match (i1 as f64).partial_cmp(&f2).ok_or(IncompatibleValues)? { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }, + ), + _ => Err(IncompatibleValues)?, + }, + Value::Real(f1) => match v2 { + Value::Integer(i2) => Ok( + match f1.partial_cmp(&(i2 as f64)).ok_or(IncompatibleValues)? { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }, + ), + Value::Real(f2) => Ok(match f1.partial_cmp(&f2).ok_or(IncompatibleValues)? { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }), + _ => Err(IncompatibleValues)?, + }, + Value::String(s1) => { + if let Value::String(s2) = v2 { + Ok(match s1.cmp(&s2) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }) + } else { + Err(IncompatibleValues)? + } + } + _ => Err(IncompatibleValues)?, + } + } +} + +pub trait Run { + fn run(&self, ctx: &mut Context, globals: &Context) -> Result, Box>; + fn run_all( + ins: &Vec, + ctx: &mut Context, + globals: &Context, + ) -> Result>, Box>; +} + +impl Run for Instruction { + fn run_all( + ins: &Vec, + ctx: &mut Context, + globals: &Context, + ) -> Result>, Box> { + ins.iter().map(|i| i.run(ctx, globals)).collect() + } + + fn run(&self, ctx: &mut Context, globals: &Context) -> Result, Box> { + Ok(if let None = ctx.value(&String::from("__return")) { + match self { + Instruction::Value(key) => { + Some(match ctx.value(key).ok_or(UnknownVariable(key.clone()))? { + Value::Array(vecrc) => Value::Array(Rc::clone(&vecrc)), + val => val.clone(), + }) + } + Instruction::Assign(key, ins) => { + let v = ins.run(ctx, globals)?.ok_or(InvalidValue("assign"))?; + ctx.assign(key.clone(), v); + None + } + Instruction::Integer(val) => Some(Value::Integer(val.parse()?)), + Instruction::IntegerCast(ins) => Some(Value::Integer( + match ins.run(ctx, globals)?.ok_or(InvalidValue("integer"))? { + Value::Integer(i) => i, + Value::Real(f) => f as i64, + Value::String(s) => s.parse()?, + _ => Err(InvalidValue("integer"))?, + }, + )), + Instruction::Real(val) => Some(Value::Real(val.parse()?)), + Instruction::RealCast(ins) => Some(Value::Real( + match ins.run(ctx, globals)?.ok_or(InvalidValue("real"))? { + Value::Integer(i) => i as f64, + Value::Real(f) => f, + Value::String(s) => s.parse()?, + _ => Err(InvalidValue("real"))?, + }, + )), + Instruction::String(val) => Some(Value::String(val.clone())), + Instruction::StringCast(ins) => Some(Value::String( + match ins.run(ctx, globals)?.ok_or(InvalidValue("string"))? { + Value::Integer(i) => i.to_string(), + Value::Real(f) => f.to_string(), + Value::String(s) => s, + _ => Err(InvalidValue("string"))?, + }, + )), + Instruction::Array(args) => Some(Value::Array(Rc::new(RefCell::new( + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("array"))?, + )))), + Instruction::Add(args) => { + let vals = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("add"))?; + Some(Instruction::add(vals)?) + } + Instruction::Subtract(args) => { + let vals = Instruction::run_all(args, ctx, globals)? + .ok_or(InvalidValue("subtract"))?; + Some(Instruction::subtract(vals)?) + } + Instruction::Multiply(args) => { + let vals = Instruction::run_all(args, ctx, globals)? + .ok_or(InvalidValue("multiply"))?; + Some(Instruction::multiply(vals)?) + } + Instruction::Divide(args) => { + let vals = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("divide"))?; + Some(Instruction::divide(vals)?) + } + Instruction::And(args) => { + let vals = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("and"))?; + Some(Value::Integer(if vals.iter().all(Value::to_bool) { + 1 + } else { + 0 + })) + } + Instruction::Or(args) => { + let vals = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("or"))?; + Some(Value::Integer(if vals.iter().any(Value::to_bool) { + 1 + } else { + 0 + })) + } + Instruction::Not(arg) => Some(Value::Integer( + if arg.run(ctx, globals)?.ok_or(InvalidValue("not"))?.to_bool() { + 0 + } else { + 1 + }, + )), + Instruction::Equal(v1, v2) => Some(Value::Integer( + if Instruction::compare( + v1.run(ctx, globals)?.ok_or(InvalidValue("equal"))?, + v2.run(ctx, globals)?.ok_or(InvalidValue("equal"))?, + )? == 0 + { + 1 + } else { + 0 + }, + )), + Instruction::Greater(v1, v2) => Some(Value::Integer( + if Instruction::compare( + v1.run(ctx, globals)?.ok_or(InvalidValue("greater"))?, + v2.run(ctx, globals)?.ok_or(InvalidValue("greater"))?, + )? > 0 + { + 1 + } else { + 0 + }, + )), + Instruction::Lower(v1, v2) => Some(Value::Integer( + if Instruction::compare( + v1.run(ctx, globals)?.ok_or(InvalidValue("lower"))?, + v2.run(ctx, globals)?.ok_or(InvalidValue("lower"))?, + )? < 0 + { + 1 + } else { + 0 + }, + )), + Instruction::Call(fct_ins, args) => { + let vals = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("call"))?; + let fct_val = fct_ins.run(ctx, globals)?.ok_or(InvalidValue("call"))?; + if let Value::Function(f) = fct_val { + let mut fun_ctx = Context::new(Some(globals)); + f.run(vals, &mut fun_ctx, globals)? + } else if let Value::StdFunction(f) = fct_val { + f(vals)? + } else { + Err(InvalidValue("call"))? + } + } + Instruction::CallNamed(fct_name, args) => { + let vals: Vec = + Instruction::run_all(args, ctx, globals)?.ok_or(InvalidValue("call"))?; + let fct_val = ctx + .value(&fct_name) + .ok_or(UnknownVariable(fct_name.clone()))?; + if let Value::Function(f) = fct_val { + let mut local = ctx.clone(); + f.run(vals, &mut local, globals)? + } else if let Value::StdFunction(f) = fct_val { + f(vals)? + } else { + Err(InvalidValue("call"))? + } + } + Instruction::Return(ins) => { + let v = ins.run(ctx, globals)?.ok_or(InvalidValue("return"))?; + ctx.assign(String::from("__return"), v); + None + } + Instruction::If(cond, then) => { + if cond.run(ctx, globals)?.ok_or(InvalidValue("if"))?.to_bool() { + for i in then { + i.run(ctx, globals)?; + } + } + None + } + Instruction::IfElse(cond, then, els) => { + if cond.run(ctx, globals)?.ok_or(InvalidValue("if"))?.to_bool() { + for i in then { + i.run(ctx, globals)?; + } + } else { + for i in els { + i.run(ctx, globals)?; + } + } + None + } + Instruction::For { + variable, + from, + to, + step, + body, + } => { + if let Value::Integer(f) = from.run(ctx, globals)?.ok_or(InvalidValue("for"))? { + if let Value::Integer(t) = + to.run(ctx, globals)?.ok_or(InvalidValue("for"))? + { + if let Value::Integer(s) = + step.run(ctx, globals)?.ok_or(InvalidValue("for"))? + { + for i in (f..t).step_by(s.try_into()?) { + ctx.assign(variable.clone(), Value::Integer(i)); + for ins in body { + ins.run(ctx, globals)?; + } + } + } + } + } + None + } + Instruction::Each(variable, array_ins, body) => { + if let Value::Array(v) = + array_ins.run(ctx, globals)?.ok_or(InvalidValue("each"))? + { + for i in v.borrow().iter() { + ctx.assign(variable.clone(), i.clone()); + for ins in body { + ins.run(ctx, globals)?; + } + } + } else { + Err(InvalidValue("each"))? + } + None + } + Instruction::While(cond, body) => { + while cond + .run(ctx, globals)? + .ok_or(InvalidValue("while"))? + .to_bool() + { + for ins in body { + ins.run(ctx, globals)?; + } + } + None + } + Instruction::Handle(try_block, catch_block, variable) => { + for ins in try_block { + if let Err(e) = ins.run(ctx, globals) { + ctx.assign(variable.clone(), Value::String(e.to_string())); + for ins in catch_block { + ins.run(ctx, globals)?; + } + break; + } + } + None + } + } + } else { + None + }) + } +} + +pub fn run_file(filename: &str) -> Result<(), Box> { + let contents = fs::read_to_string(filename)?; + run(contents) +} + +pub fn run(program: String) -> Result<(), Box> { + let doc = Document::parse(&program)?; + let root = doc.root(); + + let mut ctx = Context::new(None); + stl::inject_all(&mut ctx); + + let main = root + .first_element_child() + .ok_or(InvalidProgram)? + .children() + .find(|node| util::tag_name(&node) == "main") + .ok_or(MissingChild("program", "main"))?; + let main_ast = Instruction::from_children(main)?; + + let functions = root + .first_element_child() + .ok_or(InvalidProgram)? + .children() + .filter(|node| node.tag_name().name() == String::from("function")); + + for fun in functions { + ctx.assign( + String::from(fun.attribute("name").ok_or(Unnamed("function"))?), + Value::Function(Function::from(&fun)?), + ); + } + + let mut main_ctx = Context::new(Some(&ctx)); + + for ins in main_ast { + ins.run(&mut main_ctx, &ctx)?; + } + + Ok(()) +} diff --git a/src/stl.rs b/src/interpreter/stl.rs similarity index 98% rename from src/stl.rs rename to src/interpreter/stl.rs index a49faef..a5887e5 100644 --- a/src/stl.rs +++ b/src/interpreter/stl.rs @@ -1,7 +1,7 @@ -use crate::error::InaccessibleFile; +use crate::error::{BadArgumentCount, InaccessibleFile, InvalidArgument}; +use crate::interpreter::context::Context; +use crate::interpreter::value::Value; -use super::error::{BadArgumentCount, InvalidArgument}; -use super::{Context, Value}; use std::cell::RefCell; use std::error::Error; use std::fs::{self, OpenOptions}; diff --git a/src/value.rs b/src/interpreter/value.rs similarity index 90% rename from src/value.rs rename to src/interpreter/value.rs index 2f477e1..5939ba6 100644 --- a/src/value.rs +++ b/src/interpreter/value.rs @@ -1,10 +1,14 @@ -use super::error::{BadArgumentCount, MissingChild, Unnamed}; -use super::{util, Context, Instruction}; use roxmltree::Node; use std::cell::RefCell; use std::error::Error; use std::rc::Rc; +use crate::error::{BadArgumentCount, MissingChild, Unnamed}; +use crate::instruction::Instruction; +use crate::interpreter::context::Context; +use crate::interpreter::run::Run; +use crate::util; + #[derive(Clone, Debug)] pub struct Function { pub args: Vec, diff --git a/src/lib.rs b/src/lib.rs index bfd4522..4403a7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,58 +1,9 @@ -use std::error::Error; -use std::fs; - -use roxmltree::Document; - -mod context; mod error; mod instruction; -mod stl; mod util; -mod value; -use context::Context; -use error::{InvalidProgram, MissingChild, Unnamed}; -use instruction::Instruction; -use value::{Function, Value}; +mod compiler; +mod interpreter; -pub fn run_file(filename: &str) -> Result<(), Box> { - let contents = fs::read_to_string(filename)?; - run(contents) -} - -pub fn run(program: String) -> Result<(), Box> { - let doc = Document::parse(&program)?; - let root = doc.root(); - - let mut ctx = Context::new(None); - stl::inject_all(&mut ctx); - - let main = root - .first_element_child() - .ok_or(InvalidProgram)? - .children() - .find(|node| util::tag_name(&node) == "main") - .ok_or(MissingChild("program", "main"))?; - let main_ast = Instruction::from_children(main)?; - - let functions = root - .first_element_child() - .ok_or(InvalidProgram)? - .children() - .filter(|node| node.tag_name().name() == String::from("function")); - - for fun in functions { - ctx.assign( - String::from(fun.attribute("name").ok_or(Unnamed("function"))?), - Value::Function(Function::from(&fun)?), - ); - } - - let mut main_ctx = Context::new(Some(&ctx)); - - for ins in main_ast { - ins.run(&mut main_ctx, &ctx)?; - } - - Ok(()) -} +pub use compiler::compile::{compile, compile_file}; +pub use interpreter::run::{run, run_file}; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e78f6c7..0000000 --- a/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -use plxml::run_file; -use std::env; - -fn main() { - let args: Vec = env::args().collect(); - if let Err(e) = run_file(&args[1]) { - eprintln!("Error occurred: {}", e); - } -}