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)?,