From e256abfee66a303a1f1183c07653493b20e4b802 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 26 Aug 2019 21:24:17 -0500 Subject: [PATCH] DEV: Plugin the VM to our FileBuilder. Most of the tests do not yet pass and the main.rs doesn't compile but we are snapshotting here so we can start fixing unit tests before we hook directly into the main.rs. --- src/build/compile_test.rs | 42 +- src/build/format.rs | 231 +--- src/build/ir.rs | 31 +- src/build/mod.rs | 1979 ++----------------------------- src/build/opcode/debug.rs | 53 + src/build/opcode/environment.rs | 55 +- src/build/opcode/error.rs | 32 +- src/build/opcode/mod.rs | 94 +- src/build/opcode/pointer.rs | 8 +- src/build/opcode/runtime.rs | 176 ++- src/build/opcode/test.rs | 25 +- src/build/opcode/translate.rs | 76 +- src/build/opcode/vm.rs | 172 ++- src/build/scope.rs | 4 +- src/build/test.rs | 104 +- src/convert/env.rs | 8 - src/convert/exec.rs | 21 +- src/convert/flags.rs | 6 +- src/convert/json.rs | 8 - src/convert/toml.rs | 8 - src/convert/yaml.rs | 8 - 21 files changed, 553 insertions(+), 2588 deletions(-) create mode 100644 src/build/opcode/debug.rs diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index 962e3ef..8c06128 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -23,37 +23,39 @@ use crate::convert::ConverterRegistry; fn assert_build(input: &str) { let i_paths = Vec::new(); - let cache = MemoryCache::new(); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new("", &i_paths, Rc::new(RefCell::new(cache)), ®istry); + let out_buffer: Vec = Vec::new(); + let err_buffer: Vec = Vec::new(); + let mut b = FileBuilder::new("", &i_paths, out_buffer, err_buffer); b.enable_validate_mode(); b.eval_string(input).unwrap(); - if !b.assert_collector.success { - assert!(false, b.assert_collector.failures); - } + // FIXME(jwall): What do we want to do with the assert collector? + //if !b.assert_collector.success { + // assert!(false, b.assert_collector.failures); + //} } fn assert_build_failure(input: &str, expect: Vec) { let i_paths = Vec::new(); - let cache = MemoryCache::new(); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new("", &i_paths, Rc::new(RefCell::new(cache)), ®istry); + let out_buffer: Vec = Vec::new(); + let err_buffer: Vec = Vec::new(); + let mut b = FileBuilder::new("", &i_paths, out_buffer, err_buffer); b.enable_validate_mode(); let err = b.eval_string(input); match err { Ok(_) => { for r in expect.iter() { - if !b.assert_collector.success { - if let None = r.find(&b.assert_collector.failures) { - assert!( - false, - "[{}] was not found in Assertion Failures:\n{}", - r, b.assert_collector.failures - ); - } - } else { - assert!(false, "Building input Did not panic!"); - } + // FIXME(jwall): Assert collector... + //if !b.assert_collector.success { + // if let None = r.find(&b.assert_collector.failures) { + // assert!( + // false, + // "[{}] was not found in Assertion Failures:\n{}", + // r, b.assert_collector.failures + // ); + // } + //} else { + // assert!(false, "Building input Did not panic!"); + //} } } Err(ref err) => { diff --git a/src/build/format.rs b/src/build/format.rs index 78a243d..3d49ee2 100644 --- a/src/build/format.rs +++ b/src/build/format.rs @@ -13,8 +13,6 @@ // limitations under the License. //! The format string logic for ucg format expressions. -use std::cell::RefCell; -use std::clone::Clone; use std::error::Error; use std::str::Chars; @@ -22,9 +20,6 @@ use abortable_parser::iter::SliceIter; use abortable_parser::Result as ParseResult; use crate::ast::*; -use crate::build::assets; -use crate::build::{FileBuilder, Val}; -use crate::error; use crate::iter; use crate::parse; use crate::tokenizer; @@ -47,29 +42,13 @@ impl SimpleTemplate { } } -/// Implements the logic for format strings in UCG format expressions. -pub struct SimpleFormatter + Clone> { - tmpl: String, - args: Vec, -} - -impl + Clone> SimpleFormatter { - /// Constructs a Formatter with a template and args. - pub fn new>(tmpl: S, args: Vec) -> Self { - SimpleFormatter { - tmpl: tmpl.into(), - args: args, - } - } -} - impl TemplateParser for SimpleTemplate { fn parse(&self, input: &str) -> TemplateResult { let mut result = Vec::new(); let mut count = 0; let mut should_escape = false; let mut buf: Vec = Vec::new(); - for c in dbg!(input).chars() { + for c in input.chars() { if c == '@' && !should_escape { result.push(TemplatePart::Str(buf)); buf = Vec::new(); @@ -81,7 +60,6 @@ impl TemplateParser for SimpleTemplate { continue; } else { buf.push(c); - dbg!(&buf); } should_escape = false; } @@ -92,59 +70,6 @@ impl TemplateParser for SimpleTemplate { } } -impl + Clone> FormatRenderer for SimpleFormatter { - /// Renders a formatter to a string or returns an error. - /// - /// If the formatter has the wrong number of arguments for the number of replacements - /// it will return an error. Otherwise it will return the formatted string. - fn render(&self, pos: &Position) -> Result> { - let mut buf = String::new(); - let mut count = 0; - let parser = SimpleTemplate::new(); - let parts = parser.parse(&self.tmpl)?; - for p in parts { - match p { - TemplatePart::PlaceHolder(idx) => { - if idx == self.args.len() { - return Err(error::BuildError::with_pos( - "Too few arguments to string \ - formatter.", - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } - let arg = self.args[count].clone(); - let strval = arg.into(); - buf.push_str(&strval); - count += 1; - } - TemplatePart::Str(cs) => { - buf.reserve(cs.len()); - for c in cs { - buf.push(c); - } - } - TemplatePart::Expression(_) => unreachable!(), - } - } - if self.args.len() != count { - return Err(error::BuildError::with_pos( - format!( - "Too many arguments to string \ - formatter. Expected {} got {}", - count, - self.args.len() - ), - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } - return Ok(buf); - } -} - pub struct ExpressionTemplate(); impl ExpressionTemplate { @@ -226,157 +151,3 @@ impl TemplateParser for ExpressionTemplate { Ok(parts) } } - -pub struct ExpressionFormatter<'a, C> -where - C: assets::Cache, -{ - tmpl: String, - builder: RefCell>, -} - -impl<'a, C> ExpressionFormatter<'a, C> -where - C: assets::Cache, -{ - pub fn new>(tmpl: S, builder: FileBuilder<'a, C>) -> Self { - ExpressionFormatter { - tmpl: tmpl.into(), - builder: RefCell::new(builder), - } - } - - fn consume_expr( - &self, - builder: &mut FileBuilder<'a, C>, - iter: &mut Chars, - pos: &Position, - ) -> Result> { - // we expect the next char to be { or we error. - let mut expr_string = String::new(); - let mut brace_count = 0; - match iter.next() { - Some(c) => { - if c == '{' { - brace_count += 1; - } else { - return Err(error::BuildError::with_pos( - format!( - "Invalid syntax for format string expected '{{' but got {}", - c - ), - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } - } - None => { - return Err(error::BuildError::with_pos( - "Invalid syntax for format string expected '{' but string ended", - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } - }; - loop { - let c = match iter.next() { - Some(c) => c, - None => break, - }; - if c == '{' { - brace_count += 1; - } - if c == '}' { - brace_count -= 1; - // if brace_count is 0 then this is the end of expression. - if brace_count != 0 { - // if it is not zero then this character is just part of - // the embedded expression. - expr_string.push(c); - continue; - } - // empty expressions are an error - if expr_string.is_empty() { - return Err(error::BuildError::with_pos( - "Got an empty expression in format string", - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } - if !expr_string.ends_with(";") { - expr_string.push(';'); - } - // we are done and it is time to compute the expression and return it. - return Ok(builder.eval_string(&expr_string)?.as_ref().clone()); - } else { - expr_string.push(c); - } - } - return Err(error::BuildError::with_pos( - "Expected '}' but got end of string", - error::ErrorType::FormatError, - pos.clone(), - ) - .to_boxed()); - } -} - -impl<'a, C> FormatRenderer for ExpressionFormatter<'a, C> -where - C: assets::Cache, -{ - fn render(&self, pos: &Position) -> Result> { - let mut buf = String::new(); - let mut should_escape = false; - let mut iter = self.tmpl.chars(); - loop { - let c = match iter.next() { - Some(c) => c, - None => break, - }; - if c == '@' && !should_escape { - // This is kind of wasteful. Can we do better? - let val = self.consume_expr(&mut self.builder.borrow_mut(), &mut iter, pos)?; - let strval: String = val.into(); - buf.push_str(&strval); - should_escape = false; - } else if c == '\\' && !should_escape { - should_escape = true; - } else { - buf.push(c); - should_escape = false; - } - } - return Ok(buf); - } -} - -#[cfg(test)] -mod test { - use super::{FormatRenderer, SimpleFormatter}; - use crate::ast::Position; - - #[test] - fn test_format_happy_path() { - let formatter = SimpleFormatter::new("foo @ @ \\@", vec!["bar", "quux"]); - let pos = Position::new(0, 0, 0); - assert_eq!(formatter.render(&pos).unwrap(), "foo bar quux @"); - } - - #[test] - fn test_format_happy_wrong_too_few_args() { - let formatter = SimpleFormatter::new("foo @ @ \\@", vec!["bar"]); - let pos = Position::new(0, 0, 0); - assert!(formatter.render(&pos).is_err()); - } - - #[test] - fn test_format_happy_wrong_too_many_args() { - let formatter = SimpleFormatter::new("foo @ @ \\@", vec!["bar", "quux", "baz"]); - let pos = Position::new(0, 0, 0); - assert!(formatter.render(&pos).is_err()); - } -} diff --git a/src/build/ir.rs b/src/build/ir.rs index d288583..aa05a00 100644 --- a/src/build/ir.rs +++ b/src/build/ir.rs @@ -20,8 +20,6 @@ pub enum Val { List(Vec>), Tuple(Vec<(String, Rc)>), Env(Vec<(String, String)>), - Func(FuncDef), - Module(ModuleDef), } impl Val { @@ -36,8 +34,6 @@ impl Val { &Val::List(_) => "List".to_string(), &Val::Tuple(_) => "Tuple".to_string(), &Val::Env(_) => "Env".to_string(), - &Val::Func(_) => "Func".to_string(), - &Val::Module(_) => "Module".to_string(), } } @@ -53,9 +49,7 @@ impl Val { &Val::Str(_), &Val::List(_), &Val::Tuple(_), - &Val::Env(_), - &Val::Func(_), - &Val::Module(_) + &Val::Env(_) ) } @@ -99,14 +93,6 @@ impl Val { Ok(true) } } - (&Val::Func(_), &Val::Func(_)) => Err(error::BuildError::new( - "Func are not comparable", - error::ErrorType::TypeFail, - )), - (&Val::Module(_), &Val::Module(_)) => Err(error::BuildError::new( - "Module are not comparable", - error::ErrorType::TypeFail, - )), // EMPTY is always comparable for equality. (&Val::Empty, _) => Ok(false), (_, &Val::Empty) => Ok(false), @@ -182,25 +168,12 @@ impl Val { return false; } - pub fn is_func(&self) -> bool { - if let &Val::Func(_) = self { - return true; - } - return false; - } - pub fn is_str(&self) -> bool { if let &Val::Str(_) = self { return true; } return false; } - pub fn is_module(&self) -> bool { - if let &Val::Module(_) = self { - return true; - } - return false; - } } impl Display for Val { @@ -218,8 +191,6 @@ impl Display for Val { } write!(f, "]") } - &Val::Func(_) => write!(f, "Func(..)"), - &Val::Module(_) => write!(f, "Module{{..}}"), &Val::Tuple(ref def) => { write!(f, "{{\n")?; for v in def.iter() { diff --git a/src/build/mod.rs b/src/build/mod.rs index 9981a02..4dec097 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -14,30 +14,26 @@ //! The build stage of the ucg compiler. use std::cell::RefCell; -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::convert::TryInto; use std::env; use std::error::Error; use std::fs::File; use std::io::Read; -use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; -use std::string::ToString; -use regex; use simple_error; -use unicode_segmentation::UnicodeSegmentation; use crate::ast::*; -use crate::build::format::{ExpressionFormatter, FormatRenderer, SimpleFormatter}; -use crate::build::scope::{find_in_fieldlist, Scope, ValueMap}; -use crate::convert::ConverterRegistry; -use crate::convert::ImporterRegistry; use crate::error; use crate::iter::OffsetStrIter; use crate::parse::parse; +use crate::build::opcode::translate; +use crate::build::opcode::Environment; +use crate::build::opcode::VM; + pub mod assets; pub mod format; pub mod ir; @@ -53,44 +49,6 @@ enum ProcessingOpType { Filter, } -impl FuncDef { - /// Expands a ucg function using the given arguments into a new Val. - pub fn eval( - &self, - parent_builder: &FileBuilder, - mut args: Vec>, - ) -> Result, Box> - where - C: assets::Cache, - { - // Error conditions. If the args don't match the length and types of the argdefs then this is - // func call error. - if args.len() > self.argdefs.len() { - return Err(error::BuildError::with_pos( - "Func called with too many args", - error::ErrorType::BadArgLen, - self.pos.clone(), - ) - .to_boxed()); - } - // If the args don't match the types required by the expressions then that is a TypeFail. - // If the expressions reference Symbols not defined in the FuncDef that is also an error. - let mut build_output = HashMap::, Rc>::new(); - for (i, arg) in args.drain(0..).enumerate() { - build_output - .entry(self.argdefs[i].clone()) - .or_insert(arg.clone()); - } - let mut b = parent_builder.clone_builder(); - if let Some(ref scope) = self.scope { - b.scope = scope.spawn_child(); - } - // We clobber anything that used to be in the scope with the arguments. - b.merge_build_output(build_output, true); - Ok(b.eval_expr(self.fields.as_ref(), &b.scope.spawn_child())?) - } -} - /// The result of a build. type BuildResult = Result<(), Box>; @@ -126,104 +84,57 @@ impl AssertCollector { } } +// TODO(jwall): I think the Rc is no longer necessary. /// Builder handles building ucg code for a single file. -pub struct FileBuilder<'a, C> +pub struct FileBuilder<'a, Stdout, Stderr> where - C: assets::Cache, + Stdout: std::io::Write, + Stderr: std::io::Write, { + environment: Rc>>, working_dir: PathBuf, + // FIXME(jwall): These need to be compiled and added to the op cache. std: Rc>, import_path: &'a Vec, - validate_mode: bool, - pub assert_collector: AssertCollector, - scope: Scope, - import_registry: ImporterRegistry, - converter_registry: &'a ConverterRegistry, - // NOTE(jwall): We use interior mutability here because we need - // our asset cache to be shared by multiple different sub-builders. - // We use Rc to handle the reference counting for us and we use - // RefCell to give us interior mutability. This sacrifices our - // compile time memory safety for runtime checks. However it's - // acceptable in this case since I can't figure out a better way to - // handle it. - // The assets are other parsed files from import statements. They - // are keyed by the canonicalized import path. This acts as a cache - // so multiple imports of the same file don't have to be parsed - // multiple times. - assets: Rc>, - pub is_module: bool, pub last: Option>, - pub out_lock: Option<(String, Rc)>, + pub out: Option>, + validate_mode: bool, } -macro_rules! eval_binary_expr { - ($case:pat, $pos:ident, $rside:ident, $result:expr, $msg:expr) => { - match $rside.as_ref() { - $case => { - return Ok(Rc::new($result)); - } - val => { - return Err(error::BuildError::with_pos( - format!("Expected {} but got ({})", $msg, val), - error::ErrorType::TypeFail, - $pos.clone(), - ) - .to_boxed()); - } - } - }; -} - -impl<'a, C> FileBuilder<'a, C> +impl<'a, Stdout, Stderr> FileBuilder<'a, Stdout, Stderr> where - C: assets::Cache, + Stdout: std::io::Write, + Stderr: std::io::Write, { /// Constructs a new Builder. pub fn new>( working_dir: P, import_paths: &'a Vec, - cache: Rc>, - converter_registry: &'a ConverterRegistry, + stdout: Stdout, + stderr: Stderr, ) -> Self { - let env_vars: Vec<(String, String)> = env::vars().collect(); - let scope = scope::Scope::new(Rc::new(Val::Env(env_vars))); + let environment = Environment::new_with_vars(stdout, stderr, env::vars().collect()); FileBuilder { + environment: Rc::new(RefCell::new(environment)), // Our import stack is initialized with ourself. working_dir: working_dir.into(), std: Rc::new(stdlib::get_libs()), import_path: import_paths, - validate_mode: false, - assert_collector: AssertCollector::new(), - scope: scope, - import_registry: ImporterRegistry::make_registry(), - converter_registry: converter_registry, - assets: cache, - out_lock: None, - is_module: false, + out: None, last: None, + validate_mode: false, } } pub fn clone_builder(&self) -> Self { FileBuilder { + environment: self.environment.clone(), working_dir: self.working_dir.clone(), std: self.std.clone(), import_path: self.import_path, - validate_mode: false, - assert_collector: AssertCollector { - counter: 0, - success: true, - summary: String::new(), - failures: String::new(), - }, - assets: self.assets.clone(), - // This is admittedly a little wasteful but we can live with it for now. - import_registry: ImporterRegistry::make_registry(), - converter_registry: self.converter_registry, - scope: self.scope.spawn_clean(), - out_lock: None, - is_module: false, + out: None, last: None, + validate_mode: self.validate_mode, } } @@ -235,6 +146,7 @@ where let mut s = String::new(); f.read_to_string(&mut s)?; let input = OffsetStrIter::new(&s).with_src_file(file.clone()); + // TODO(jwall): Pass in the file name? let eval_result = self.eval_input(input); match eval_result { Ok(v) => { @@ -252,1547 +164,6 @@ where } } - pub fn merge_build_output(&mut self, scope: ValueMap, clobber: bool) { - for (name, value) in scope.iter() { - if !clobber && !self.scope.build_output.contains_key(name) { - self.scope.build_output.insert(name.clone(), value.clone()); - } else { - self.scope.build_output.insert(name.clone(), value.clone()); - } - } - } - - pub fn set_strict(&mut self, to: bool) { - self.scope.strict = to; - } - - fn eval_tuple( - &self, - fields: &Vec<(Token, Expression)>, - scope: &Scope, - ) -> Result, Box> { - let mut new_fields = Vec::<(String, Rc)>::new(); - for &(ref name, ref expr) in fields.iter() { - let val = self.eval_expr(expr, scope)?; - new_fields.push((name.fragment.clone(), val)); - } - Ok(Rc::new(Val::Tuple(new_fields))) - } - - fn eval_list(&self, def: &ListDef, scope: &Scope) -> Result, Box> { - let mut vals = Vec::new(); - for expr in def.elems.iter() { - vals.push(self.eval_expr(expr, scope)?); - } - Ok(Rc::new(Val::List(vals))) - } - - fn eval_value(&self, v: &Value, scope: &Scope) -> Result, Box> { - match v { - &Value::Empty(_) => Ok(Rc::new(Val::Empty)), - &Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))), - &Value::Int(ref i) => Ok(Rc::new(Val::Int(i.val))), - &Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))), - &Value::Str(ref s) => Ok(Rc::new(Val::Str(s.val.to_string()))), - &Value::Symbol(ref s) => { - scope - .lookup_sym(&(s.into()), true) - .ok_or(Box::new(error::BuildError::with_pos( - format!("Unable to find binding {}", s.val,), - error::ErrorType::NoSuchSymbol, - v.pos().clone(), - ))) - } - &Value::List(ref def) => self.eval_list(def, scope), - &Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val, scope), - } - } - - /// Returns a Val by name from previously built UCG. - pub fn get_out_by_name(&self, name: &str) -> Option> { - let key = PositionedItem { - pos: Position::new(0, 0, 0), - val: name.to_string(), - }; - self.scope.lookup_sym(&key, true) - } - - pub fn scope_mut(&mut self) -> &mut Scope { - &mut self.scope - } - - /// Puts the builder in validation mode. - /// - /// Among other things this means that assertions will be evaluated and their results - /// will be saved in a report for later output. - pub fn enable_validate_mode(&mut self) { - self.validate_mode = true; - } - - /// Builds a list of parsed UCG Statements. - pub fn eval_stmts(&mut self, ast: &Vec) -> BuildResult { - for stmt in ast.iter() { - self.eval_stmt(stmt)?; - } - Ok(()) - } - - pub fn eval_input(&mut self, input: OffsetStrIter) -> Result, Box> { - match parse(input.clone(), None) { - Ok(stmts) => { - //panic!("Successfully parsed {}", input); - let mut out: Option> = None; - for stmt in stmts.iter() { - out = Some(self.eval_stmt(stmt)?); - } - match out { - None => return Ok(Rc::new(Val::Empty)), - Some(val) => Ok(val), - } - } - Err(err) => Err(Box::new(err)), - } - } - - /// Evaluate an input string as UCG. - pub fn eval_string(&mut self, input: &str) -> Result, Box> { - self.eval_input(OffsetStrIter::new(input)) - } - - fn check_reserved_word(name: &str) -> bool { - match name { - "self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "func" - | "module" | "env" | "map" | "filter" | "NULL" | "out" | "in" | "is" | "not" => true, - _ => false, - } - } - - fn detect_import_cycle(&self, path: &str) -> bool { - self.scope - .import_stack - .iter() - .find(|p| *p == path) - .is_some() - } - - fn find_file>( - &self, - path: P, - use_import_path: bool, - ) -> Result> { - // Try a relative path first. - let path = path.into(); - let mut normalized = self.working_dir.clone(); - if path.is_relative() { - normalized.push(&path); - // First see if the normalized file exists or not. - if !normalized.exists() && use_import_path { - // TODO(jwall): Support importing from a zip file in this - // import_path? - // If it does not then look for it in the list of import_paths - for mut p in self.import_path.iter().cloned() { - p.push(&path); - if p.exists() { - normalized = p; - break; - } - } - } - } else { - normalized = path; - } - match normalized.canonicalize() { - Ok(p) => Ok(p), - Err(e) => Err(error::BuildError::new( - format!("Path not found {}", normalized.to_string_lossy()), - error::ErrorType::OSError, - ) - .wrap_cause(Box::new(e)) - .to_boxed()), - } - } - - fn eval_import(&self, def: &ImportDef) -> Result, Box> { - // Look for a std file first. - if def.path.fragment.starts_with("std/") { - if self.std.contains_key(&def.path.fragment) { - // Okay then this is a stdlib and it's special. - // Introduce a scope so the above borrow is dropped before we modify - // the cache below. - // Only parse the file once on import. - let path = PathBuf::from(&def.path.fragment); - let maybe_asset = self.assets.borrow().get(&path)?; - let result = match maybe_asset { - Some(v) => v.clone(), - None => { - let mut b = self.clone_builder(); - b.eval_input( - OffsetStrIter::new(self.std.get(&def.path.fragment).unwrap()) - .with_src_file(&def.path.fragment), - )?; - b.get_outputs_as_val() - } - }; - let mut mut_assets_cache = self.assets.borrow_mut(); - // standard library assets are not real files so the - // mod key is always 0. - mut_assets_cache.stash(path, result.clone(), 0)?; - return Ok(result); - } else { - return Err(error::BuildError::with_pos( - format!("No such import {} in the std library.", def.path.fragment), - error::ErrorType::Unsupported, - def.pos.clone(), - ) - .to_boxed()); - } - } - let sep = format!("{}", std::path::MAIN_SEPARATOR); - let raw_path = def.path.fragment.replace("/", &sep); - let normalized = match self.find_file(&raw_path, true) { - Ok(p) => p, - Err(e) => { - return Err(error::BuildError::with_pos( - "No Such Import", - error::ErrorType::ImportError, - def.path.pos.clone(), - ) - .wrap_cause(e) - .to_boxed()); - } - }; - if self.detect_import_cycle(normalized.to_string_lossy().as_ref()) { - return Err(error::BuildError::with_pos( - format!( - "Import Cycle Detected!!!! {} is already in import stack: {:?}", - normalized.to_string_lossy(), - self.scope.import_stack, - ), - error::ErrorType::Unsupported, - def.pos.clone(), - ) - .to_boxed()); - } - // 1. calculate mod time for file. - let lib_meta = std::fs::metadata(&normalized)?; - let modkey = lib_meta - .modified()? - .duration_since(std::time::SystemTime::UNIX_EPOCH)? - .as_secs(); - { - // 2. check mod time against previous stored mod time for file. - if !self.assets.borrow().check_mod_key(&normalized, modkey)? { - // 3. if different then evict from the cache - eprintln!( - "{} has changed evicting from cache...", - normalized.to_string_lossy().to_string() - ); - self.assets.borrow_mut().evict(&normalized)?; - } - } - // Only parse the file once on import. - let maybe_asset = self.assets.borrow().get(&normalized)?; - let result = match maybe_asset { - Some(v) => v.clone(), - None => { - let mut b = self.clone_builder(); - b.scope - .import_stack - .push(normalized.to_string_lossy().to_string()); - - match b.build(&normalized) { - Ok(_) => b.get_outputs_as_val(), - Err(e) => { - return Err(error::BuildError::with_pos( - "Import failed", - error::ErrorType::ImportError, - def.pos.clone(), - ) - .wrap_cause(e) - .to_boxed()); - } - } - } - }; - let mut mut_assets_cache = self.assets.borrow_mut(); - mut_assets_cache.stash(normalized.clone(), result.clone(), modkey)?; - return Ok(result); - } - - fn eval_let(&mut self, def: &LetDef) -> Result, Box> { - let child_scope = self.scope.clone(); - let val = self.eval_expr(&def.value, &child_scope)?; - let name = &def.name; - if Self::check_reserved_word(&name.fragment) { - return Err(error::BuildError::with_pos( - format!("Let {} binding collides with reserved word", name.fragment), - error::ErrorType::ReservedWordError, - name.pos.clone(), - ) - .to_boxed()); - } - match self.scope.build_output.entry(name.into()) { - Entry::Occupied(e) => { - return Err(error::BuildError::with_pos( - format!( - "Binding \ - for {:?} already \ - exists", - e.key(), - ), - error::ErrorType::DuplicateBinding, - def.name.pos.clone(), - ) - .to_boxed()); - } - Entry::Vacant(e) => { - e.insert(val.clone()); - } - } - Ok(val) - } - - fn eval_stmt(&mut self, stmt: &Statement) -> Result, Box> { - let child_scope = self.scope.clone(); - match stmt { - &Statement::Assert(_, ref expr) => self.eval_assert(&expr, &child_scope), - &Statement::Let(ref def) => self.eval_let(def), - &Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope), - // Only one output can be used per file. Right now we enforce this by - // having a single builder per file. - &Statement::Output(ref pos, ref typ, ref expr) => { - if let None = self.out_lock { - let val = self.eval_expr(expr, &child_scope)?; - self.out_lock = Some((typ.fragment.to_string(), val.clone())); - Ok(val) - } else { - Err(error::BuildError::with_pos( - format!("You can only have one output per file."), - error::ErrorType::Unsupported, - pos.clone(), - ) - .to_boxed()) - } - } - &Statement::Print(ref pos, ref typ, ref expr) => { - if let None = self.out_lock { - let val = self.eval_expr(expr, &child_scope)?; - match self.converter_registry.get_converter(&typ.fragment) { - Some(c) => { - let mut buf = Vec::new(); - c.convert(val.clone(), &mut buf)?; - Ok(Rc::new(Val::Str(String::from_utf8(buf)?))) - } - None => Err(error::BuildError::with_pos( - format!("Invalid Converter specified for print {}", typ.fragment), - error::ErrorType::Unsupported, - pos.clone(), - ) - .to_boxed()), - } - } else { - Err(error::BuildError::with_pos( - format!("You can only have one output per file."), - error::ErrorType::Unsupported, - pos.clone(), - ) - .to_boxed()) - } - } - } - } - - fn add_vals( - &self, - lpos: &Position, - rpos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match *left { - Val::Int(i) => { - eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i + ii), "Integer") - } - Val::Float(f) => { - eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f + ff), "Float") - } - Val::Str(ref s) => match right.as_ref() { - &Val::Str(ref ss) => { - return Ok(Rc::new(Val::Str([s.to_string(), ss.clone()].concat()))); - } - val => { - return Err(error::BuildError::with_pos( - format!( - "Expected \ - String \ - but got \ - {:?}", - val - ), - error::ErrorType::TypeFail, - rpos.clone(), - ) - .to_boxed()); - } - }, - Val::List(ref l) => match right.as_ref() { - &Val::List(ref r) => { - let mut new_vec = Vec::new(); - new_vec.extend(l.iter().cloned()); - new_vec.extend(r.iter().cloned()); - return Ok(Rc::new(Val::List(new_vec))); - } - val => { - return Err(error::BuildError::with_pos( - format!( - "Expected \ - List \ - but got \ - {:?}", - val - ), - error::ErrorType::TypeFail, - rpos.clone(), - ) - .to_boxed()); - } - }, - ref expr => { - return Err(error::BuildError::with_pos( - format!("{} does not support the '+' operation", expr.type_name()), - error::ErrorType::Unsupported, - lpos.clone(), - ) - .to_boxed()); - } - } - } - - fn subtract_vals( - &self, - lpos: &Position, - rpos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match *left { - Val::Int(i) => { - eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i - ii), "Integer") - } - Val::Float(f) => { - eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f - ff), "Float") - } - ref expr => { - return Err(error::BuildError::with_pos( - format!("{} does not support the '-' operation", expr.type_name()), - error::ErrorType::Unsupported, - lpos.clone(), - ) - .to_boxed()); - } - } - } - - fn multiply_vals( - &self, - lpos: &Position, - rpos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match *left { - Val::Int(i) => { - eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i * ii), "Integer") - } - Val::Float(f) => { - eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f * ff), "Float") - } - ref expr => { - return Err(error::BuildError::with_pos( - format!("{} does not support the '*' operation", expr.type_name()), - error::ErrorType::Unsupported, - lpos.clone(), - ) - .to_boxed()); - } - } - } - - fn mod_vals( - &self, - lpos: &Position, - rpos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match *left { - Val::Int(i) => { - eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i % ii), "Integer") - } - Val::Float(f) => { - eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f % ff), "Float") - } - ref expr => { - return Err(error::BuildError::with_pos( - format!( - "{} does not support the 'modulus' operation", - expr.type_name() - ), - error::ErrorType::Unsupported, - lpos.clone(), - ) - .to_boxed()); - } - } - } - - fn divide_vals( - &self, - lpos: &Position, - rpos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match *left { - Val::Int(i) => { - eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i / ii), "Integer") - } - Val::Float(f) => { - eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f / ff), "Float") - } - ref expr => { - return Err(error::BuildError::with_pos( - format!("{} does not support the '*' operation", expr.type_name()), - error::ErrorType::Unsupported, - lpos.clone(), - ) - .to_boxed()); - } - } - } - - fn do_deep_equal( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match left.equal(right.as_ref()) { - Ok(b) => Ok(Rc::new(Val::Boolean(b))), - Err(e) => Err(error::BuildError::with_pos(e.msg, e.err_type, pos.clone()).to_boxed()), - } - } - - fn do_not_deep_equal( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - match left.equal(right.as_ref()) { - Ok(b) => Ok(Rc::new(Val::Boolean(!b))), - Err(e) => Err(error::BuildError::with_pos(e.msg, e.err_type, pos.clone()).to_boxed()), - } - } - - fn do_gt( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - // first ensure that left and right are numeric vals of the same type. - if let &Val::Int(ref l) = left.as_ref() { - if let &Val::Int(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l > r))); - } - } - if let &Val::Float(ref l) = left.as_ref() { - if let &Val::Float(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l > r))); - } - } - Err(error::BuildError::with_pos( - format!("Expected {} but got ({})", left.type_name(), right,), - error::ErrorType::TypeFail, - pos.clone(), - ) - .to_boxed()) - } - - fn do_lt( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - // first ensure that left and right are numeric vals of the same type. - if let &Val::Int(ref l) = left.as_ref() { - if let &Val::Int(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l < r))); - } - } - if let &Val::Float(ref l) = left.as_ref() { - if let &Val::Float(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l < r))); - } - } - Err(error::BuildError::with_pos( - format!("Expected {} but got ({})", left.type_name(), right,), - error::ErrorType::TypeFail, - pos.clone(), - ) - .to_boxed()) - } - - fn do_ltequal( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - if let &Val::Int(ref l) = left.as_ref() { - if let &Val::Int(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l <= r))); - } - } - if let &Val::Float(ref l) = left.as_ref() { - if let &Val::Float(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l <= r))); - } - } - Err(error::BuildError::with_pos( - format!("Expected {} but got ({})", left.type_name(), right), - error::ErrorType::TypeFail, - pos.clone(), - ) - .to_boxed()) - } - - fn do_gtequal( - &self, - pos: &Position, - left: Rc, - right: Rc, - ) -> Result, Box> { - if let &Val::Int(ref l) = left.as_ref() { - if let &Val::Int(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l >= r))); - } - } - if let &Val::Float(ref l) = left.as_ref() { - if let &Val::Float(ref r) = right.as_ref() { - return Ok(Rc::new(Val::Boolean(l >= r))); - } - } - Err(error::BuildError::with_pos( - format!("Expected {} but got ({})", left.type_name(), right,), - error::ErrorType::TypeFail, - pos.clone(), - ) - .to_boxed()) - } - - fn do_dot_lookup(&self, right: &Expression, scope: &Scope) -> Result, Box> { - let pos = right.pos().clone(); - let scope = scope.clone().use_curr_val(); - match right { - Expression::Copy(_) => return self.eval_expr(right, &scope), - Expression::Call(_) => return self.eval_expr(right, &scope), - Expression::Simple(Value::Symbol(ref s)) => { - scope - .lookup_sym(s, true) - .ok_or(Box::new(error::BuildError::with_pos( - format!("Unable to find binding {}", s.val,), - error::ErrorType::NoSuchSymbol, - pos, - ))) - } - Expression::Simple(Value::Str(ref s)) => { - scope - .lookup_sym(s, false) - .ok_or(Box::new(error::BuildError::with_pos( - format!("Unable to find binding {}", s.val,), - error::ErrorType::NoSuchSymbol, - pos, - ))) - } - Expression::Simple(Value::Int(ref i)) => { - scope.lookup_idx(right.pos(), &Val::Int(i.val)) - } - _ => { - // We need to clear any curr_vals for the eval so we don't include them - // in the scope for dot lookups. - let eval_scope = scope.spawn_child(); - let val = self.eval_expr(right, &eval_scope)?; - match val.as_ref() { - Val::Int(i) => scope.lookup_idx(right.pos(), &Val::Int(*i)), - Val::Str(ref s) => scope - .lookup_sym(&PositionedItem::new(s.clone(), pos.clone()), false) - .ok_or(Box::new(error::BuildError::with_pos( - format!("Unable to find binding {}", s,), - error::ErrorType::NoSuchSymbol, - pos, - ))), - _ => Err(error::BuildError::with_pos( - format!("Invalid selector lookup {}", val.type_name(),), - error::ErrorType::NoSuchSymbol, - pos, - ) - .to_boxed()), - } - } - } - } - - fn do_bool_operator( - &self, - kind: &BinaryExprType, - left: &Expression, - right: &Expression, - scope: &Scope, - ) -> Result, Box> { - let left_pos = left.pos(); - let left = self.eval_expr(left, scope)?; - if let Val::Boolean(b) = left.as_ref() { - let right_pos = right.pos(); - let b = *b; - if kind == &BinaryExprType::AND { - if !b { - // short circuit - return Ok(Rc::new(Val::Boolean(b))); - } - let right = self.eval_expr(right, scope)?; - if right.is_bool() { - return Ok(right); - } - } else { - if b { - // short circuit - return Ok(Rc::new(Val::Boolean(b))); - } - let right = self.eval_expr(right, scope)?; - if right.is_bool() { - return Ok(right); - } - } - return Err(error::BuildError::with_pos( - format!( - "Expected boolean value for operator but got ({})", - left.type_name() - ), - error::ErrorType::TypeFail, - right_pos.clone(), - ) - .to_boxed()); - } else { - return Err(error::BuildError::with_pos( - format!( - "Expected boolean value for operator but got ({})", - left.type_name() - ), - error::ErrorType::TypeFail, - left_pos.clone(), - ) - .to_boxed()); - } - } - - fn do_element_check( - &self, - left: &Expression, - right: &Expression, - scope: &Scope, - ) -> Result, Box> { - // First we evaluate our right hand side so we have a something to search - // inside for our left hand expression. - let right_pos = right.pos(); - let right = self.eval_expr(right, scope)?; - // presence checks are only valid for tuples and lists. - if !(right.is_tuple() || right.is_list()) { - return Err(error::BuildError::with_pos( - format!( - "Invalid righthand type for in operator {}", - right.type_name() - ), - error::ErrorType::TypeFail, - right_pos.clone(), - ) - .to_boxed()); - } - if let &Val::List(ref els) = right.as_ref() { - let left = self.eval_expr(left, scope)?; - for val in els.iter() { - if let Ok(b) = self.do_deep_equal(right_pos, left.clone(), val.clone()) { - if let &Val::Boolean(b) = b.as_ref() { - if b { - // We found a match - return Ok(Rc::new(Val::Boolean(true))); - } - } - } - } - // We didn't find a match anywhere so return false. - return Ok(Rc::new(Val::Boolean(false))); - } else { - // Handle our tuple case since this isn't a list. - let child_scope = scope.spawn_child().set_curr_val(right.clone()); - // Search for the field in our tuple or list. - let maybe_val = self.do_dot_lookup(left, &child_scope); - // Return the result of the search. - return Ok(Rc::new(Val::Boolean(maybe_val.is_ok()))); - } - } - - fn eval_re_match( - &self, - left: Rc, - left_pos: &Position, - right: Rc, - right_pos: &Position, - negate: bool, - ) -> Result, Box> { - let re = if let Val::Str(ref s) = right.as_ref() { - regex::Regex::new(s.as_ref())? - } else { - return Err(error::BuildError::with_pos( - format!("Expected string for regex but got ({})", right.type_name()), - error::ErrorType::TypeFail, - right_pos.clone(), - ) - .to_boxed()); - }; - let tgt = if let Val::Str(ref s) = left.as_ref() { - s.as_ref() - } else { - return Err(error::BuildError::with_pos( - format!("Expected string but got ({})", left.type_name()), - error::ErrorType::TypeFail, - left_pos.clone(), - ) - .to_boxed()); - }; - return if negate { - Ok(Rc::new(Val::Boolean(!re.is_match(tgt)))) - } else { - Ok(Rc::new(Val::Boolean(re.is_match(tgt)))) - }; - } - - fn eval_binary(&self, def: &BinaryOpDef, scope: &Scope) -> Result, Box> { - let kind = &def.kind; - if let &BinaryExprType::IN = kind { - return self.do_element_check(&def.left, &def.right, scope); - } - if let &BinaryExprType::IS = kind { - return self.eval_is_check(def, scope); - } - match kind { - // We special case the boolean operators because we want them to short circuit. - &BinaryExprType::AND | &BinaryExprType::OR => { - return self.do_bool_operator(kind, &def.left, &def.right, scope); - } - _ => { - // noop - } - } - let left = self.eval_expr(&def.left, scope)?; - let child_scope = scope.spawn_child().set_curr_val(left.clone()); - if let &BinaryExprType::DOT = kind { - return self.do_dot_lookup(&def.right, &child_scope); - }; - let right = match self.eval_expr(&def.right, scope) { - Ok(v) => v, - Err(e) => return Err(e), - }; - match kind { - // Handle math and concatenation operators here - &BinaryExprType::Add => self.add_vals(&def.pos, def.right.pos(), left, right), - &BinaryExprType::Sub => self.subtract_vals(&def.pos, def.right.pos(), left, right), - &BinaryExprType::Mul => self.multiply_vals(&def.pos, def.right.pos(), left, right), - &BinaryExprType::Div => self.divide_vals(&def.pos, def.right.pos(), left, right), - &BinaryExprType::Mod => self.mod_vals(&def.pos, def.right.pos(), left, right), - // Handle Comparison operators here - &BinaryExprType::Equal => self.do_deep_equal(&def.right.pos(), left, right), - &BinaryExprType::GT => self.do_gt(&def.right.pos(), left, right), - &BinaryExprType::LT => self.do_lt(&def.right.pos(), left, right), - &BinaryExprType::GTEqual => self.do_gtequal(&def.right.pos(), left, right), - &BinaryExprType::LTEqual => self.do_ltequal(&def.right.pos(), left, right), - &BinaryExprType::NotEqual => self.do_not_deep_equal(&def.right.pos(), left, right), - &BinaryExprType::REMatch => { - self.eval_re_match(left, def.left.pos(), right, def.right.pos(), false) - } - &BinaryExprType::NotREMatch => { - self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true) - } - &BinaryExprType::IN - | &BinaryExprType::IS - | &BinaryExprType::DOT - | &BinaryExprType::AND - | &BinaryExprType::OR => panic!("Unreachable"), - } - } - - fn get_outputs_as_val(&mut self) -> Rc { - let fields: Vec<(String, Rc)> = self - .scope - .build_output - .drain() - .map(|v| (v.0.val, v.1)) - .collect(); - Rc::new(Val::Tuple(fields)) - } - - fn copy_fields_from_base( - &self, - src_fields: &Vec<(String, Rc)>, - overrides: &Vec<(Token, Expression)>, - scope: &Scope, - ) -> Result, Box> { - let mut m = HashMap::)>::new(); - // loop through fields and build up a hashmap - let mut count = 0; - for &(ref key, ref val) in src_fields.iter() { - if let Entry::Vacant(v) = m.entry(key.clone()) { - v.insert((count, val.clone())); - count += 1; - } else { - return Err(error::BuildError::new( - format!( - "Duplicate \ - field: {} in \ - tuple", - key - ), - error::ErrorType::TypeFail, - ) - .to_boxed()); - } - } - for &(ref key, ref val) in overrides.iter() { - let expr_result = self.eval_expr(val, scope)?; - match m.entry(key.fragment.clone()) { - // brand new field here. - Entry::Vacant(v) => { - v.insert((count, expr_result)); - count += 1; - } - Entry::Occupied(mut v) => { - // overriding field here. - // Ensure that the new type matches the old type. - let src_val = v.get().clone(); - if src_val.1.type_equal(&expr_result) - || src_val.1.is_empty() - || expr_result.is_empty() - { - v.insert((src_val.0, expr_result)); - } else { - return Err(error::BuildError::with_pos( - format!( - "Expected type {} for field {} but got ({})", - src_val.1.type_name(), - key.fragment, - expr_result.type_name() - ), - error::ErrorType::TypeFail, - key.pos.clone(), - ) - .to_boxed()); - } - } - }; - } - let mut new_fields: Vec<(String, (i32, Rc))> = m.drain().collect(); - // We want to maintain our order for the fields to make comparing tuples - // easier in later code. So we sort by the field order before constructing a new tuple. - new_fields.sort_by(|a, b| { - let ta = a.1.clone(); - let tb = b.1.clone(); - ta.0.cmp(&tb.0) - }); - return Ok(Rc::new(Val::Tuple( - new_fields - .iter() - .map(|a| { - let first = a.0.clone(); - let t = a.1.clone(); - (first, t.1) - }) - .collect(), - ))); - } - - fn eval_module_copy( - &self, - def: &CopyDef, - mod_def: &ModuleDef, - scope: &Scope, - ) -> Result, Box> { - let maybe_tpl = mod_def.arg_tuple.as_ref().unwrap().clone(); - if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() { - // 1. First we create a builder. - let mut b = self.clone_builder(); - b.is_module = true; - // 2. We construct an argument tuple by copying from the defs - // argset. - // Push our base tuple on the stack so the copy can use - // self to reference it. - let child_scope = scope.spawn_child().set_curr_val(maybe_tpl.clone()); - let mut overrides = Vec::new(); - if let Some(ref path) = mod_def.pos.file { - overrides.push(( - Token::new("pkg", TokenType::BAREWORD, def.pos.clone()), - Expression::Func(FuncDef { - scope: None, - argdefs: Vec::new(), - fields: Box::new(Expression::Import(ImportDef { - pos: def.pos.clone(), - path: Token::new( - path.to_string_lossy().to_string(), - TokenType::QUOTED, - def.pos.clone(), - ), - })), - pos: def.pos.clone(), - }), - )); - } - overrides.push(( - Token::new("this", TokenType::BAREWORD, def.pos.clone()), - Expression::Module(mod_def.clone()), - )); - overrides.extend(def.fields.iter().cloned()); - let mod_args = self.copy_fields_from_base(src_fields, &overrides, &child_scope)?; - // put our copied parameters tuple in our builder under the mod key. - let mod_key = PositionedItem::new_with_pos(String::from("mod"), def.pos.clone()); - match b.scope.build_output.entry(mod_key) { - Entry::Occupied(e) => { - return Err(error::BuildError::with_pos( - format!( - "Binding \ - for {:?} already \ - exists in module", - e.key(), - ), - error::ErrorType::DuplicateBinding, - mod_def.pos.clone(), - ) - .to_boxed()); - } - Entry::Vacant(e) => { - e.insert(mod_args.clone()); - } - } - // 4. Evaluate all the statements using the builder. - b.eval_stmts(&mod_def.statements)?; - if let Some(ref expr) = mod_def.out_expr { - // 5. Produce the out expression in the context of the statements - // we evaluated previously. - return b.eval_expr(expr, &b.scope); - } else { - // 5. Take all of the bindings in the module and construct a new - // tuple using them. - return Ok(b.get_outputs_as_val()); - } - } else { - return Err(error::BuildError::with_pos( - format!( - "Weird value stored in our module parameters slot {:?}", - mod_def.arg_tuple - ), - error::ErrorType::TypeFail, - def.selector.pos().clone(), - ) - .to_boxed()); - } - } - - fn eval_copy(&self, def: &CopyDef, scope: &Scope) -> Result, Box> { - let v = self.eval_value(&def.selector, scope)?; - if let &Val::Tuple(ref src_fields) = v.as_ref() { - let child_scope = scope.spawn_child().set_curr_val(v.clone()); - return self.copy_fields_from_base(&src_fields, &def.fields, &child_scope); - } - if let &Val::Module(ref mod_def) = v.as_ref() { - return self.eval_module_copy(def, mod_def, scope); - } - Err(error::BuildError::with_pos( - format!("Expected Tuple or Module but got ({})", v), - error::ErrorType::TypeFail, - def.selector.pos().clone(), - ) - .to_boxed()) - } - - fn eval_format(&self, def: &FormatDef, scope: &Scope) -> Result, Box> { - let tmpl = &def.template; - return match &def.args { - FormatArgs::List(ref args) => { - let mut vals = Vec::new(); - for v in args.iter() { - let rcv = self.eval_expr(v, scope)?; - vals.push(rcv.deref().clone()); - } - let formatter = SimpleFormatter::new(tmpl.clone(), vals); - Ok(Rc::new(Val::Str(formatter.render(&def.pos)?))) - } - FormatArgs::Single(ref expr) => { - let val = self.eval_expr(expr, scope)?; - let mut builder = self.clone_builder(); - builder.scope.build_output.insert( - PositionedItem::new("item".to_string(), expr.pos().clone()), - val, - ); - let formatter = ExpressionFormatter::new(tmpl.clone(), builder); - Ok(Rc::new(Val::Str(formatter.render(&def.pos)?))) - } - }; - } - - fn eval_call(&self, def: &CallDef, scope: &Scope) -> Result, Box> { - let args = &def.arglist; - let v = self.eval_value(&def.funcref, scope)?; - let call_pos = def.pos.clone(); - if let &Val::Func(ref def) = v.deref() { - // Congratulations this is actually a function. - let mut argvals: Vec> = Vec::new(); - for arg in args.iter() { - argvals.push(self.eval_expr(arg, scope)?); - } - return match def.eval(self, argvals) { - Ok(v) => Ok(v), - Err(e) => Err(error::BuildError::with_pos( - format!("Func evaluation failed\nCaused by:\n\t{}", e), - error::ErrorType::TypeFail, - call_pos, - ) - .to_boxed()), - }; - } - Err(error::BuildError::with_pos( - // We should pretty print the selectors here. - format!("{} is not a Function", v), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()) - } - - fn eval_func_def(&self, def: &mut FuncDef, scope: &Scope) -> Result, Box> { - def.scope = Some(scope.spawn_child()); - Ok(Rc::new(Val::Func(def.clone()))) - } - - fn file_dir(&self) -> PathBuf { - return if self.working_dir.is_file() { - // Only use the dirname portion if the root is a file. - self.working_dir.parent().unwrap().to_path_buf() - } else { - // otherwise use clone of the root. - self.working_dir.clone() - }; - } - - fn eval_module_def(&self, def: &ModuleDef, scope: &Scope) -> Result, Box> { - let root = self.file_dir(); - // Always work on a copy. The original should not be modified. - let mut def = def.clone(); - // First we rewrite the imports to be absolute paths. - def.imports_to_absolute(root); - // Then we create our tuple default. - if def.arg_tuple.is_none() { - // NOTE: This is an ugly hack to enable recursive references to work - // in eval_module_copy. We should really fix out DataModel to properly - // handle this but for now this should work. - def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?); - } - // Then we construct a new Val::Module - Ok(Rc::new(Val::Module(def))) - } - - fn eval_select(&self, def: &SelectDef, scope: &Scope) -> Result, Box> { - let target = &def.val; - let fields = &def.tuple; - // First resolve the target expression. - let v = self.eval_expr(target, scope)?; - // Second ensure that the expression resolves to a string. - if let &Val::Str(ref name) = v.deref() { - // Third find the field with that name in the tuple. - for &(ref fname, ref val_expr) in fields.iter() { - if &fname.fragment == name { - // Fourth return the result of evaluating that field. - return self.eval_expr(val_expr, scope); - } - } - // Otherwise return the default. - match def.default.as_ref() { - Some(e) => self.eval_expr(e, scope), - None => { - return Err(error::BuildError::with_pos( - format!("Unhandled select case \"{}\" with no default", name), - error::ErrorType::TypeFail, - def.val.pos().clone(), - ) - .to_boxed()); - } - } - } else if let &Val::Boolean(b) = v.deref() { - for &(ref fname, ref val_expr) in fields.iter() { - if &fname.fragment == "true" && b { - // Fourth return the result of evaluating that field. - return self.eval_expr(val_expr, scope); - } else if &fname.fragment == "false" && !b { - return self.eval_expr(val_expr, scope); - } - } - // Otherwise return the default. - match def.default.as_ref() { - Some(e) => self.eval_expr(e, scope), - None => { - return Err(error::BuildError::with_pos( - format!("Unhandled select case {} with no default", b), - error::ErrorType::TypeFail, - def.val.pos().clone(), - ) - .to_boxed()); - } - } - } else { - return Err(error::BuildError::with_pos( - format!( - "Expected String but got \ - {} in Select expression", - v.type_name() - ), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - } - - fn eval_functional_list_processing( - &self, - elems: &Vec>, - def: &FuncDef, - typ: ProcessingOpType, - ) -> Result, Box> { - let mut out = Vec::new(); - for item in elems.iter() { - let argvals = vec![item.clone()]; - let val = def.eval(self, argvals)?; - match typ { - ProcessingOpType::Map => { - out.push(val.clone()); - } - ProcessingOpType::Filter => { - if let &Val::Empty = val.as_ref() { - // noop - continue; - } else if let &Val::Boolean(false) = val.as_ref() { - // noop - continue; - } - out.push(item.clone()); - } - } - } - return Ok(Rc::new(Val::List(out))); - } - - fn eval_functional_tuple_processing( - &self, - fs: &Vec<(String, Rc)>, - def: &FuncDef, - typ: ProcessingOpType, - ) -> Result, Box> { - let mut out = Vec::new(); - for &(ref name, ref val) in fs { - let argvals = vec![Rc::new(Val::Str(name.clone())), val.clone()]; - let result = def.eval(self, argvals)?; - match typ { - ProcessingOpType::Map => { - if let &Val::List(ref fs) = result.as_ref() { - if fs.len() == 2 { - // index 0 should be a string for the new field name. - // index 1 should be the val. - let new_name = if let &Val::Str(ref s) = fs[0].as_ref() { - s.clone() - } else { - return Err(error::BuildError::with_pos( - format!( - "map on tuple expects the first item out list to be a string but got size {}", - fs[0].type_name() - ), - error::ErrorType::TypeFail, - def.pos.clone(), - ).to_boxed()); - }; - out.push((new_name, fs[1].clone())); - } else { - return Err(error::BuildError::with_pos( - format!( - "map on a tuple field expects a list of size 2 as output but got size {}", - fs.len() - ), - error::ErrorType::TypeFail, - def.pos.clone(), - ).to_boxed()); - } - } else { - return Err(error::BuildError::with_pos( - format!( - "map on a tuple field expects a list as output but got ({})", - result.type_name() - ), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - } - ProcessingOpType::Filter => { - if let &Val::Empty = result.as_ref() { - // noop - continue; - } else if let &Val::Boolean(false) = result.as_ref() { - // noop - continue; - } - out.push((name.clone(), val.clone())); - } - } - } - Ok(Rc::new(Val::Tuple(out))) - } - - fn eval_reduce_op(&self, def: &ReduceOpDef, scope: &Scope) -> Result, Box> { - let maybe_target = self.eval_expr(&def.target, scope)?; - let mut acc = self.eval_expr(&def.acc, scope)?; - let maybe_mac = self.eval_expr(&def.func, scope)?; - let funcdef = match maybe_mac.as_ref() { - &Val::Func(ref funcdef) => funcdef, - _ => { - return Err(error::BuildError::with_pos( - format!("Expected func but got {:?}", def.func), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - }; - match maybe_target.as_ref() { - &Val::List(ref elems) => { - for item in elems.iter() { - let argvals = vec![acc.clone(), item.clone()]; - let result = funcdef.eval(self, argvals)?; - acc = result; - } - } - &Val::Tuple(ref fs) => { - for &(ref name, ref val) in fs.iter() { - let argvals = vec![acc.clone(), Rc::new(Val::Str(name.clone())), val.clone()]; - let result = funcdef.eval(self, argvals)?; - acc = result; - } - } - &Val::Str(ref s) => { - for gc in s.graphemes(true) { - let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))]; - let result = funcdef.eval(self, argvals)?; - acc = result; - } - } - other => { - return Err(error::BuildError::with_pos( - format!( - "Expected List Str, or Tuple as target but got {:?}", - other.type_name() - ), - error::ErrorType::TypeFail, - def.target.pos().clone(), - ) - .to_boxed()); - } - } - Ok(acc) - } - - fn eval_functional_string_processing( - &self, - s: &str, - def: &FuncDef, - typ: ProcessingOpType, - ) -> Result, Box> { - let mut result = String::new(); - for gc in s.graphemes(true) { - let arg = Rc::new(Val::Str(gc.to_string())); - let out = def.eval(self, vec![arg])?; - match typ { - ProcessingOpType::Filter => { - match out.as_ref() { - Val::Boolean(b) => { - if *b { - result.push_str(gc); - } - } - Val::Empty => { - // noop - } - _ => { - return Err(error::BuildError::with_pos( - format!( - "Expected boolean or NULL for filter return but got ({})", - out.type_name() - ), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - } - } - ProcessingOpType::Map => match out.as_ref() { - Val::Str(s) => { - result.push_str(&s); - } - _ => { - return Err(error::BuildError::with_pos( - format!("Expected string map return but got ({})", out.type_name()), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - }, - } - } - Ok(Rc::new(Val::Str(result))) - } - - fn eval_functional_processing( - &self, - def: &MapFilterOpDef, - typ: ProcessingOpType, - scope: &Scope, - ) -> Result, Box> { - let maybe_target = self.eval_expr(&def.target, scope)?; - let maybe_mac = self.eval_expr(&def.func, scope)?; - let macdef = match maybe_mac.as_ref() { - &Val::Func(ref macdef) => macdef, - _ => { - return Err(error::BuildError::with_pos( - format!("Expected func but got {:?}", def.func), - error::ErrorType::TypeFail, - def.pos.clone(), - ) - .to_boxed()); - } - }; - return match maybe_target.as_ref() { - &Val::List(ref elems) => self.eval_functional_list_processing(elems, macdef, typ), - &Val::Tuple(ref fs) => self.eval_functional_tuple_processing(fs, macdef, typ), - &Val::Str(ref s) => self.eval_functional_string_processing(s, macdef, typ), - other => Err(error::BuildError::with_pos( - format!( - "Expected List or Tuple as target but got {:?}", - other.type_name() - ), - error::ErrorType::TypeFail, - def.target.pos().clone(), - ) - .to_boxed()), - }; - } - - fn eval_assert(&mut self, expr: &Expression, scope: &Scope) -> Result, Box> { - if !self.validate_mode { - // we are not in validate_mode so build_asserts are noops. - return Ok(Rc::new(Val::Empty)); - } - let mut buffer: Vec = Vec::new(); - { - let mut printer = crate::ast::printer::AstPrinter::new(2, &mut buffer); - let _ = printer.render_expr(expr); - } - let expr_pretty = String::from_utf8(buffer).unwrap(); - let ok = match self.eval_expr(expr, scope) { - Ok(v) => v, - Err(e) => { - // failure! - let msg = format!("CompileError: {}\nfor expression:\n{}\n", e, expr_pretty); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - }; - - match ok.as_ref() { - &Val::Tuple(ref fs) => { - let ok_field = match find_in_fieldlist("ok", fs) { - Some(ref val) => match val.as_ref() { - &Val::Boolean(b) => b, - _ => { - let msg = format!( - "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}", - ok.as_ref(), expr.pos().line, expr.pos().column - ); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - }, - None => { - let msg = format!( - "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}", - ok.as_ref(), expr.pos().line, expr.pos().column - ); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - }; - let desc = match find_in_fieldlist("desc", fs) { - Some(ref val) => match val.as_ref() { - Val::Str(ref s) => s.clone(), - _ => { - let msg = format!( - "TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}", - ok, expr.pos().line, expr.pos().column - ); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - }, - None => { - let msg = format!( - "TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}\n", - ok, expr.pos().line, expr.pos().column - ); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - }; - self.assert_collector.record_assert_result(&desc, ok_field); - } - &Val::Empty - | &Val::Boolean(_) - | &Val::Env(_) - | &Val::Float(_) - | &Val::Int(_) - | &Val::Str(_) - | &Val::List(_) - | &Val::Func(_) - | &Val::Module(_) => { - // record an assertion type-failure result. - let msg = format!( - "TYPE FAIL - Expected tuple with ok and desc fields got {} at line: {} column: {}\n", - ok, expr.pos().line, expr.pos().column - ); - self.assert_collector.record_assert_result(&msg, false); - return Ok(Rc::new(Val::Empty)); - } - } - Ok(ok) - } - fn open_file>(&self, pos: &Position, path: P) -> Result> { let path = path.into(); match File::open(&path) { @@ -1806,257 +177,77 @@ where } } - fn get_file_as_string(&self, pos: &Position, path: &str) -> Result> { - let sep = format!("{}", std::path::MAIN_SEPARATOR); - let raw_path = path.replace("/", &sep); - let normalized = match self.find_file(raw_path, false) { - Ok(p) => p, - Err(e) => { - return Err(error::BuildError::with_pos( - format!("Error finding file {} {}", path, e), - error::ErrorType::TypeFail, - pos.clone(), - ) - .to_boxed()); - } - }; - let mut f = self.open_file(pos, normalized)?; - let mut contents = String::new(); - f.read_to_string(&mut contents)?; - Ok(contents) + /// Puts the builder in validation mode. + /// + /// Among other things this means that assertions will be evaluated and their results + /// will be saved in a report for later output. + pub fn enable_validate_mode(&mut self) { + self.validate_mode = true; } - pub fn eval_include(&self, def: &IncludeDef) -> Result, Box> { - return if def.typ.fragment == "str" { - Ok(Rc::new(Val::Str( - self.get_file_as_string(&def.path.pos, &def.path.fragment)?, - ))) - } else { - let maybe_importer = self.import_registry.get_importer(&def.typ.fragment); - match maybe_importer { - Some(importer) => { - let file_contents = - self.get_file_as_string(&def.path.pos, &def.path.fragment)?; - let val = if file_contents.len() == 0 { - eprintln!("including an empty file. Use NULL as the result"); - Rc::new(Val::Empty) - } else { - match importer.import(file_contents.as_bytes()) { - Ok(v) => v, - Err(e) => { - let err = Box::new(error::BuildError::with_pos( - format!( - "{} include failed for {}", - &def.typ.fragment, &def.path.fragment - ), - error::ErrorType::IncludeError, - def.pos.clone(), - )); - return Err(err.wrap_cause(e).to_boxed()); - } - } - }; - Ok(val) + /// Builds a list of parsed UCG Statements. + pub fn eval_stmts(&mut self, ast: Vec) -> BuildResult { + // We should probably stash this in an op_cache somewhere? + let ops = translate::AST::translate(ast, &self.working_dir); + let mut vm = VM::new(Rc::new(ops), self.environment.clone()); + if self.validate_mode { + vm.enable_validate_mode(); + } + vm.run()?; + self.out = Some(Rc::new(vm.symbols_to_tuple(false).into())); + Ok(()) + } + + pub fn eval_input(&mut self, input: OffsetStrIter) -> Result, Box> { + match parse(input.clone(), None) { + Ok(stmts) => { + self.eval_stmts(stmts)?; + if let Some(v) = self.out.clone() { + return Ok(v); } - None => Err(error::BuildError::with_pos( - format!("Unknown include conversion type {}", def.typ.fragment), - error::ErrorType::Unsupported, - def.typ.pos.clone(), - ) - .to_boxed()), + unreachable!(); } - }; - } - - fn eval_func_op(&self, def: &FuncOpDef, scope: &Scope) -> Result, Box> { - match def { - FuncOpDef::Filter(ref def) => { - self.eval_functional_processing(def, ProcessingOpType::Filter, scope) - } - FuncOpDef::Map(ref def) => { - self.eval_functional_processing(def, ProcessingOpType::Map, scope) - } - FuncOpDef::Reduce(ref def) => self.eval_reduce_op(def, scope), + Err(err) => Err(Box::new(err)), } } - pub fn eval_range(&self, def: &RangeDef, scope: &Scope) -> Result, Box> { - let start = self.eval_expr(&def.start, scope)?; - let start = match start.as_ref() { - &Val::Int(i) => i, - _ => { - return Err(error::BuildError::with_pos( - format!( - "Expected an integer for range start but got ({})", - start.type_name() - ), - error::ErrorType::TypeFail, - def.start.pos().clone(), - ) - .to_boxed()); - } + /// Evaluate an input string as UCG. + pub fn eval_string(&mut self, input: &str) -> Result, Box> { + self.eval_input(OffsetStrIter::new(input)) + } + + pub fn eval_expr(&mut self, expr: Expression) -> Result, Box> { + let mut ops_map = translate::PositionMap { + ops: Vec::new(), + pos: Vec::new(), }; - // See if there was a step. - let step = match &def.step { - Some(step) => { - let step = self.eval_expr(&step, scope)?; - match step.as_ref() { - &Val::Int(i) => i, - _ => { - return Err(error::BuildError::with_pos( - format!( - "Expected an integer for range step but got ({})", - step.type_name() - ), - error::ErrorType::TypeFail, - def.start.pos().clone(), - ) - .to_boxed()); + translate::AST::translate_stmt( + Statement::Expression(expr), + &mut ops_map, + &self.working_dir, + ); + let mut vm = VM::new(Rc::new(ops_map), self.environment.clone()); + if self.validate_mode { + vm.enable_validate_mode(); + } + vm.run()?; + if let Some((val, _)) = vm.last.clone() { + return Ok(Rc::new(val.try_into()?)); + } + unreachable!(); + } + + pub fn get_out_by_name(&self, name: &str) -> Option> { + if let Some(val) = self.out.clone() { + if let &Val::Tuple(ref flds) = val.as_ref() { + for (k, v) in flds.iter() { + if k == name { + return Some(v.clone()); } } } - None => 1, - }; - - // Get the end. - let end = self.eval_expr(&def.end, scope)?; - let end = match end.as_ref() { - &Val::Int(i) => i, - _ => { - return Err(error::BuildError::with_pos( - format!( - "Expected an integer for range start but got ({})", - end.type_name() - ), - error::ErrorType::TypeFail, - def.start.pos().clone(), - ) - .to_boxed()); - } - }; - - let vec = (start..end + 1) - .step_by(step as usize) - .map(|i| Rc::new(Val::Int(i))) - .collect(); - Ok(Rc::new(Val::List(vec))) - } - - pub fn eval_is_check( - &self, - def: &BinaryOpDef, - scope: &Scope, - ) -> Result, Box> { - let tval = self.eval_expr(def.right.as_ref(), scope)?; - let typ = match tval.as_ref() { - Val::Str(ref s) => s.clone(), - _ => { - return Err(error::BuildError::with_pos( - format!("Expected string expression but got ({})", tval), - error::ErrorType::TypeFail, - def.right.pos().clone(), - ) - .to_boxed()); - } - }; - let val = self.eval_expr(def.left.as_ref(), scope)?; - let result = match typ.as_str() { - "str" => val.is_str(), - "bool" => val.is_bool(), - "null" => val.is_empty(), - "int" => val.is_int(), - "float" => val.is_float(), - "tuple" => val.is_tuple(), - "list" => val.is_list(), - "func" => val.is_func(), - "module" => val.is_module(), - other => { - return Err(error::BuildError::with_pos( - format!("Expected valid type name but got ({})", other), - error::ErrorType::TypeFail, - def.right.pos().clone(), - ) - .to_boxed()); - } - }; - Ok(Rc::new(Val::Boolean(result))) - } - - // Evals a single Expression in the context of a running Builder. - // It does not mutate the builders collected state at all. - pub fn eval_expr(&self, expr: &Expression, scope: &Scope) -> Result, Box> { - match expr { - &Expression::Simple(ref val) => self.eval_value(val, scope), - &Expression::Binary(ref def) => self.eval_binary(def, scope), - &Expression::Copy(ref def) => self.eval_copy(def, scope), - &Expression::Range(ref def) => self.eval_range(def, scope), - &Expression::Grouped(ref expr, _) => self.eval_expr(expr, scope), - &Expression::Format(ref def) => self.eval_format(def, scope), - &Expression::Call(ref def) => self.eval_call(def, scope), - &Expression::Func(ref def) => { - let mut def_clone = def.clone(); - self.eval_func_def(&mut def_clone, scope) - } - &Expression::Module(ref def) => { - let mut def_clone = def.clone(); - self.eval_module_def(&mut def_clone, scope) - } - &Expression::Select(ref def) => self.eval_select(def, scope), - &Expression::FuncOp(ref def) => self.eval_func_op(def, scope), - &Expression::Include(ref def) => self.eval_include(def), - &Expression::Import(ref def) => self.eval_import(def), - &Expression::Fail(ref def) => { - let val = self.eval_expr(&def.message, scope)?; - return if let Val::Str(ref s) = val.as_ref() { - Err(error::BuildError::with_pos( - s.clone(), - error::ErrorType::UserDefined, - def.pos.clone(), - ) - .to_boxed()) - } else { - Err(error::BuildError::with_pos( - format!( - "Expected string for message but got ({})", - def.message.as_ref() - ), - error::ErrorType::TypeFail, - def.message.pos().clone(), - ) - .to_boxed()) - }; - } - &Expression::Debug(ref def) => { - let mut buffer: Vec = Vec::new(); - { - let mut printer = crate::ast::printer::AstPrinter::new(2, &mut buffer); - let _ = printer.render_expr(&def.expr); - } - let expr_pretty = String::from_utf8(buffer).unwrap(); - - let val = self.eval_expr(&def.expr, scope); - if let Ok(ref val) = val { - eprintln!("TRACE: {} = {} at {}", expr_pretty, val, def.pos); - } - val - } - &Expression::Not(ref def) => { - let val = self.eval_expr(&def.expr, scope)?; - return if let Val::Boolean(b) = val.as_ref() { - Ok(Rc::new(Val::Boolean(!b))) - } else { - Err(error::BuildError::with_pos( - format!( - "Expected boolean for expression but got ({})", - def.expr.as_ref() - ), - error::ErrorType::TypeFail, - def.expr.pos().clone(), - ) - .to_boxed()) - }; - } } + return None; } } diff --git a/src/build/opcode/debug.rs b/src/build/opcode/debug.rs new file mode 100644 index 0000000..5ae9481 --- /dev/null +++ b/src/build/opcode/debug.rs @@ -0,0 +1,53 @@ +// Copyright 2019 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt; + +use super::Composite; +use super::Primitive; +use super::Value; + +use Composite::{List, Tuple}; +use Primitive::{Bool, Empty, Float, Int, Str}; +use Value::{C, F, M, P, S, T}; + +impl fmt::Debug for Value { + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + match self { + P(Bool(v)) => write!(w, "Bool({})", v), + P(Int(v)) => write!(w, "Int({})", v), + P(Float(v)) => write!(w, "Float({})", v), + P(Str(v)) => write!(w, "String({})", v), + P(Empty) => write!(w, "NULL"), + C(List(ref els)) => { + write!(w, "List[")?; + for e in els { + write!(w, "{:?},", e)?; + } + write!(w, "]") + } + C(Tuple(ref flds)) => { + write!(w, "Tuple(")?; + for (k, v) in flds { + write!(w, "\"{}\"={:?},", k, v)?; + } + write!(w, ")") + } + F(_) => write!(w, ""), + M(_) => write!(w, ""), + T(_) => write!(w, ""), + S(v) => write!(w, "Symbol({})", v), + } + } +} diff --git a/src/build/opcode/environment.rs b/src/build/opcode/environment.rs index de4a8de..5ae7e2a 100644 --- a/src/build/opcode/environment.rs +++ b/src/build/opcode/environment.rs @@ -12,12 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. use std::collections::BTreeMap; -use std::io::Write; +use std::io::{Write, Read}; use std::rc::Rc; +use std::path::PathBuf; +use std::fs::File; +use super::pointer::OpPointer; use super::cache; use super::Value; +use super::Error; use crate::convert::{ConverterRegistry, ImporterRegistry}; +use crate::iter::OffsetStrIter; +use crate::parse::parse; // Shared Environmental between VM's for runtime usage. pub struct Environment @@ -26,18 +32,23 @@ where Stderr: Write, { pub val_cache: BTreeMap>, - pub op_cache: cache::Ops, // Shared environment - pub converter_registry: ConverterRegistry, // Shared environment - pub importer_registry: ImporterRegistry, // Shared environment - pub stdout: Stdout, // Shared environment - pub stderr: Stderr, // Shared environment - // TODO(jwall): Environment Variables + pub op_cache: cache::Ops, + pub converter_registry: ConverterRegistry, + pub importer_registry: ImporterRegistry, + pub stdout: Stdout, + pub stderr: Stderr, + pub env_vars: BTreeMap, // Environment Variables } impl Environment { pub fn new(out: Stdout, err: Stderr) -> Self { + Self::new_with_vars(out, err, BTreeMap::new()) + } + + pub fn new_with_vars(out: Stdout, err: Stderr, vars: BTreeMap) -> Self { Self { val_cache: BTreeMap::new(), + env_vars: vars, op_cache: cache::Ops::new(), converter_registry: ConverterRegistry::make_registry(), importer_registry: ImporterRegistry::make_registry(), @@ -45,4 +56,34 @@ impl Environment { stderr: err, } } + + pub fn get_cached_path_val(&self, path: &String) -> Option> { + self.val_cache.get(path).cloned() + } + + pub fn update_path_val(&mut self, path: &String, val: Rc) { + self.val_cache.insert(path.clone(), val); + } + + pub fn get_ops_for_path(&mut self, path: &String) -> Result { + let op_pointer = self.op_cache.entry(path).get_pointer_or_else( + || { + // FIXME(jwall): We need to do proper error handling here. + let p = PathBuf::from(&path); + let root = p.parent().unwrap(); + // first we read in the file + let mut f = File::open(&path).unwrap(); + // then we parse it + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + let iter = OffsetStrIter::new(&contents).with_src_file(&p); + let stmts = parse(iter, None).unwrap(); + // then we create an ops from it + let ops = super::translate::AST::translate(stmts, &root); + ops + }, + &path, + ); + Ok(op_pointer) + } } diff --git a/src/build/opcode/error.rs b/src/build/opcode/error.rs index 7ee54d6..c226e77 100644 --- a/src/build/opcode/error.rs +++ b/src/build/opcode/error.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. use std::convert::From; +use std::fmt; +use std::fmt::Display; use crate::ast::Position; @@ -30,14 +32,32 @@ impl Error { } } -impl From for Error -where - E: std::error::Error + Sized, -{ - fn from(e: E) -> Self { +impl From for Error { + fn from(e: regex::Error) -> Self { Error { - message: e.description().to_owned(), + message: format!("{}", e), pos: None, } } } + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error { + message: format!("{}", e), + pos: None, + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref pos) = self.pos { + write!(f, "{} at {}", self.message, pos) + } else { + write!(f, "{}", self.message) + } + } +} + +impl std::error::Error for Error {} \ No newline at end of file diff --git a/src/build/opcode/mod.rs b/src/build/opcode/mod.rs index 38ba37b..8bd8987 100644 --- a/src/build/opcode/mod.rs +++ b/src/build/opcode/mod.rs @@ -15,14 +15,16 @@ use std::convert::{TryFrom, TryInto}; use std::rc::Rc; mod cache; -mod environment; +mod debug; +pub mod environment; mod error; pub mod pointer; mod runtime; pub mod scope; -mod translate; +pub mod translate; mod vm; +pub use environment::Environment; pub use error::Error; pub use vm::VM; @@ -43,6 +45,24 @@ pub enum Primitive { use Primitive::{Bool, Empty, Float, Int, Str}; +impl Value { + fn type_name(&self) -> &'static str { + match self { + P(Int(_)) => "Int", + P(Float(_)) => "Float", + P(Str(_)) => "String", + P(Bool(_)) => "Bool", + P(Empty) => "NULL", + C(List(_)) => "List", + C(Tuple(_)) => "Tuple", + F(_) => "Func", + M(_) => "Func", + T(_) => "Expression", + S(_) => "Symbol", + } + } +} + impl From<&Primitive> for String { fn from(p: &Primitive) -> Self { match p { @@ -107,7 +127,7 @@ pub struct Module { pkg_ptr: Option, } -#[derive(Debug, PartialEq, Clone)] +#[derive(PartialEq, Clone)] pub enum Value { // Binding names. S(String), @@ -218,29 +238,30 @@ pub enum Op { use super::ir::Val; -impl TryFrom> for Val { - type Error = Error; - - fn try_from(val: Rc) -> Result { - val.as_ref().try_into() +impl From> for Val { + fn from(val: Rc) -> Val { + val.as_ref().into() } } -impl TryFrom<&Value> for Val { - type Error = Error; +impl From for Val { + fn from(val: Value) -> Val { + (&val).into() + } +} - fn try_from(val: &Value) -> Result { - Ok(match val { +impl From<&Value> for Val { + fn from(val: &Value) -> Val { + match val { P(Int(i)) => Val::Int(*i), P(Float(f)) => Val::Float(*f), P(Str(s)) => Val::Str(s.clone()), P(Bool(b)) => Val::Boolean(*b), - P(Empty) => Val::Empty, C(Tuple(fs)) => { let mut flds = Vec::new(); for &(ref k, ref v) in fs.iter() { let v = v.clone(); - flds.push((k.clone(), Rc::new(v.try_into()?))); + flds.push((k.clone(), Rc::new(v.into()))); } Val::Tuple(flds) } @@ -248,33 +269,30 @@ impl TryFrom<&Value> for Val { let mut els = Vec::new(); for e in elems.iter() { let e = e.clone(); - els.push(Rc::new(e.try_into()?)); + els.push(Rc::new(e.into())); } Val::List(els) } - S(_) | F(_) | M(_) | T(_) => { - return Err(dbg!(Error::new( - format!("Invalid Value {:?} to Val translation", val), - Position::new(0, 0, 0) - ))); - } - }) + S(_) | F(_) | M(_) | T(_) | P(Empty) => Val::Empty, + } } } -impl TryFrom> for Value { - type Error = Error; - - fn try_from(val: Rc) -> Result { - val.as_ref().try_into() +impl From> for Value { + fn from(val: Rc) -> Self { + val.as_ref().into() } } -impl TryFrom<&Val> for Value { - type Error = Error; +impl From for Value { + fn from(val: Val) -> Self { + (&val).into() + } +} - fn try_from(val: &Val) -> Result { - Ok(match val { +impl From<&Val> for Value { + fn from(val: &Val) -> Self { + match val { Val::Int(i) => P(Int(*i)), Val::Float(f) => P(Float(*f)), Val::Boolean(b) => P(Bool(*b)), @@ -284,7 +302,7 @@ impl TryFrom<&Val> for Value { let mut lst = Vec::new(); for e in els.iter() { let e = e.clone(); - lst.push(Rc::new(e.try_into()?)); + lst.push(Rc::new(e.into())); } C(List(lst)) } @@ -292,7 +310,7 @@ impl TryFrom<&Val> for Value { let mut field_list = Vec::new(); for &(ref key, ref val) in flds.iter() { let val = val.clone(); - field_list.push((key.clone(), Rc::new(val.try_into()?))); + field_list.push((key.clone(), Rc::new(val.into()))); } C(Tuple(field_list)) } @@ -303,15 +321,7 @@ impl TryFrom<&Val> for Value { } C(Tuple(field_list)) } - // TODO(jwall): These can go away eventually when we replace the tree - // walking interpreter. - Val::Module(_) | Val::Func(_) => { - return Err(dbg!(Error::new( - format!("Invalid Translation from Val {} to Value", val), - Position::new(0, 0, 0) - ))) - } - }) + } } } diff --git a/src/build/opcode/pointer.rs b/src/build/opcode/pointer.rs index 9095b90..8d9b00a 100644 --- a/src/build/opcode/pointer.rs +++ b/src/build/opcode/pointer.rs @@ -60,13 +60,13 @@ impl OpPointer { self.ptr = Some(ptr); return Ok(()); } - Err(dbg!(Error::new( + Err(Error::new( format!("FAULT!!! Invalid Jump!"), match self.pos() { Some(pos) => pos.clone(), None => Position::new(0, 0, 0), }, - ))) + )) } pub fn op(&self) -> Option<&Op> { @@ -86,10 +86,10 @@ impl OpPointer { pub fn idx(&self) -> Result { match self.ptr { Some(ptr) => Ok(ptr), - None => dbg!(Err(Error::new( + None => Err(Error::new( format!("FAULT!!! Position Check failure!"), Position::new(0, 0, 0), - ))), + )), } } diff --git a/src/build/opcode/runtime.rs b/src/build/opcode/runtime.rs index 7b637bb..a049e69 100644 --- a/src/build/opcode/runtime.rs +++ b/src/build/opcode/runtime.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. use std::cell::RefCell; -use std::convert::{TryFrom, TryInto}; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; @@ -27,8 +26,6 @@ use super::{Composite, Error, Hook, Primitive, Value}; use crate::ast::Position; use crate::build::ir::Val; use crate::build::AssertCollector; -use crate::iter::OffsetStrIter; -use crate::parse::parse; use Composite::{List, Tuple}; use Primitive::{Bool, Empty, Int, Str}; @@ -36,6 +33,7 @@ pub struct Builtins { assert_results: AssertCollector, working_dir: PathBuf, import_path: Vec, + validate_mode: bool, } impl Builtins { @@ -47,7 +45,9 @@ impl Builtins { Self { assert_results: AssertCollector::new(), working_dir: path.into(), + // FIXME(jwall): This should probably be injected in. import_path: Vec::new(), + validate_mode: false, } } @@ -56,9 +56,14 @@ impl Builtins { assert_results: AssertCollector::new(), working_dir: self.working_dir.clone(), import_path: self.import_path.clone(), + validate_mode: self.validate_mode, } } + pub fn enable_validate_mode(&mut self) { + self.validate_mode = true; + } + pub fn handle, O, E>( &mut self, path: Option

, @@ -115,10 +120,10 @@ impl Builtins { } match normalized.canonicalize() { Ok(p) => Ok(p), - Err(_e) => Err(dbg!(Error::new( + Err(_e) => Err(Error::new( format!("Invalid path: {}", normalized.to_string_lossy()), pos.clone(), - ))), + )), } } @@ -134,6 +139,14 @@ impl Builtins { Ok(contents) } + // FIXME(jwall): Should probably move this to the runtime. + //fn detect_import_cycle(&self, path: &str) -> bool { + // self.scope + // .import_stack + // .iter() + // .find(|p| *p == path) + // .is_some() + //} fn import( &mut self, stack: &mut Vec<(Rc, Position)>, @@ -148,37 +161,22 @@ impl Builtins { if let Some((val, path_pos)) = path { if let &Value::P(Str(ref path)) = val.as_ref() { let mut borrowed_env = env.borrow_mut(); - let val_cache = &mut borrowed_env.val_cache; - if val_cache.contains_key(path) { - stack.push((val_cache[path].clone(), path_pos)); - } else { - let op_pointer = env.borrow_mut().op_cache.entry(path).get_pointer_or_else( - || { - // FIXME(jwall): We need to do proper error handling here. - let p = PathBuf::from(&path); - let root = p.parent().unwrap(); - // first we read in the file - let mut f = File::open(&path).unwrap(); - // then we parse it - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - let iter = OffsetStrIter::new(&contents).with_src_file(&p); - let stmts = parse(iter, None).unwrap(); - // then we create an ops from it - let ops = super::translate::AST::translate(stmts, &root); - ops - }, - &path, - ); - let mut vm = VM::with_pointer(op_pointer, env.clone()); - vm.run()?; - let result = Rc::new(vm.symbols_to_tuple(true)); - val_cache.insert(path.clone(), result.clone()); - stack.push((result, pos)); + match borrowed_env.get_cached_path_val(path) { + Some(v) => { + stack.push((v, path_pos)); + } + None => { + let op_pointer = borrowed_env.get_ops_for_path(path)?; + let mut vm = VM::with_pointer(op_pointer, env.clone()); + vm.run()?; + let result = Rc::new(vm.symbols_to_tuple(true)); + borrowed_env.update_path_val(&path, result.clone()); + stack.push((result, pos)); + } } return Ok(()); } - return Err(dbg!(Error::new(format!("Invalid Path {:?}", val), pos,))); + return Err(Error::new(format!("Invalid Path {:?}", val), pos)); } unreachable!(); } @@ -199,10 +197,7 @@ impl Builtins { if let &Value::P(Str(ref path)) = val.as_ref() { path.clone() } else { - return Err(dbg!(Error::new( - format!("Invalid Path {:?}", val), - path_pos, - ))); + return Err(Error::new(format!("Invalid Path {:?}", val), path_pos)); } } else { unreachable!(); @@ -211,10 +206,10 @@ impl Builtins { if let &Value::P(Str(ref typ)) = val.as_ref() { typ.clone() } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Expected conversion type but got {:?}", val), typ_pos, - ))); + )); } } else { unreachable!(); @@ -234,16 +229,13 @@ impl Builtins { P(Empty) } else { match importer.import(contents.as_bytes()) { - Ok(v) => v.try_into()?, - Err(e) => return Err(dbg!(Error::new(format!("{}", e), pos,))), + Ok(v) => v.into(), + Err(e) => return Err(Error::new(format!("{}", e), pos)), } } } None => { - return Err(dbg!(Error::new( - format!("No such conversion type {}", &typ), - pos, - ))) + return Err(Error::new(format!("No such conversion type {}", &typ), pos)) } }), pos, @@ -306,26 +298,26 @@ impl Builtins { }; let val = stack.pop(); if let Some((val, val_pos)) = val { - let val = val.try_into()?; + let val = val.into(); let c_type = stack.pop(); if let Some((c_type_val, c_type_pos)) = c_type { if let &Value::S(ref c_type) = c_type_val.as_ref() { if let Some(c) = env.borrow().converter_registry.get_converter(c_type) { if let Err(e) = c.convert(Rc::new(val), &mut writer) { - return Err(dbg!(Error::new(format!("{}", e), pos.clone(),))); + return Err(Error::new(format!("{}", e), pos.clone())); } return Ok(()); } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!("No such conversion type {:?}", c_type), c_type_pos, - ))); + )); } } - return Err(dbg!(Error::new( + return Err(Error::new( format!("Not a conversion type {:?}", c_type_val), val_pos, - ))); + )); } } unreachable!(); @@ -343,7 +335,7 @@ impl Builtins { { let val = stack.pop(); if let Some((val, val_pos)) = val { - let val = val.try_into()?; + let val = val.into(); if let Some((c_type_val, c_typ_pos)) = stack.pop() { if let &Value::S(ref c_type) = c_type_val.as_ref() { if let Some(c) = env.borrow().converter_registry.get_converter(c_type) { @@ -358,20 +350,20 @@ impl Builtins { )); } Err(_e) => { - return Err(dbg!(Error::new( + return Err(Error::new( format!("No such conversion type {:?}", c_type), c_typ_pos, - ))); + )); } } return Ok(()); } } } - return Err(dbg!(Error::new( + return Err(Error::new( format!("Not a conversion type {:?}", val), val_pos, - ))); + )); } unreachable!() } @@ -402,7 +394,7 @@ impl Builtins { let f = if let &F(ref f) = fptr.as_ref() { f } else { - return Err(dbg!(Error::new(format!("Not a function!!"), fptr_pos))); + return Err(Error::new(format!("Not a function!!"), fptr_pos)); }; match list.as_ref() { @@ -426,24 +418,24 @@ impl Builtins { if let &C(List(ref fval)) = result.as_ref() { // we expect them to be a list of exactly 2 items. if fval.len() != 2 { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Map Functions over tuples must return a list of two items" ), result_pos, - ))); + )); } let name = match fval[0].as_ref() { &P(Str(ref name)) => name.clone(), - _ => return Err(dbg!(Error::new( + _ => return Err(Error::new( format!("Map functions over tuples must return a String as the first list item"), result_pos, - ))), + )), }; new_fields.push((name, fval[1].clone())); } } - stack.push((Rc::new(C(Tuple(dbg!(new_fields)))), pos)); + stack.push((Rc::new(C(Tuple(new_fields))), pos)); } &P(Str(ref s)) => { let mut buf = String::new(); @@ -454,19 +446,19 @@ impl Builtins { if let &P(Str(ref s)) = result.as_ref() { buf.push_str(s); } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Map functions over string should return strings"), - result_pos - ))); + result_pos, + )); } } stack.push((Rc::new(P(Str(buf))), pos)); } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!("You can only map over lists, tuples, or strings"), pos, - ))) + )) } }; Ok(()) @@ -498,7 +490,7 @@ impl Builtins { let f = if let &F(ref f) = fptr.as_ref() { f } else { - return Err(dbg!(Error::new(format!("Not a function!!"), fptr_pos))); + return Err(Error::new(format!("Not a function!!"), fptr_pos)); }; match list.as_ref() { @@ -535,7 +527,7 @@ impl Builtins { _ => new_fields.push((name.clone(), val.clone())), } } - stack.push((Rc::new(C(Tuple(dbg!(new_fields)))), pos)); + stack.push((Rc::new(C(Tuple(new_fields))), pos)); } &P(Str(ref s)) => { let mut buf = String::new(); @@ -555,10 +547,10 @@ impl Builtins { stack.push((Rc::new(P(Str(buf))), pos)); } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!("You can only filter over lists, tuples, or strings"), pos, - ))) + )) } } Ok(()) @@ -570,10 +562,10 @@ impl Builtins { if let &P(Str(ref s)) = val.as_ref() { s.clone() } else { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Expected string bug got {:?}", val), val_pos, - ))); + )); } } else { unreachable!(); @@ -584,10 +576,10 @@ impl Builtins { if let &P(Str(ref s)) = val.as_ref() { s.clone() } else { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Expected string bug got {:?}", val), val_pos, - ))); + )); } } else { unreachable!(); @@ -631,15 +623,15 @@ impl Builtins { let f = if let &F(ref f) = fptr.as_ref() { f } else { - return dbg!(Err(Error::new(format!("Not a function!"), fptr_pos))); + return Err(Error::new(format!("Not a function!"), fptr_pos)); }; match list.as_ref() { &C(List(ref elems)) => { - for e in dbg!(elems).iter() { + for e in elems.iter() { // push function arguments on the stack. - stack.push((dbg!(e.clone()), list_pos.clone())); - stack.push((dbg!(acc.clone()), acc_pos.clone())); + stack.push((e.clone(), list_pos.clone())); + stack.push((acc.clone(), acc_pos.clone())); // call function and push it's result on the stack. let (new_acc, new_acc_pos) = VM::fcall_impl(f, stack, env.clone())?; acc = new_acc; @@ -651,7 +643,7 @@ impl Builtins { // push function arguments on the stack. stack.push((val.clone(), list_pos.clone())); stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone())); - stack.push((dbg!(acc.clone()), acc_pos.clone())); + stack.push((acc.clone(), acc_pos.clone())); // call function and push it's result on the stack. let (new_acc, new_acc_pos) = VM::fcall_impl(f, stack, env.clone())?; acc = new_acc; @@ -661,8 +653,8 @@ impl Builtins { &P(Str(ref _s)) => { for c in _s.chars() { // push function arguments on the stack. - stack.push((dbg!(Rc::new(P(Str(c.to_string())))), list_pos.clone())); - stack.push((dbg!(acc.clone()), acc_pos.clone())); + stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone())); + stack.push((acc.clone(), acc_pos.clone())); // call function and push it's result on the stack. let (new_acc, new_acc_pos) = VM::fcall_impl(f, stack, env.clone())?; acc = new_acc; @@ -670,15 +662,15 @@ impl Builtins { } } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!("You can only reduce over lists, tuples, or strings"), - pos.clone() - ))) + pos.clone(), + )) } }; // push the acc on the stack as our result - stack.push((dbg!(acc), pos)); + stack.push((acc, pos)); Ok(()) } @@ -716,10 +708,10 @@ impl Builtins { } } _ => { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Ranges can only be created with Ints"), pos, - ))); + )); } } stack.push((Rc::new(C(List(elems))), pos)); @@ -736,20 +728,20 @@ impl Builtins { O: std::io::Write, E: std::io::Write, { - let (val, val_pos) = if let Some(val) = dbg!(stack.pop()) { + let (val, val_pos) = if let Some(val) = stack.pop() { val } else { unreachable!(); }; let expr = stack.pop(); let expr_pretty = match expr { - Some((ref expr, _)) => match dbg!(expr.as_ref()) { + Some((ref expr, _)) => match expr.as_ref() { &P(Str(ref expr)) => expr.clone(), _ => unreachable!(), }, _ => unreachable!(), }; - let writable_val: Val = TryFrom::try_from(val.clone())?; + let writable_val: Val = val.clone().into(); if let Err(e) = writeln!( &mut env.borrow_mut().stderr, "TRACE: {} = {} at {}", @@ -757,7 +749,7 @@ impl Builtins { writable_val, &val_pos ) { - return Err(dbg!(Error::new(format!("{}", e), pos))); + return Err(Error::new(format!("{}", e), pos)); }; stack.push((val, val_pos)); Ok(()) diff --git a/src/build/opcode/test.rs b/src/build/opcode/test.rs index 8f7d36d..8bb902a 100644 --- a/src/build/opcode/test.rs +++ b/src/build/opcode/test.rs @@ -41,7 +41,7 @@ macro_rules! assert_cases { }; let mut vm = VM::new(Rc::new(map), env); vm.run().unwrap(); - assert_eq!(dbg!(vm.pop()).unwrap().0, Rc::new(case.1)); + assert_eq!(vm.pop().unwrap().0, Rc::new(case.1)); } }; @@ -792,9 +792,30 @@ fn simple_selects() { ]; } +#[test] +#[should_panic] +fn select_fails() { + assert_parse_cases![ + "select \"quux\", { foo = 1, bar = 2, };" => P(Int(1)), + ]; +} + +#[test] +fn select_compound_expressions() { + assert_parse_cases![ + "select \"foo\", { foo = 1, bar = 2, } == 1;" => P(Bool(true)), + "select \"foo\", { foo = 1, bar = 2, } == 2;" => P(Bool(false)), + "select \"foo\", 3, { foo = 1, bar = 2, } == 2;" => P(Bool(false)), + "select \"quux\", 3, { foo = 1, bar = 2, } == 3;" => P(Bool(true)), + "let want = \"foo\"; select want, { foo = 1, bar = 2, } == 1;" => P(Bool(true)), + "let want = \"foo\"; select want, 3, { foo = 1, bar = 2, } == 1;" => P(Bool(true)), + "{ok = select \"foo\", { foo = 1, bar = 2, } == 2}.ok;" => P(Bool(false)), + ]; +} + #[test] fn simple_trace() { - let stmts = parse(OffsetStrIter::from(dbg!("TRACE 1+1;")), None).unwrap(); + let stmts = parse(OffsetStrIter::from("TRACE 1+1;"), None).unwrap(); let root = std::env::current_dir().unwrap(); let ops = Rc::new(translate::AST::translate(stmts, &root)); assert!(ops.len() > 0); diff --git a/src/build/opcode/translate.rs b/src/build/opcode/translate.rs index 36482f5..d6ab68a 100644 --- a/src/build/opcode/translate.rs +++ b/src/build/opcode/translate.rs @@ -52,36 +52,40 @@ impl AST { return ops; } + pub fn translate_stmt(stmt: Statement, mut ops: &mut PositionMap, root: &Path) { + match stmt { + Statement::Expression(expr) => { + let expr_pos = expr.pos().clone(); + Self::translate_expr(expr, &mut ops, root); + ops.push(Op::Pop, expr_pos); + } + Statement::Assert(pos, expr) => { + Self::translate_expr(expr, &mut ops, root); + ops.push(Op::Runtime(Hook::Assert), pos); + } + Statement::Let(def) => { + let binding = def.name.fragment; + ops.push(Op::Sym(binding), def.name.pos); + Self::translate_expr(def.value, &mut ops, root); + ops.push(Op::Bind, def.pos); + } + Statement::Output(pos, tok, expr) => { + ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos); + Self::translate_expr(expr, &mut ops, root); + ops.push(Op::Runtime(Hook::Out), pos); + } + Statement::Print(pos, tok, expr) => { + ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos); + Self::translate_expr(expr, &mut ops, root); + ops.push(Op::Runtime(Hook::Convert), pos.clone()); + ops.push(Op::Pop, pos); + } + } + } + fn translate_stmts(stmts: Vec, mut ops: &mut PositionMap, root: &Path) { for stmt in stmts { - match stmt { - Statement::Expression(expr) => { - let expr_pos = expr.pos().clone(); - Self::translate_expr(expr, &mut ops, root); - ops.push(Op::Pop, expr_pos); - } - Statement::Assert(pos, expr) => { - Self::translate_expr(expr, &mut ops, root); - ops.push(Op::Runtime(Hook::Assert), pos); - } - Statement::Let(def) => { - let binding = def.name.fragment; - ops.push(Op::Sym(binding), def.name.pos); - Self::translate_expr(def.value, &mut ops, root); - ops.push(Op::Bind, def.pos); - } - Statement::Output(pos, tok, expr) => { - ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos); - Self::translate_expr(expr, &mut ops, root); - ops.push(Op::Runtime(Hook::Out), pos); - } - Statement::Print(pos, tok, expr) => { - ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos); - Self::translate_expr(expr, &mut ops, root); - ops.push(Op::Runtime(Hook::Convert), pos.clone()); - ops.push(Op::Pop, pos); - } - } + Self::translate_stmt(stmt, &mut ops, root); } } @@ -166,7 +170,7 @@ impl AST { let idx = ops.len() - 1; Self::translate_expr(*def.right, &mut ops, root); let jptr = (ops.len() - 1 - idx) as i32; - ops.replace(idx, Op::And(dbg!(jptr))); + ops.replace(idx, Op::And(jptr)); } BinaryExprType::OR => { Self::translate_expr(*def.left, &mut ops, root); @@ -174,7 +178,7 @@ impl AST { let idx = ops.len() - 1; Self::translate_expr(*def.right, &mut ops, root); let jptr = (ops.len() - 1 - idx) as i32; - ops.replace(idx, Op::Or(dbg!(jptr))); + ops.replace(idx, Op::Or(jptr)); } BinaryExprType::Mod => { Self::translate_expr(*def.right, &mut ops, root); @@ -208,7 +212,7 @@ impl AST { Self::translate_expr(*def.left, &mut ops, root); // Symbols on the right side should be converted to strings to satisfy // the Index operation contract. - match dbg!(*def.right) { + match *def.right { Expression::Copy(copy_def) => { // first handle the selector match copy_def.selector { @@ -460,16 +464,16 @@ impl AST { ops.replace(idx, Op::SelectJump(jptr as i32)); } ops.push(Op::Pop, def.pos.clone()); - let end = ops.len() - 1; - for i in jumps { - let idx = end - i; - ops.replace(i, Op::Jump(idx as i32)); - } if let Some(default) = def.default { Self::translate_expr(*default, &mut ops, root); } else { ops.push(Op::Bang, def.pos); } + let end = ops.len() - 1; + for i in jumps { + let idx = end - i; + ops.replace(i, Op::Jump(idx as i32)); + } } Expression::Call(def) => { // first push our arguments. diff --git a/src/build/opcode/vm.rs b/src/build/opcode/vm.rs index 13d0be4..f5ccc77 100644 --- a/src/build/opcode/vm.rs +++ b/src/build/opcode/vm.rs @@ -77,6 +77,10 @@ where } } + pub fn enable_validate_mode(&mut self) { + self.runtime.enable_validate_mode(); + } + pub fn to_scoped(self, symbols: Stack) -> Self { Self { stack: Vec::new(), @@ -153,7 +157,7 @@ where Op::FCall => self.op_fcall(pos)?, Op::NewScope(jp) => self.op_new_scope(jp, self.ops.clone())?, Op::Return => { - dbg!(&self.stack); + &self.stack; return Ok(()); } Op::Pop => { @@ -213,33 +217,33 @@ where self.op_jump(jp)?; } } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Not a boolean condition {:?} in && expression at {}", cond, pos ), cond_pos.clone(), - ))); + )); } Ok(()) } fn op_or(&mut self, jp: i32, pos: Position) -> Result<(), Error> { let (cond, cond_pos) = self.pop()?; - let cc = dbg!(cond.clone()); + let cc = cond.clone(); if let &P(Bool(cond)) = cond.as_ref() { - if dbg!(cond) { + if cond { self.push(cc, cond_pos)?; self.op_jump(jp)?; } } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Not a boolean condition {:?} in || expression at {}!", cond, pos ), cond_pos.clone(), - ))); + )); } Ok(()) } @@ -251,10 +255,10 @@ where self.op_jump(jp)?; } } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Expected boolean but got {:?}!", cond), cond_pos.clone(), - ))); + )); } Ok(()) } @@ -266,19 +270,19 @@ where self.op_jump(jp)?; } } else { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Expected boolean but got {:?}!", cond), pos.clone(), - ))); + )); } Ok(()) } fn op_select_jump(&'a mut self, jp: i32) -> Result<(), Error> { // pop field value off - let (field_name, _) = dbg!(self.pop())?; + let (field_name, _) = self.pop()?; // pop search value off - let (search, srch_pos) = dbg!(self.pop())?; + let (search, srch_pos) = self.pop()?; // compare them. let matched = match (field_name.as_ref(), search.as_ref()) { (&S(ref fname), &P(Str(ref sname))) | (&S(ref fname), &S(ref sname)) => fname == sname, @@ -286,14 +290,14 @@ where }; if !matched { // if they aren't equal then push search value back on and jump - self.push(dbg!(search), srch_pos)?; - self.op_jump(dbg!(jp))?; + self.push(search, srch_pos)?; + self.op_jump(jp)?; } Ok(()) } fn op_module(&'a mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> { - let (mod_val, mod_val_pos) = dbg!(self.pop())?; + let (mod_val, mod_val_pos) = self.pop()?; let (result_ptr, flds) = match mod_val.as_ref() { &C(Tuple(ref flds)) => (None, flds.clone()), &T(ptr) => { @@ -301,17 +305,17 @@ where if let &C(Tuple(ref flds)) = tpl_val.as_ref() { (Some(ptr), flds.clone()) } else { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Expected tuple but got {:?}", tpl_val), tpl_val_pos, - ))); + )); } } _ => { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Expected tuple but got {:?}", mod_val), mod_val_pos, - ))); + )); } }; let mut ops = self.ops.clone(); @@ -362,17 +366,14 @@ where if let &S(ref sym) = e.as_ref() { bindings.push(sym.clone()); } else { - return dbg!(Err(Error::new( + return Err(Error::new( format!("Not an argument name {:?}", e), args_pos, - ))); + )); } } } else { - return dbg!(Err(Error::new( - format!("Fault!!! Bad Argument List"), - args_pos, - ))); + return Err(Error::new(format!("Fault!!! Bad Argument List"), args_pos)); } let mut ops = self.ops.clone(); ops.jump(idx)?; @@ -422,10 +423,10 @@ where } fn op_fcall(&mut self, pos: Position) -> Result<(), Error> { - let (f, _) = dbg!(self.pop())?; + let (f, _) = self.pop()?; if let &F(ref f) = f.as_ref() { let (val, _) = Self::fcall_impl(f, &mut self.stack, self.env.clone())?; - self.push(dbg!(val), pos.clone())?; + self.push(val, pos.clone())?; } Ok(()) } @@ -441,18 +442,19 @@ where self.push(Rc::new(P(Bool(!val))), operand_pos)?; return Ok(()); } - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Expected Boolean but got {:?} in expression at {}", operand, pos ), operand_pos, - ))); + )); } fn op_equal(&mut self, pos: Position) -> Result<(), Error> { let (left, _) = self.pop()?; let (right, _) = self.pop()?; + // FIXME(jwall): We need to enforce our equality rules here. self.push(Rc::new(P(Bool(left == right))), pos)?; Ok(()) } @@ -468,13 +470,13 @@ where self.push(Rc::new(P(Bool(f > ff))), pos.clone())?; } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Expected Numeric values of the same type but got {:?} and {:?}", left, right ), pos.clone(), - ))); + )); } } Ok(()) @@ -491,13 +493,13 @@ where self.push(Rc::new(P(Bool(f < ff))), pos.clone())?; } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Expected Numeric values of the same type but got {:?} and {:?}", left, right ), pos.clone(), - ))); + )); } } Ok(()) @@ -514,13 +516,13 @@ where self.push(Rc::new(P(Bool(f <= ff))), pos)?; } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Expected Numeric values of the same type but got {:?} and {:?}", left, right ), pos, - ))); + )); } } Ok(()) @@ -537,13 +539,13 @@ where self.push(Rc::new(P(Bool(f >= ff))), pos)?; } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!( "Expected Numeric values of the same type but got {:?} and {:?}", left, right ), pos, - ))); + )); } } Ok(()) @@ -597,7 +599,7 @@ where fn op_push_self(&mut self) -> Result<(), Error> { // We'll need a self stack. let (val, pos) = self.pop()?; - self.self_stack.push((dbg!(val.clone()), pos.clone())); + self.self_stack.push((val.clone(), pos.clone())); self.push(val.clone(), pos)?; Ok(()) } @@ -625,12 +627,15 @@ where fn op_field(&mut self) -> Result<(), Error> { // Add a Composite field value to a tuple on the stack // get value from stack + //dbg!(&self.stack); let (val, _) = self.pop()?; // get name from stack. let (name_val, _) = self.pop()?; let name = if let &S(ref s) | &P(Str(ref s)) = name_val.as_ref() { s } else { + //dbg!(name_val); + //dbg!(val); unreachable!(); }; // get composite tuple from stack @@ -649,9 +654,9 @@ where fn op_element(&mut self) -> Result<(), Error> { // get element from stack. - let (val, _) = dbg!(self.pop()?); + let (val, _) = self.pop()?; // get next value. It should be a Composite list. - let (list, pos) = dbg!(self.pop()?); + let (list, pos) = self.pop()?; if let &C(List(ref elems)) = list.as_ref() { // add value to list let mut elems = elems.clone(); @@ -670,8 +675,8 @@ where fn op_index(&mut self, safe: bool, pos: Position) -> Result<(), Error> { // left and then right - let (right, right_pos) = dbg!(self.pop()?); - let (left, _) = dbg!(self.pop()?); + let (right, right_pos) = self.pop()?; + let (left, _) = self.pop()?; match right.as_ref() { &P(Int(i)) => { if let &C(List(ref elems)) = left.as_ref() { @@ -699,17 +704,17 @@ where self.push(Rc::new(P(Empty)), pos)?; return Ok(()); } - return Err(dbg!(Error::new( + return Err(Error::new( format!("Invalid selector index: {:?} target: {:?}", right, left), pos, - ))); + )); } fn op_copy(&mut self, pos: Position) -> Result<(), Error> { // This value should always be a tuple - let (override_val, _) = dbg!(self.pop()?); + let (override_val, _) = self.pop()?; // get targett value. It should be a Module or Tuple. - let (tgt, tgt_pos) = dbg!(self.pop()?); + let (tgt, tgt_pos) = self.pop()?; let overrides = if let &C(Tuple(ref oflds)) = override_val.as_ref() { oflds.clone() } else { @@ -758,17 +763,16 @@ where vm.ops.jump(ptr.clone())?; vm.run()?; let (result_val, result_pos) = vm.pop()?; - self.push(dbg!(result_val), result_pos)?; + self.push(result_val, result_pos)?; } else { - dbg!(&vm.symbols); - self.push(Rc::new(dbg!(vm.symbols_to_tuple(false))), pos)?; + self.push(Rc::new(vm.symbols_to_tuple(false)), pos)?; } } _ => { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Expected a Tuple or a Module but got {:?}", tgt), pos, - ))); + )); } } Ok(()) @@ -804,16 +808,16 @@ where name_pos: &Position, ) -> Result<(), Error> { if self.reserved_words.contains(name.as_str()) { - return Err(dbg!(Error::new( + return Err(Error::new( format!("{} is a reserved word.", name), name_pos.clone(), - ))); + )); } if self.symbols.is_bound(&name) && strict { - return Err(dbg!(Error::new( + return Err(Error::new( format!("Binding {} already exists", name), pos.clone(), - ))); + )); } self.symbols.add(name, val, pos.clone()); Ok(()) @@ -826,20 +830,14 @@ where ) -> Result<(Rc, Position), Error> { if name == "self" { if let Some((val, pos)) = self.self_stack.last() { - return Ok((dbg!(val.clone()), pos.clone())); + return Ok((val.clone(), pos.clone())); } - return Err(dbg!(Error::new( - format!("No such binding {}", name), - pos.clone() - ))); + return Err(Error::new(format!("No such binding {}", name), pos.clone())); } match self.symbols.get(name) { Some((ref v, ref pos)) => Ok((v.clone(), pos.clone())), None => { - return Err(dbg!(Error::new( - format!("No such binding {}", name), - pos.clone() - ))); + return Err(Error::new(format!("No such binding {}", name), pos.clone())); } } } @@ -859,13 +857,10 @@ where (P(Int(i)), P(Int(ii))) => Int(i * ii), (P(Float(f)), P(Float(ff))) => Float(f * ff), _ => { - return Err(dbg!(Error::new( - format!( - "Expected numeric values of the same type but got {:?} and {:?}", - left, right - ), + return Err(Error::new( + format!("Expected {} but got {:?}", left.type_name(), right), pos.clone(), - ))) + )) } }) } @@ -875,13 +870,10 @@ where (P(Int(i)), P(Int(ii))) => Int(i / ii), (P(Float(f)), P(Float(ff))) => Float(f / ff), _ => { - return Err(dbg!(Error::new( - format!( - "Expected numeric values of the same type but got {:?} and {:?}", - left, right - ), + return Err(Error::new( + format!("Expected {} but got {:?}", left.type_name(), right), pos.clone(), - ))) + )) } }) } @@ -891,13 +883,10 @@ where (P(Int(i)), Value::P(Int(ii))) => Int(i - ii), (P(Float(f)), Value::P(Float(ff))) => Float(f - ff), _ => { - return Err(dbg!(Error::new( - format!( - "Expected numeric values of the same type but got {:?} and {:?}", - left, right - ), + return Err(Error::new( + format!("Expected {} but got {:?}", left.type_name(), right), pos.clone(), - ))) + )) } }) } @@ -907,13 +896,10 @@ where (P(Int(i)), Value::P(Int(ii))) => Int(i % ii), (P(Float(f)), Value::P(Float(ff))) => Float(f % ff), _ => { - return Err(dbg!(Error::new( - format!( - "Expected numeric values of the same type but got {:?} and {:?}", - left, right - ), + return Err(Error::new( + format!("Expected {} but got {:?}", left.type_name(), right), pos.clone(), - ))) + )) } }) } @@ -939,10 +925,10 @@ where C(List(new_list)) } _ => { - return Err(dbg!(Error::new( - format!("You can not add {:?} and {:?}", left, right), - pos.clone() - ))) + return Err(Error::new( + format!("Expected {} but got {:?}", left.type_name(), right), + pos.clone(), + )) } }) } diff --git a/src/build/scope.rs b/src/build/scope.rs index 4adfda5..f439097 100644 --- a/src/build/scope.rs +++ b/src/build/scope.rs @@ -169,9 +169,7 @@ impl Scope { | Val::Empty | Val::Float(_) | Val::Int(_) - | Val::Module(_) - | Val::Str(_) - | Val::Func(_) => { + | Val::Str(_) => { // noop } }; diff --git a/src/build/test.rs b/src/build/test.rs index 078711e..977c490 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -21,15 +21,13 @@ use std; use std::cell::RefCell; use std::rc::Rc; -fn test_expr_to_val<'a, C: assets::Cache>( - mut cases: Vec<(Expression, Val)>, - b: FileBuilder<'a, C>, -) { +fn test_expr_to_val<'a, O, E>(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder<'a, O, E>) +where + O: std::io::Write, + E: std::io::Write, +{ for tpl in cases.drain(0..) { - assert_eq!( - b.eval_expr(&tpl.0, &b.scope.spawn_child()).unwrap(), - Rc::new(tpl.1) - ); + assert_eq!(b.eval_expr(tpl.0).unwrap(), Rc::new(tpl.1)); } } @@ -39,7 +37,9 @@ fn test_eval_div_expr_fail() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let registry = ConverterRegistry::make_registry(); - let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -64,9 +64,9 @@ fn test_eval_div_expr_fail() { #[should_panic(expected = "Expected Float")] fn test_eval_mul_expr_fail() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -91,9 +91,9 @@ fn test_eval_mul_expr_fail() { #[should_panic(expected = "Expected Float")] fn test_eval_subtract_expr_fail() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -117,9 +117,9 @@ fn test_eval_subtract_expr_fail() { #[should_panic(expected = "Expected Float")] fn test_eval_add_expr_fail() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -140,31 +140,14 @@ fn test_eval_add_expr_fail() { ); } -#[test] -fn test_eval_simple_lookup_error() { - let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); - b.scope - .build_output - .entry(value_node!("var1".to_string(), Position::new(1, 0, 0))) - .or_insert(Rc::new(Val::Int(1))); - let expr = Expression::Simple(Value::Symbol(value_node!( - "var".to_string(), - Position::new(1, 1, 1) - ))); - assert!(b.eval_expr(&expr, &b.scope.spawn_child()).is_err()); -} - // Include nested for each. #[test] -#[should_panic(expected = "Unable to find binding tpl1")] +#[should_panic(expected = "No such binding tpl1")] fn test_expr_copy_no_such_tuple() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); test_expr_to_val( vec![( Expression::Copy(CopyDef { @@ -180,46 +163,3 @@ fn test_expr_copy_no_such_tuple() { b, ); } - -#[test] -#[should_panic(expected = "Expected String but got Integer in Select expression")] -fn test_select_expr_not_a_string() { - let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); - b.scope - .build_output - .entry(value_node!("foo".to_string(), Position::new(1, 0, 0))) - .or_insert(Rc::new(Val::Int(4))); - test_expr_to_val( - vec![( - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Symbol(value_node!( - "foo".to_string(), - Position::new(1, 1, 1) - )))), - default: Some(Box::new(Expression::Simple(Value::Int(value_node!( - 1, - Position::new(1, 1, 1) - ))))), - tuple: vec![ - ( - make_tok!("bar", Position::new(1, 1, 1)), - Expression::Simple(Value::Int(value_node!(2, Position::new(1, 1, 1)))), - ), - ( - make_tok!("quux", Position::new(1, 1, 1)), - Expression::Simple(Value::Str(value_node!( - "2".to_string(), - Position::new(1, 1, 1) - ))), - ), - ], - pos: Position::new(1, 0, 0), - }), - Val::Int(2), - )], - b, - ); -} diff --git a/src/convert/env.rs b/src/convert/env.rs index 0d83196..a0c0ecd 100644 --- a/src/convert/env.rs +++ b/src/convert/env.rs @@ -73,18 +73,10 @@ impl EnvConverter { &Val::Tuple(ref flds) => { self.convert_tuple(flds, w)?; } - &Val::Func(ref _def) => { - // This is ignored - eprintln!("Skipping func..."); - } &Val::Env(ref _fs) => { // This is ignored eprintln!("Skipping env..."); } - &Val::Module(ref _def) => { - // This is ignored - eprintln!("Skipping module..."); - } } Ok(()) } diff --git a/src/convert/exec.rs b/src/convert/exec.rs index bff2762..e99ec03 100644 --- a/src/convert/exec.rs +++ b/src/convert/exec.rs @@ -194,21 +194,18 @@ impl Converter for ExecConverter { #[cfg(test)] mod exec_test { use super::*; - use crate::build::assets::MemoryCache; use crate::build::FileBuilder; use crate::convert::traits::Converter; - use crate::convert::ConverterRegistry; use std; - use std::cell::RefCell; use std::io::Cursor; #[test] fn convert_just_command_test() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -229,9 +226,9 @@ mod exec_test { #[test] fn convert_command_with_env_test() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -259,9 +256,9 @@ mod exec_test { #[test] fn convert_command_with_arg_test() { let i_paths = Vec::new(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); - let registry = ConverterRegistry::make_registry(); - let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache, ®istry); + let out: Vec = Vec::new(); + let err: Vec = Vec::new(); + let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err); let conv = ExecConverter::new(); b.eval_string( "let script = { diff --git a/src/convert/flags.rs b/src/convert/flags.rs index 3d57974..643e8d4 100644 --- a/src/convert/flags.rs +++ b/src/convert/flags.rs @@ -55,7 +55,7 @@ impl FlagConverter { // first of all we need to make sure that each &Val is only a primitive type. for v in def.iter() { let vref = v.as_ref(); - if vref.is_list() || vref.is_tuple() || vref.is_func() || vref.is_module() { + if vref.is_list() || vref.is_tuple() { eprintln!( "Skipping non primitive val in list for flag {}{}", pfx, name @@ -86,7 +86,7 @@ impl FlagConverter { &Val::Str(ref s) => { write!(w, "'{}' ", s)?; } - &Val::List(_) | &Val::Tuple(_) | &Val::Func(_) | &Val::Env(_) | &Val::Module(_) => { + &Val::List(_) | &Val::Tuple(_) | &Val::Env(_) => { // This is ignored eprintln!("Skipping {}...", v.type_name()); } @@ -101,7 +101,7 @@ impl FlagConverter { continue; } match val.as_ref() { - &Val::Tuple(_) | &Val::Module(_) | &Val::Func(_) | &Val::Env(_) => { + &Val::Tuple(_) | &Val::Env(_) => { eprintln!("Skipping {} in flag output tuple.", val.type_name()); } &Val::List(ref def) => { diff --git a/src/convert/json.rs b/src/convert/json.rs index 3d6efc7..7f65c6d 100644 --- a/src/convert/json.rs +++ b/src/convert/json.rs @@ -72,14 +72,6 @@ impl JsonConverter { serde_json::Value::Number(n) } &Val::Str(ref s) => serde_json::Value::String(s.clone()), - &Val::Func(_) => { - eprintln!("Skipping func encoding as null..."); - serde_json::Value::Null - } - &Val::Module(_) => { - eprintln!("Skipping module encoding as null..."); - serde_json::Value::Null - } &Val::Env(ref fs) => self.convert_env(fs)?, &Val::List(ref l) => self.convert_list(l)?, &Val::Tuple(ref t) => self.convert_tuple(t)?, diff --git a/src/convert/toml.rs b/src/convert/toml.rs index 0c65ee1..ac6e9f1 100644 --- a/src/convert/toml.rs +++ b/src/convert/toml.rs @@ -68,14 +68,6 @@ impl TomlConverter { &Val::Float(f) => toml::Value::Float(f), &Val::Int(i) => toml::Value::Integer(i), &Val::Str(ref s) => toml::Value::String(s.clone()), - &Val::Func(_) => { - let err = SimpleError::new("Functions are not allowed in Toml Conversions!"); - return Err(Box::new(err)); - } - &Val::Module(_) => { - let err = SimpleError::new("Modules are not allowed in Toml Conversions!"); - return Err(Box::new(err)); - } &Val::Env(ref fs) => self.convert_env(fs)?, &Val::List(ref l) => self.convert_list(l)?, &Val::Tuple(ref t) => self.convert_tuple(t)?, diff --git a/src/convert/yaml.rs b/src/convert/yaml.rs index 1dbe33a..eb3d526 100644 --- a/src/convert/yaml.rs +++ b/src/convert/yaml.rs @@ -57,14 +57,6 @@ impl YamlConverter { _ => panic!("Int is too large or not a Number {}", i), }, &Val::Str(ref s) => serde_yaml::Value::String(s.clone()), - &Val::Func(_) => { - eprintln!("Skipping func encoding as null..."); - serde_yaml::Value::Null - } - &Val::Module(_) => { - eprintln!("Skipping module encoding as null..."); - serde_yaml::Value::Null - } &Val::Env(ref fs) => self.convert_env(fs)?, &Val::List(ref l) => self.convert_list(l)?, &Val::Tuple(ref t) => self.convert_tuple(t)?,