From ac4dc2addd956ba7d83a60da00ce30848af6f492 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 1 Sep 2019 19:21:02 -0500 Subject: [PATCH] DEV: All unit tests pass. --- src/ast/mod.rs | 2 +- src/build/compile_test.rs | 76 ++- src/build/opcode/debug.rs | 5 +- src/build/opcode/display.rs | 52 ++ src/build/opcode/environment.rs | 18 +- src/build/opcode/error.rs | 33 +- src/build/opcode/mod.rs | 41 +- src/build/opcode/runtime.rs | 205 ++++--- src/build/opcode/test.rs | 913 -------------------------------- src/build/opcode/translate.rs | 23 +- src/build/opcode/vm.rs | 241 +++++++-- src/error.rs | 6 +- 12 files changed, 505 insertions(+), 1110 deletions(-) create mode 100644 src/build/opcode/display.rs delete mode 100644 src/build/opcode/test.rs diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 54a7373..5379cc8 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -97,7 +97,7 @@ impl<'a> From<&'a Position> for Position { impl std::fmt::Display for Position { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { if let Some(ref file) = self.file { - write!(f, "file: {}", file.to_string_lossy().to_string())?; + write!(f, "file: {} ", file.to_string_lossy().to_string())?; } write!(f, "line: {} column: {}", self.line, self.column) } diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index 668e74f..e667672 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -162,10 +162,7 @@ fn test_declarative_failures_can_with_format_expr() { fn test_assert_just_keyword_compile_failures() { assert_build_failure( "assert ", - vec![ - Regex::new(r"Expected Tuple \{ok=, desc=\} at line: 1, column: 8") - .unwrap(), - ], + vec![Regex::new(r"Expected Tuple \{ok=, desc=\} at line: 1 column: 8").unwrap()], ); } @@ -174,9 +171,8 @@ fn test_assert_partial_tuple_compile_failures() { assert_build_failure( "assert {", vec![ - Regex::new(r"Expected Tuple \{ok=, desc=\} at line: 1, column: 8") - .unwrap(), - Regex::new(r"Expected \(\}\) but got \(\) at line: 1, column: 9").unwrap(), + Regex::new(r"Expected Tuple \{ok=, desc=\} at line: 1 column: 8").unwrap(), + Regex::new(r"Expected \(\}\) but got \(\) at line: 1 column: 9").unwrap(), ], ); } @@ -187,7 +183,7 @@ fn test_assert_partial_tuple_missing_ok_compile_failures() { "assert {};", vec![ Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(), - Regex::new(r"line: 1, column: 8").unwrap(), + Regex::new(r"line: 1 column: 8").unwrap(), ], ); } @@ -198,7 +194,7 @@ fn test_assert_partial_tuple_bad_ok_compile_failures() { "assert { ok = 1, };", vec![ Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(), - Regex::new(r"line: 1, column: 8").unwrap(), + Regex::new(r"line: 1 column: 8").unwrap(), ], ); } @@ -209,7 +205,7 @@ fn test_assert_partial_tuple_missing_desc_compile_failures() { "assert { ok=true, };", vec![ Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(), - Regex::new(r"line: 1, column: 8").unwrap(), + Regex::new(r"line: 1 column: 8").unwrap(), ], ); } @@ -220,7 +216,7 @@ fn test_assert_partial_tuple_bad_desc_compile_failures() { "assert { ok=true, desc = 1 };", vec![ Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(), - Regex::new(r"line: 1, column: 8").unwrap(), + Regex::new(r"line: 1 column: 8").unwrap(), ], ); } @@ -229,7 +225,7 @@ fn test_assert_partial_tuple_bad_desc_compile_failures() { fn test_import_missing_path_compile_failure() { assert_build_failure( "import ;", - vec![Regex::new(r"Expected import path at line: 1, column: 8").unwrap()], + vec![Regex::new(r"Expected import path at line: 1 column: 8").unwrap()], ) } @@ -237,7 +233,7 @@ fn test_import_missing_path_compile_failure() { fn test_import_path_not_a_string_compile_failure() { assert_build_failure( "import 1;", - vec![Regex::new(r"Expected import path at line: 1, column: 8").unwrap()], + vec![Regex::new(r"Expected import path at line: 1 column: 8").unwrap()], ) } @@ -245,10 +241,7 @@ fn test_import_path_not_a_string_compile_failure() { fn test_binary_operator_missing_operand_compile_failure() { assert_build_failure( "1 +", - vec![ - Regex::new(r"Missing operand for binary expression at line: 1, column: 4") - .unwrap(), - ], + vec![Regex::new(r"Missing operand for binary expression at line: 1 column: 4").unwrap()], ) } @@ -357,10 +350,10 @@ fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() { assert_build_failure( "1 == \"foo\";", vec![ - Regex::new( - r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 5 for expression", - ) - .unwrap(), + Regex::new(r"Expected values of the same type").unwrap(), + Regex::new(r"but got Int\(1\) at line: 1 column: 1").unwrap(), + Regex::new(r"and String\(foo\) at line: 1 column: 6").unwrap(), + Regex::new(r"for expression at line: 1 column: 1").unwrap(), Regex::new(r"line: 1 column: 1").unwrap(), ], ) @@ -372,7 +365,7 @@ fn test_incomplete_tuple_compile_failure() { "{;", vec![ Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(), - Regex::new(r"at line: 1 column: 2").unwrap(), + Regex::new(r"at line: 1 column: 2").unwrap(), ], ) } @@ -383,7 +376,7 @@ fn test_incomplete_tuple_key_value_missing_eq_compile_failure() { "{foo", vec![ Regex::new(r"Expected \(=\) but got \(\)").unwrap(), - Regex::new(r"at line: 1, column: 5").unwrap(), + Regex::new(r"at line: 1 column: 5").unwrap(), ], ) } @@ -394,7 +387,7 @@ fn test_incomplete_tuple_key_value_missing_value_compile_failure() { "{foo=", vec![ Regex::new(r"Not a Bareword").unwrap(), - Regex::new(r"at line: 1, column: 6").unwrap(), + Regex::new(r"at line: 1 column: 6").unwrap(), ], ) } @@ -405,7 +398,7 @@ fn test_format_expr_missing_expr_compile_error() { "\"foo\" %", vec![ Regex::new(r"Expected format arguments").unwrap(), - Regex::new(r"at line: 1, column: 8").unwrap(), + Regex::new(r"at line: 1 column: 8").unwrap(), ], ) } @@ -416,7 +409,7 @@ fn test_format_expr_unclosed_parens_compile_failure() { "\"foo\" % (1", vec![ Regex::new(r"Expected \(\)\) but got \(\)").unwrap(), - Regex::new(r"at line: 1, column: 9").unwrap(), + Regex::new(r"at line: 1 column: 9").unwrap(), ], ) } @@ -427,7 +420,7 @@ fn test_list_unclosed_bracket_compile_failure() { "[1", vec![ Regex::new(r"Expected \(\]\) but got \(\)").unwrap(), - Regex::new(r"at line: 1, column: 3").unwrap(), + Regex::new(r"at line: 1 column: 3").unwrap(), ], ) } @@ -438,7 +431,7 @@ fn test_out_missing_type_compile_failure() { "out", vec![ Regex::new(r"Expected converter name").unwrap(), - Regex::new(r"at line: 1, column: 4").unwrap(), + Regex::new(r"at line: 1 column: 4").unwrap(), ], ) } @@ -449,7 +442,7 @@ fn test_out_missing_out_expr_compile_failure() { "out json", vec![ Regex::new(r"Expected Expression to export").unwrap(), - Regex::new(r"at line: 1, column: 9").unwrap(), + Regex::new(r"at line: 1 column: 9").unwrap(), ], ) } @@ -460,7 +453,7 @@ fn test_out_multiple_times_compile_failure() { "out json {};\nout json {};", vec![ Regex::new(r"You can only have one output per file").unwrap(), - Regex::new(r"at line: 2, column: 1").unwrap(), + Regex::new(r"at line: 2 column: 1").unwrap(), ], ) } @@ -471,7 +464,7 @@ fn test_let_missing_name_compile_failure() { "let ", vec![ Regex::new(r"Expected name for binding").unwrap(), - Regex::new(r"at line: 1, column: 5").unwrap(), + Regex::new(r"at line: 1 column: 5").unwrap(), ], ) } @@ -482,7 +475,7 @@ fn test_let_missing_equal_compile_failure() { "let foo ", vec![ Regex::new(r"Expected \(=\) but got \(\)").unwrap(), - Regex::new(r"at line: 1, column: 9").unwrap(), + Regex::new(r"at line: 1 column: 9").unwrap(), ], ) } @@ -493,7 +486,7 @@ fn test_let_missing_expression_compile_failure() { "let foo =", vec![ Regex::new(r"Expected Expression to bind").unwrap(), - Regex::new(r"at line: 1, column: 10").unwrap(), + Regex::new(r"at line: 1 column: 10").unwrap(), ], ) } @@ -504,7 +497,7 @@ fn test_let_missing_semicolon_compile_failure() { "let foo = 1", vec![ Regex::new(r"Expected \(;\) but got \(\)").unwrap(), - Regex::new(r"at line: 1, column: 12").unwrap(), + Regex::new(r"at line: 1 column: 12").unwrap(), ], ) } @@ -514,7 +507,7 @@ fn test_copy_expression_not_a_tuple_compile_failure() { assert_build_failure( "let foo = 1;\nfoo{};", vec![ - Regex::new(r"Expected Tuple or Module but got \(1\)").unwrap(), + Regex::new(r"Expected a Tuple or Module but got Int\(1\)").unwrap(), Regex::new(r"line: 2 column: 1").unwrap(), ], ) @@ -525,8 +518,8 @@ fn test_copy_expression_wrong_field_type_compile_failure() { assert_build_failure( "let foo = {bar=1};\nfoo{bar=[]};", vec![ - Regex::new(r"Expected type Integer for field bar but got \(List\)").unwrap(), - Regex::new(r"at line: 2, column: 5").unwrap(), + Regex::new(r"Expected type Int for field bar but got \(List\)").unwrap(), + Regex::new(r"at line: 2 column: 9").unwrap(), ], ) } @@ -537,7 +530,7 @@ fn test_func_call_wrong_argument_length_compile_failure() { "let foo = func() => 1;\nfoo(1);", vec![ Regex::new(r"Func called with too many args").unwrap(), - Regex::new(r"at line: 2, column: 1").unwrap(), + Regex::new(r"at line: 2 column: 1").unwrap(), ], ) } @@ -547,10 +540,9 @@ fn test_func_call_wrong_argument_type_compile_failure() { assert_build_failure( "let foo = func(i) => 1 + i;\nfoo(\"bar\");", vec![ - Regex::new(r"Func evaluation failed").unwrap(), - Regex::new(r"at line: 1, column: 26").unwrap(), - Regex::new(r"Expected Integer but got \(.bar.\)").unwrap(), - Regex::new(r"at line: 2, column: 1").unwrap(), + Regex::new(r"Expected Int but got String\(bar\)").unwrap(), + Regex::new(r"at line: 1 column: 26").unwrap(), + Regex::new(r"line: 2 column: 1").unwrap(), ], ) } diff --git a/src/build/opcode/debug.rs b/src/build/opcode/debug.rs index 5ae9481..f35c8fd 100644 --- a/src/build/opcode/debug.rs +++ b/src/build/opcode/debug.rs @@ -11,7 +11,6 @@ // 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; @@ -30,14 +29,14 @@ impl fmt::Debug for Value { P(Float(v)) => write!(w, "Float({})", v), P(Str(v)) => write!(w, "String({})", v), P(Empty) => write!(w, "NULL"), - C(List(ref els)) => { + C(List(ref els, _)) => { write!(w, "List[")?; for e in els { write!(w, "{:?},", e)?; } write!(w, "]") } - C(Tuple(ref flds)) => { + C(Tuple(ref flds, _)) => { write!(w, "Tuple(")?; for (k, v) in flds { write!(w, "\"{}\"={:?},", k, v)?; diff --git a/src/build/opcode/display.rs b/src/build/opcode/display.rs new file mode 100644 index 0000000..8406feb --- /dev/null +++ b/src/build/opcode/display.rs @@ -0,0 +1,52 @@ +// 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::Display for Value { + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { + match self { + P(Bool(v)) => write!(w, "{}", v), + P(Int(v)) => write!(w, "{}", v), + P(Float(v)) => write!(w, "{}", v), + P(Str(v)) => write!(w, "\"{}\"", v.replace("\"", "\\\"")), + P(Empty) => write!(w, "NULL"), + C(List(ref els, _)) => { + write!(w, "[")?; + for e in els { + write!(w, "{},", e)?; + } + write!(w, "]") + } + C(Tuple(ref flds, _)) => { + write!(w, "{{")?; + for (k, v) in flds { + write!(w, "\"{}\"={},\n", k, v)?; + } + write!(w, "}}") + } + F(_) => write!(w, ""), + M(_) => write!(w, ""), + T(_) => write!(w, ""), + S(v) => write!(w, "{}", v), + } + } +} diff --git a/src/build/opcode/environment.rs b/src/build/opcode/environment.rs index 2a34b8b..08df900 100644 --- a/src/build/opcode/environment.rs +++ b/src/build/opcode/environment.rs @@ -11,10 +11,10 @@ // 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::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::fs::File; use std::io::{Read, Write}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; use super::cache; @@ -40,6 +40,7 @@ where pub stdout: Stdout, pub stderr: Stderr, pub env_vars: BTreeMap, // Environment Variables + pub out_lock: BTreeSet, } impl Environment { @@ -57,6 +58,7 @@ impl Environment { importer_registry: ImporterRegistry::make_registry(), stdout: out, stderr: err, + out_lock: BTreeSet::new(), } } @@ -93,4 +95,16 @@ impl Environment { pub fn record_assert_result(&mut self, desc: &str, ok: bool) { self.assert_results.record_assert_result(desc, ok); } + + pub fn get_out_lock_for_path>(&self, path: P) -> bool { + self.out_lock.contains(path.as_ref()) + } + + pub fn set_out_lock_for_path>(&mut self, path: P) { + self.out_lock.insert(path.into()); + } + + pub fn reset_out_lock_for_path>(&mut self, path: P) { + self.out_lock.remove(path.as_ref()); + } } diff --git a/src/build/opcode/error.rs b/src/build/opcode/error.rs index 3172bb6..2d573bf 100644 --- a/src/build/opcode/error.rs +++ b/src/build/opcode/error.rs @@ -22,6 +22,7 @@ use crate::ast::Position; pub struct Error { message: String, pos: Option, + call_stack: Vec, } impl Error { @@ -29,6 +30,7 @@ impl Error { Self { message: msg, pos: Some(pos), + call_stack: Vec::new(), } } @@ -36,14 +38,29 @@ impl Error { self.pos = Some(pos); self } + + pub fn push_call_stack(&mut self, pos: Position) { + self.call_stack.push(pos); + } } macro_rules! decorate_error { ($pos:expr => $result:expr) => { match $result { Ok(v) => Ok(v), - Err(e) => { - Err(e.with_pos($pos.clone())) + Err(e) => Err(e.with_pos($pos.clone())), + } + }; +} + +macro_rules! decorate_call { + ($pos:expr => $result:expr) => { + match $result { + Ok(v) => Ok(v), + Err(mut e) => { + dbg!(&$pos); + e.push_call_stack($pos.clone()); + Err(e) } } }; @@ -54,6 +71,7 @@ impl From for Error { Error { message: format!("{}", e), pos: None, + call_stack: Vec::new(), } } } @@ -67,6 +85,7 @@ impl From for Error { Error { message: msg, pos: None, + call_stack: Vec::new(), } } } @@ -74,10 +93,16 @@ impl From for Error { 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) + write!(f, "{} at {}", self.message, pos)?; } else { - write!(f, "{}", self.message) + write!(f, "{}", self.message)?; } + if !self.call_stack.is_empty() { + for p in self.call_stack.iter() { + write!(f, "\nVIA: {}", p)?; + } + } + Ok(()) } } diff --git a/src/build/opcode/mod.rs b/src/build/opcode/mod.rs index 6b316d8..58a692b 100644 --- a/src/build/opcode/mod.rs +++ b/src/build/opcode/mod.rs @@ -17,6 +17,7 @@ use std::rc::Rc; mod cache; mod debug; +mod display; pub mod environment; #[macro_use] mod error; @@ -55,8 +56,8 @@ impl Value { P(Str(_)) => "String", P(Bool(_)) => "Bool", P(Empty) => "NULL", - C(List(_)) => "List", - C(Tuple(_)) => "Tuple", + C(List(_, _)) => "List", + C(Tuple(_, _)) => "Tuple", F(_) => "Func", M(_) => "Func", T(_) => "Expression", @@ -79,8 +80,8 @@ impl From<&Primitive> for String { #[derive(Debug, PartialEq, Clone)] pub enum Composite { - List(Vec>), - Tuple(Vec<(String, Rc)>), + List(Vec>, Vec), + Tuple(Vec<(String, Rc)>, Vec<(Position, Position)>), } use Composite::{List, Tuple}; @@ -89,7 +90,7 @@ impl From<&Composite> for String { fn from(c: &Composite) -> Self { let mut buf = String::new(); match c { - &List(ref elems) => { + &List(ref elems, _) => { buf.push_str("["); for e in elems.iter() { let val: String = e.as_ref().into(); @@ -98,7 +99,7 @@ impl From<&Composite> for String { } buf.push_str("]"); } - &Tuple(ref flds) => { + &Tuple(ref flds, _) => { buf.push_str("{"); for &(ref k, ref v) in flds.iter() { buf.push_str(&k); @@ -126,6 +127,7 @@ pub struct Module { ptr: OpPointer, result_ptr: Option, flds: Vec<(String, Rc)>, + flds_pos_list: Vec<(Position, Position)>, pkg_ptr: Option, } @@ -245,8 +247,8 @@ impl PartialEq for Value { fn eq(&self, other: &Value) -> bool { match (self, other) { (P(left), P(right)) => left == right, - (C(List(left)), C(List(right))) => left == right, - (C(Tuple(left)), C(Tuple(right))) => { + (C(List(left, _)), C(List(right, _))) => left == right, + (C(Tuple(left, _)), C(Tuple(right, _))) => { if left.len() != right.len() { return false; } @@ -293,7 +295,7 @@ impl From<&Value> for Val { P(Float(f)) => Val::Float(*f), P(Str(s)) => Val::Str(s.clone()), P(Bool(b)) => Val::Boolean(*b), - C(Tuple(fs)) => { + C(Tuple(fs, _)) => { let mut flds = Vec::new(); for &(ref k, ref v) in fs.iter() { let v = v.clone(); @@ -301,7 +303,7 @@ impl From<&Value> for Val { } Val::Tuple(flds) } - C(List(elems)) => { + C(List(elems, _)) => { let mut els = Vec::new(); for e in elems.iter() { let e = e.clone(); @@ -336,30 +338,35 @@ impl From<&Val> for Value { Val::Empty => P(Empty), Val::List(els) => { let mut lst = Vec::new(); + let mut positions = Vec::new(); for e in els.iter() { let e = e.clone(); lst.push(Rc::new(e.into())); + positions.push(Position::new(0, 0, 0)); } - C(List(lst)) + // TODO(jwall): This should have a set of + // Positions of the same length. + C(List(lst, positions)) } Val::Tuple(flds) => { let mut field_list = Vec::new(); + let mut positions = Vec::new(); for &(ref key, ref val) in flds.iter() { let val = val.clone(); field_list.push((key.clone(), Rc::new(val.into()))); + positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0))); } - C(Tuple(field_list)) + C(Tuple(field_list, positions)) } Val::Env(flds) => { let mut field_list = Vec::new(); + let mut positions = Vec::new(); for &(ref key, ref val) in flds.iter() { field_list.push((key.clone(), Rc::new(P(Str(val.clone()))))); + positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0))); } - C(Tuple(field_list)) + C(Tuple(field_list, positions)) } } } -} - -#[cfg(test)] -mod test; +} \ No newline at end of file diff --git a/src/build/opcode/runtime.rs b/src/build/opcode/runtime.rs index b0ac30d..bb8e3ea 100644 --- a/src/build/opcode/runtime.rs +++ b/src/build/opcode/runtime.rs @@ -66,6 +66,7 @@ impl Builtins { h: Hook, stack: &mut Vec<(Rc, Position)>, env: Rc>>, + import_stack: &mut Vec, pos: Position, ) -> Result<(), Error> where @@ -73,14 +74,14 @@ impl Builtins { E: std::io::Write, { match h { - Hook::Import => self.import(stack, env, pos), + Hook::Import => self.import(stack, env, import_stack, pos), Hook::Include => self.include(stack, env, pos), Hook::Assert => self.assert(stack, env), Hook::Convert => self.convert(stack, env, pos), Hook::Out => self.out(path, stack, env, pos), - Hook::Map => self.map(stack, env, pos), - Hook::Filter => self.filter(stack, env, pos), - Hook::Reduce => self.reduce(stack, env, pos), + Hook::Map => self.map(stack, env, import_stack, pos), + Hook::Filter => self.filter(stack, env, import_stack, pos), + Hook::Reduce => self.reduce(stack, env, import_stack, pos), Hook::Regex => self.regex(stack, pos), Hook::Range => self.range(stack, pos), Hook::Trace(pos) => self.trace(stack, pos, env), @@ -135,18 +136,11 @@ 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)>, env: Rc>>, + import_stack: &mut Vec, pos: Position, ) -> Result<(), Error> where @@ -156,6 +150,15 @@ impl Builtins { let path = stack.pop(); if let Some((val, path_pos)) = path { if let &Value::P(Str(ref path)) = val.as_ref() { + if import_stack + .iter() + .find(|p| *p == path) + .is_some() { + return Err(Error::new( + format!("You can only have one output per file"), + pos)); + } + import_stack.push(path.clone()); let mut borrowed_env = env.borrow_mut(); match borrowed_env.get_cached_path_val(path) { Some(v) => { @@ -250,32 +253,46 @@ impl Builtins { O: std::io::Write, E: std::io::Write, { - let tuple = stack.pop(); - if let Some((val, tpl_pos)) = tuple.clone() { - if let &Value::C(Tuple(ref tuple)) = val.as_ref() { + if let Some((tuple, tpl_pos)) = stack.pop() { + if let &Value::C(Tuple(ref tuple_flds, _)) = tuple.as_ref() { // look for the description field let mut desc = None; // look for the ok field. let mut ok = None; - for &(ref name, ref val) in tuple.iter() { + for &(ref name, ref val) in tuple_flds.iter() { if name == "desc" { - desc = Some(val.clone()); + desc = Some(val.as_ref()); } if name == "ok" { - ok = Some(val.clone()); - } - } - if let (Some(ok), Some(desc)) = (ok, desc) { - if let (&Value::P(Bool(ref b)), &Value::P(Str(ref desc))) = - (ok.as_ref(), desc.as_ref()) - { - env.borrow_mut().record_assert_result(desc, *b); - return Ok(()); + ok = Some(val.as_ref()); } } + let ok = if let Some(&P(Bool(ref b))) = ok { + *b + } else { + let msg = format!( + "TYPE FAIL - Expected Boolean field ok in tuple {} at {}\n", + tuple, tpl_pos + ); + env.borrow_mut().record_assert_result(&msg, false); + return Ok(()); + }; + + let desc = if let Some(&P(Str(ref desc))) = desc { + desc + } else { + let msg = format!( + "TYPE FAIL - Expected String field desc in tuple {} at {}\n", + tuple, tpl_pos + ); + env.borrow_mut().record_assert_result(&msg, false); + return Ok(()); + }; + env.borrow_mut().record_assert_result(desc, ok); + return Ok(()); } let msg = format!( - "TYPE FAIL - Expected tuple with ok and desc fields got {:?} at {}\n", + "TYPE FAIL - Expected tuple with ok and desc fields got {} at {}\n", tuple, tpl_pos ); env.borrow_mut().record_assert_result(&msg, false); @@ -297,8 +314,20 @@ impl Builtins { E: std::io::Write, { let mut writer: Box = if let Some(path) = path { + if env.borrow().get_out_lock_for_path(path.as_ref()) { + return Err(Error::new( + format!("You can only have one output per file"), + pos)); + } + env.borrow_mut().set_out_lock_for_path(path.as_ref()); Box::new(File::create(path)?) } else { + if env.borrow().get_out_lock_for_path("/dev/stdout") { + return Err(Error::new( + format!("You can only have one output per file"), + pos)); + } + env.borrow_mut().set_out_lock_for_path("/dev/stdout"); Box::new(std::io::stdout()) }; let val = stack.pop(); @@ -306,7 +335,7 @@ impl Builtins { 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 &Value::P(Primitive::Str(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(Error::new(format!("{}", e), pos.clone())); @@ -377,6 +406,7 @@ impl Builtins { &self, stack: &mut Vec<(Rc, Position)>, env: Rc>>, + import_stack: &Vec, pos: Position, ) -> Result<(), Error> where @@ -403,24 +433,35 @@ impl Builtins { }; match list.as_ref() { - &C(List(ref elems)) => { + &C(List(ref elems, ref elems_pos_list)) => { let mut result_elems = Vec::new(); + let mut pos_elems = Vec::new(); + let mut counter = 0; for e in elems.iter() { // push function argument on the stack. - stack.push((e.clone(), list_pos.clone())); + let e_pos = elems_pos_list[counter].clone(); + stack.push((e.clone(), e_pos.clone())); // call function and push it's result on the stack. - let (result, _) = VM::fcall_impl(f, stack, env.clone())?; + let (result, result_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; + pos_elems.push(result_pos); result_elems.push(result); + counter += 1; } - stack.push((Rc::new(C(List(result_elems))), list_pos)); + stack.push((Rc::new(C(List(result_elems, pos_elems))), list_pos)); } - &C(Tuple(ref _flds)) => { + &C(Tuple(ref flds, ref flds_pos_list)) => { let mut new_fields = Vec::new(); - for (ref name, ref val) in _flds { - stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone())); - stack.push((val.clone(), list_pos.clone())); - let (result, result_pos) = VM::fcall_impl(f, stack, env.clone())?; - if let &C(List(ref fval)) = result.as_ref() { + let mut new_flds_pos_list = Vec::new(); + let mut counter = 0; + for (ref name, ref val) in flds { + let name_pos = flds_pos_list[counter].0.clone(); + let val_pos = flds_pos_list[counter].1.clone(); + stack.push((Rc::new(P(Str(name.clone()))), name_pos)); + stack.push((val.clone(), val_pos)); + let (result, result_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; + 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(Error::new( @@ -437,17 +478,21 @@ impl Builtins { result_pos, )), }; + let name_pos = flds_pos_list[counter].0.clone(); + new_flds_pos_list.push((name_pos, result_pos)); new_fields.push((name, fval[1].clone())); } + counter += 1; } - stack.push((Rc::new(C(Tuple(new_fields))), pos)); + stack.push((Rc::new(C(Tuple(new_fields, new_flds_pos_list))), pos)); } &P(Str(ref s)) => { let mut buf = String::new(); for c in s.chars() { stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone())); // call function and push it's result on the stack. - let (result, result_pos) = VM::fcall_impl(f, stack, env.clone())?; + let (result, result_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; if let &P(Str(ref s)) = result.as_ref() { buf.push_str(s); } else { @@ -473,6 +518,7 @@ impl Builtins { &self, stack: &mut Vec<(Rc, Position)>, env: Rc>>, + import_stack: &Vec, pos: Position, ) -> Result<(), Error> where @@ -499,47 +545,65 @@ impl Builtins { }; match list.as_ref() { - &C(List(ref elems)) => { + &C(List(ref elems, ref elems_pos_list)) => { let mut result_elems = Vec::new(); + let mut pos_elems = Vec::new(); + let mut counter = 0; for e in elems.iter() { // push function argument on the stack. - stack.push((e.clone(), list_pos.clone())); + let e_pos = elems_pos_list[counter].clone(); + stack.push((e.clone(), e_pos.clone())); // call function and push it's result on the stack. - let (condition, _) = VM::fcall_impl(f, stack, env.clone())?; + let (condition, _) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; // Check for empty or boolean results and only push e back in // if they are non empty and true + counter += 1; match condition.as_ref() { &P(Empty) | &P(Bool(false)) => { continue; } - _ => result_elems.push(e.clone()), + _ => { + result_elems.push(e.clone()); + pos_elems.push(e_pos); + }, } } - stack.push((Rc::new(C(List(result_elems))), pos)); + stack.push((Rc::new(C(List(result_elems, pos_elems))), pos)); } - &C(Tuple(ref _flds)) => { + &C(Tuple(ref flds, ref pos_list)) => { let mut new_fields = Vec::new(); - for (ref name, ref val) in _flds { - stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone())); - stack.push((val.clone(), list_pos.clone())); - let (condition, _) = VM::fcall_impl(f, stack, env.clone())?; + let mut new_flds_pos_list = Vec::new(); + let mut counter = 0; + for (ref name, ref val) in flds { + let name_pos = pos_list[counter].0.clone(); + let val_pos = pos_list[counter].1.clone(); + stack.push((Rc::new(P(Str(name.clone()))), name_pos.clone())); + stack.push((val.clone(), val_pos.clone())); + let (condition, _) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; // Check for empty or boolean results and only push e back in // if they are non empty and true + counter += 1; match condition.as_ref() { &P(Empty) | &P(Bool(false)) => { continue; } - _ => new_fields.push((name.clone(), val.clone())), + _ => { + new_fields.push((name.clone(), val.clone())); + new_flds_pos_list.push((name_pos, val_pos)); + }, } } - stack.push((Rc::new(C(Tuple(new_fields))), pos)); + stack.push((Rc::new(C(Tuple(new_fields, new_flds_pos_list))), pos)); } &P(Str(ref s)) => { let mut buf = String::new(); for c in s.chars() { stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone())); // call function and push it's result on the stack. - let (condition, _) = VM::fcall_impl(f, stack, env.clone())?; + let (condition, _) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; // Check for empty or boolean results and only push c back in // if they are non empty and true match condition.as_ref() { @@ -600,6 +664,7 @@ impl Builtins { &self, stack: &mut Vec<(Rc, Position)>, env: Rc>>, + import_stack: &Vec, pos: Position, ) -> Result<(), Error> where @@ -632,36 +697,46 @@ impl Builtins { }; match list.as_ref() { - &C(List(ref elems)) => { + &C(List(ref elems, ref elems_pos_list)) => { + let mut counter = 0; for e in elems.iter() { + let e_pos = elems_pos_list[counter].clone(); // push function arguments on the stack. stack.push((acc.clone(), acc_pos.clone())); - stack.push((e.clone(), list_pos.clone())); + stack.push((e.clone(), e_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())?; + let (new_acc, new_acc_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; acc = new_acc; acc_pos = new_acc_pos; + counter += 1; } } - &C(Tuple(ref _flds)) => { + &C(Tuple(ref _flds, ref flds_pos_list)) => { + let mut counter = 0; for (ref name, ref val) in _flds.iter() { + let name_pos = flds_pos_list[counter].0.clone(); + let val_pos = flds_pos_list[counter].1.clone(); // push function arguments on the stack. stack.push((acc.clone(), acc_pos.clone())); - stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone())); - stack.push((val.clone(), list_pos.clone())); + stack.push((Rc::new(P(Str(name.clone()))), name_pos)); + stack.push((val.clone(), val_pos)); // call function and push it's result on the stack. - let (new_acc, new_acc_pos) = VM::fcall_impl(f, stack, env.clone())?; + let (new_acc, new_acc_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; acc = new_acc; acc_pos = new_acc_pos; + counter += 1; } } - &P(Str(ref _s)) => { - for c in _s.chars() { + &P(Str(ref s)) => { + for c in s.chars() { // push function arguments on the stack. stack.push((acc.clone(), acc_pos.clone())); stack.push((Rc::new(P(Str(c.to_string()))), list_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())?; + let (new_acc, new_acc_pos) = decorate_call!(pos => + VM::fcall_impl(f, stack, env.clone(), import_stack))?; acc = new_acc; acc_pos = new_acc_pos; } @@ -701,6 +776,7 @@ impl Builtins { }; let mut elems = Vec::new(); + let mut pos_list = Vec::new(); match (start.as_ref(), step.as_ref(), end.as_ref()) { (&P(Int(start)), &P(Int(step)), &P(Int(end))) => { let mut num = start; @@ -709,6 +785,7 @@ impl Builtins { break; } elems.push(Rc::new(P(Int(num)))); + pos_list.push(pos.clone()); num += step; } } @@ -719,7 +796,7 @@ impl Builtins { )); } } - stack.push((Rc::new(C(List(elems))), pos)); + stack.push((Rc::new(C(List(elems, pos_list))), pos)); Ok(()) } diff --git a/src/build/opcode/test.rs b/src/build/opcode/test.rs deleted file mode 100644 index 6a576f7..0000000 --- a/src/build/opcode/test.rs +++ /dev/null @@ -1,913 +0,0 @@ -// 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::cell::RefCell; -use std::rc::Rc; - -use super::environment::Environment; -use super::translate::PositionMap; -use super::Composite::{List, Tuple}; -use super::Op::{ - Add, Bang, Bind, BindOver, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, Index, InitList, - InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, NewScope, Noop, Pop, Render, - Return, SelectJump, Sub, Sym, Typ, Val, -}; -use super::Primitive::{Bool, Empty, Float, Int, Str}; -use super::Value::{C, P}; -use super::VM; -use crate::ast::Position; - -macro_rules! assert_cases { - (__impl__ $cases:expr) => { - for case in $cases.drain(0..) { - let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new()))); - let mut positions = Vec::with_capacity(case.0.len()); - for _ in 0..case.0.len() { - positions.push(Position::new(0, 0, 0)); - } - let map = PositionMap{ - ops: case.0, - pos: positions, - }; - let mut vm = VM::new(Rc::new(map), env); - vm.run().unwrap(); - assert_eq!(vm.pop().unwrap().0, Rc::new(case.1)); - } - }; - - (($input:expr, $result:expr), $($tok:tt)*) => { - assert_cases!(__impl__ vec![($input, $result), $($tok)*]) - }; - - ($($input:expr => $result:expr, )* ) => { - assert_cases!( - $(($input, $result),)* - ) - } -} - -#[test] -fn math_ops() { - assert_cases!( - // 1+1; - vec![Val(Int(1)), Val(Int(1)), Add] => P(Int(2)), - // 1-1; - vec![Val(Int(1)), Val(Int(1)), Sub] => P(Int(0)), - // 2*2; - vec![Val(Int(2)), Val(Int(2)), Mul] => P(Int(4)), - // 6/3; - vec![Val(Int(2)), Val(Int(6)), Div] => P(Int(3)), - // 1.0+1.0; - vec![Val(Float(1.0)), Val(Float(1.0)), Add] => P(Float(2.0)), - // 1.0-1.0; - vec![Val(Float(1.0)), Val(Float(1.0)), Sub] => P(Float(0.0)), - // 2.0*2.0; - vec![Val(Float(2.0)), Val(Float(2.0)), Mul] => P(Float(4.0)), - // 6.0/3.0; - vec![Val(Float(2.0)), Val(Float(6.0)), Div] => P(Float(3.0)), - // string concatenation - vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add] => P(Str("foobar".to_owned())), - // Composite operations - vec![ - Val(Int(1)), - Val(Int(1)), - Add, // 1 + 1 - Val(Int(1)), - Add, // 2 + 1 - Val(Int(1)), - Add, // 3 + 1 - ] => P(Int(4)), - ); -} - -#[test] -fn new_scopes() { - assert_cases![ - vec![ - Sym("foo".to_owned()), - Val(Int(1)), - Bind, - NewScope(5), - Sym("foo".to_owned()), - Val(Int(2)), - BindOver, - DeRef("foo".to_owned()), - Return, - ] => P(Int(2)), - vec![ - Sym("bar".to_owned()), - Val(Int(1)), - Bind, - NewScope(5), - Sym("foo".to_owned()), - Val(Int(2)), - Bind, - DeRef("bar".to_owned()), - Return, - ] => P(Int(1)), - ]; -} - -#[test] -fn bind_op() { - let mut cases = vec![( - vec![Sym("foo".to_owned()), Val(Int(1)), Bind], - ("foo", P(Int(1))), - vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind], - ("foo", P(Int(2))), - )]; - - for case in cases.drain(0..) { - let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new()))); - let mut positions = Vec::with_capacity(case.0.len()); - for _ in 0..case.0.len() { - positions.push(Position::new(0, 0, 0)); - } - let map = PositionMap { - ops: case.0, - pos: positions, - }; - let mut vm = VM::new(Rc::new(map), env); - vm.run().unwrap(); - let (name, result) = case.1; - let (v, _) = vm.get_binding(name, &Position::new(0, 0, 0)).unwrap(); - assert_eq!(&result, v.as_ref()); - } -} - -#[test] -fn list_ops() { - assert_cases!( - vec![InitList] => C(List(Vec::new())), - vec![InitList, Val(Int(1)), Element] => C(List(vec![Rc::new(P(Int(1)))])), - vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element] => C(List(vec![Rc::new(P(Int(2))), Rc::new(P(Int(1)))])), - vec![ - InitList, - Val(Int(1)), - Element, - Val(Int(1)), - Val(Int(1)), - Add, - Element, - ] => C(List(vec![Rc::new(P(Int(1))), Rc::new(P(Int(2)))])), - ); -} - -#[test] -fn tuple_ops() { - assert_cases!( - vec![InitTuple] => C(Tuple(Vec::new())), - vec![ - InitTuple, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - ] => C(Tuple(vec![ - ("foo".to_owned(), Rc::new(P(Int(1)))), - ])), - vec![ - InitTuple, - Sym("bar".to_owned()), - Val(Str("quux".to_owned())), - Field, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - ] => C(Tuple(vec![ - ("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))), - ("foo".to_owned(), Rc::new(P(Int(1)))), - ])), - vec![ - InitTuple, - Sym("bar".to_owned()), - Val(Str("quux".to_owned())), - Field, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - Val(Str("foo".to_owned())), - Val(Int(2)), - Field, - ] => C(Tuple(vec![ - ("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))), - ("foo".to_owned(), Rc::new(P(Int(2)))), - ])), - vec![ - InitTuple, - Sym("bar".to_owned()), - Val(Str("ux".to_owned())), - Val(Str("qu".to_owned())), - Add, - Field, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - Val(Str("foo".to_owned())), - Val(Int(2)), - Field, - ] => C(Tuple(vec![ - ("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))), - ("foo".to_owned(), Rc::new(P(Int(2)))), - ])), - vec![ - Sym("tpl".to_owned()), - InitTuple, // Target tuple - Sym("bar".to_owned()), - Val(Str("ux".to_owned())), - Val(Str("qu".to_owned())), - Add, - Field, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - Bind, - DeRef("tpl".to_owned()), - InitTuple, - // Begin the field overrides - Val(Str("foo".to_owned())), - Val(Int(2)), - Field, - Cp, // Do the tuple copy operation - ] => C(Tuple(vec![ - ("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))), - ("foo".to_owned(), Rc::new(P(Int(2)))), - ])), - ); -} - -#[test] -fn jump_ops() { - assert_cases!( - vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)), - ); -} - -#[test] -fn equality_ops() { - assert_cases![ - vec![ - Val(Str("foo".to_owned())), - Val(Str("foo".to_owned())), - Equal, - ] => P(Bool(true)), - vec![ - Val(Str("bar".to_owned())), - Val(Str("foo".to_owned())), - Equal, - ] => P(Bool(false)), - vec![Val(Int(1)), Val(Int(1)), Equal] => P(Bool(true)), - vec![Val(Int(1)), Val(Int(2)), Equal] => P(Bool(false)), - vec![Val(Bool(true)), Val(Bool(true)), Equal] => P(Bool(true)), - vec![Val(Bool(false)), Val(Bool(false)), Equal] => P(Bool(true)), - vec![Val(Bool(true)), Val(Bool(false)), Equal] => P(Bool(false)), - vec![ - InitTuple, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - InitTuple, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - Equal, - ] => P(Bool(true)), - vec![ - InitTuple, - Val(Str("foo".to_owned())), - Val(Int(1)), - Field, - InitTuple, - Val(Str("bar".to_owned())), - Val(Int(1)), - Field, - Equal, - ] => P(Bool(false)), - vec![ - InitList, - Val(Str("foo".to_owned())), - Element, - InitList, - Val(Str("foo".to_owned())), - Element, - Equal, - ] => P(Bool(true)), - vec![ - InitList, - Val(Str("foo".to_owned())), - Element, - InitList, - Val(Str("bar".to_owned())), - Element, - Equal, - ] => P(Bool(false)), - ]; -} - -#[test] -fn conditional_jump_ops() { - assert_cases![ - vec![ - Val(Bool(false)), - JumpIfTrue(2), - Val(Bool(true)), - JumpIfTrue(2), - Val(Int(1)), - Jump(1), - Val(Int(2)), - Noop, - ] => P(Int(2)), - vec![ - Val(Bool(true)), - JumpIfTrue(2), - Val(Bool(false)), - JumpIfTrue(2), - Val(Int(1)), - Jump(1), - Val(Int(2)), - Noop, - ] => P(Int(1)), - vec![ - Val(Int(1)), - Val(Int(1)), - Equal, - JumpIfTrue(2), - Val(Bool(false)), - JumpIfTrue(2), - Val(Int(1)), - Jump(1), - Val(Int(2)), - Noop, - ] => P(Int(1)), - vec![ - Val(Int(1)), - Val(Int(2)), - Equal, - JumpIfFalse(2), - Val(Bool(false)), - JumpIfTrue(2), - Val(Int(1)), - Jump(1), - Val(Int(2)), - Noop, - ] => P(Int(1)), - ]; -} - -#[test] -fn function_definition_and_call() { - assert_cases![ - vec![ - Sym("f".to_owned()), // 0 - InitList, // 1 - Sym("arg".to_owned()), // 2 - Element, // 3 - Func(2), // 4 - DeRef("arg".to_owned()), // 5 - Return, // 6 - Bind, // 7 - Val(Int(1)), // 8 - DeRef("f".to_owned()), // 9 - FCall, // 10 - ] => P(Int(1)), - vec![ - Sym("closed".to_owned()), // 0 - Val(Int(1)), // 1 - Bind, // 2 - Sym("f".to_owned()), // 3 - InitList, // 4 - Sym("arg".to_owned()), // 5 - Element, // 6 - Func(4), // 7 - DeRef("arg".to_owned()), // 8 - DeRef("closed".to_owned()), // 9 - Add, // 10 - Return, // 11 - Bind, // 12 - Val(Int(1)), // 13 - DeRef("f".to_owned()), // 14 - FCall, // 16 - ] => P(Int(2)), - ]; -} - -#[test] -fn module_call() { - assert_cases![ - vec![ - Sym("m".to_owned()), // 4 // binding name for module - InitTuple, // 5 // Module tuple bindings - Sym("one".to_owned()), // 6 - Val(Int(1)), // 7 - Field, // 8 - Sym("two".to_owned()), // 9 - Val(Int(2)), // 10 - Field, // 11 - Module(7), // 12 // Module body definition - Bind, // 13 // bind the mod tuple - Sym("foo".to_owned()), // 14 - DeRef("mod".to_owned()), // 15 - Val(Str("one".to_owned())), // 16 - Index, // 17 - Bind, // 18 // bind mod tuple to foo - Return, // 19 // end the module - Bind, // 20 // bind module to the binding name - DeRef("m".to_owned()), // 21 - InitTuple, // 0 // override tuple - Sym("one".to_owned()), // 1 - Val(Int(11)), // 2 - Field, // 3 - Cp, // 22 // Call the module - ] => C(Tuple(vec![ - ("foo".to_owned(), Rc::new(P(Int(11)))), - ])), - vec![ - Sym("m".to_owned()), // 4 // binding name for module - InitTuple, // 5 // Module tuple bindings - Sym("one".to_owned()), // 6 - Val(Int(1)), // 7 - Field, // 8 - Sym("two".to_owned()), // 9 - Val(Int(2)), // 10 - Field, // 11 - InitThunk(2), // 12 // Module Return expression - Val(Int(1)), // 13 - Return, // 14 - Module(5), // 15 // Module definition - Bind, // 16 - Sym("foo".to_owned()), // 17 - DeRef("mod".to_owned()), // 18 - Bind, // 19 // bind mod tuple to foo - Return, // 20 // end the module - Bind, // 21 // bind module to the binding name - DeRef("m".to_owned()), // 22 - InitTuple, // 0 // override tuple - Sym("one".to_owned()), // 1 - Val(Int(11)), // 2 - Field, // 3 - Cp, // 23 - ] => P(Int(1)), - ]; -} - -#[test] -fn select_short_circuit() { - assert_cases![ - vec![ - Val(Str("field".to_owned())), // 0 // search field - Sym("not_field".to_owned()), // 1 // first field to compare - SelectJump(2), // 2 - Val(Str("not our value".to_owned())), // 3 // expression for first field - Jump(4), // 4 - Sym("field".to_owned()), // 5 // second field to compare - SelectJump(2), // 6 - Val(Int(1)), // 7 // expression for second field - Jump(2), // 8 - Pop, // 9 // pop the search field off - Bang, // 10 // default case - ] => P(Int(1)), - vec![ - Val(Str("field".to_owned())), // 0 // search field - Sym("not_field".to_owned()), // 1 // first field to compare - SelectJump(2), // 2 - Val(Str("not our value".to_owned())), // 3 // expression for first field - Jump(4), // 4 - Sym("als not field".to_owned()), // 5 // second field to compare - SelectJump(2), // 6 - Val(Int(2)), // 7 // expression for second field - Jump(1), // 8 - Pop, // 9 // pop the search field off - Val(Int(1)), // 10 // default case - ] => P(Int(1)), - ]; -} - -#[test] -fn index_operation() { - assert_cases![ - vec![ - InitTuple, - Sym("foo".to_owned()), - InitTuple, - Sym("bar".to_owned()), - Val(Int(1)), - Field, - Field, - Val(Str("foo".to_owned())), - Index, - Val(Str("bar".to_owned())), - Index, - ] => P(Int(1)), - vec![ - InitList, - Val(Str("foo".to_owned())), - Element, - Val(Str("bar".to_owned())), - Element, - Val(Int(0)), - Index, - ] => P(Str("foo".to_owned())), - vec![ - InitTuple, - Sym("field".to_owned()), - InitList, - Val(Str("foo".to_owned())), - Element, - Val(Str("bar".to_owned())), - Element, - Field, - Val(Str("field".to_owned())), - Index, - Val(Int(0)), - Index, - ] => P(Str("foo".to_owned())), - ]; -} - -#[test] -fn type_comparisons() { - assert_cases![ - vec![ - Val(Str("foo".to_owned())), - Typ, - ] => P(Str("str".to_owned())), - vec![ - Val(Int(1)), - Typ, - ] => P(Str("int".to_owned())), - vec![ - Val(Float(1.0)), - Typ, - ] => P(Str("float".to_owned())), - vec![ - Val(Bool(true)), - Typ, - ] => P(Str("bool".to_owned())), - vec![ - Val(Empty), - Typ, - ] => P(Str("null".to_owned())), - vec![ - InitTuple, - Typ, - ] => P(Str("tuple".to_owned())), - vec![ - InitList, - Typ, - ] => P(Str("list".to_owned())), - vec![ - Val(Str("str".to_owned())), - Val(Str("foo".to_owned())), - Typ, - Equal, - ] => P(Bool(true)), - ]; -} - -use super::translate; -use crate::iter::OffsetStrIter; -use crate::parse::parse; - -macro_rules! assert_parse_cases { - (__impl__ $cases:expr) => { - for case in $cases.drain(0..) { - let stmts = parse(OffsetStrIter::from(dbg!(case.0)), None).unwrap(); - let root = std::env::current_dir().unwrap(); - // TODO(jwall): preprocessor - let ops = Rc::new(translate::AST::translate(stmts, &root)); - assert!(ops.len() > 0); - let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new()))); - let mut vm = VM::new(ops.clone(), env); - vm.run().unwrap(); - assert_eq!(vm.last.unwrap().0, Rc::new(case.1)); - } - }; - - ( ($input:expr, $result:expr), $( $tok:tt )* ) => { - assert_parse_cases!(__impl__ vec![($input, $result), $($tok)*]) - }; - - ( $( $input:expr => $result:expr, )* ) => { - assert_parse_cases!($(($input, $result),)*) - } -} - -#[test] -fn simple_expr_scalar_value() { - assert_parse_cases!( - "1;" => P(Int(1)), - "(1);" => P(Int(1)), - "1.0;" => P(Float(1.0)), - "true;" => P(Bool(true)), - "NULL;" => P(Empty), - "\"foo\";" => P(Str("foo".to_owned())), - ) -} - -#[test] -fn simple_binary_expr() { - assert_parse_cases!( - "1+1;" => P(Int(2)), - "2-1;" => P(Int(1)), - "2*2;" => P(Int(4)), - "6/2;" => P(Int(3)), - "4 %% 2;" => P(Int(0)), - "5 %% 2;" => P(Int(1)), - "1.0+1.0;" => P(Float(2.0)), - "\"foo\"+\"bar\";" => P(Str("foobar".to_owned())), - "1==1;" => P(Bool(true)), - "1>1;" => P(Bool(false)), - "1<1;" => P(Bool(false)), - "2>1;" => P(Bool(true)), - "2<1;" => P(Bool(false)), - "2<=1;" => P(Bool(false)), - "2>=1;" => P(Bool(true)), - "1!=1;" => P(Bool(false)), - "\"foo\" ~ \"bar\";" => P(Bool(false)), - "\"foo\" !~ \"bar\";" => P(Bool(true)), - "\"foo\" is \"str\";" => P(Bool(true)), - "true && true;" => P(Bool(true)), - "true && false;" => P(Bool(false)), - "false && false;" => P(Bool(false)), - "false && true;" => P(Bool(false)), - "false || false;" => P(Bool(false)), - "true || false;" => P(Bool(true)), - "false || true;" => P(Bool(true)), - "true || true;" => P(Bool(true)), - "foo in {foo = 1};" => P(Bool(true)), - "bar in {foo = 1};" => P(Bool(false)), - "let bar = \"foo\"; (bar) in {foo = 1};" => P(Bool(true)), - ) -} - -#[test] -fn simple_let_statements() { - assert_parse_cases![ - "let foo = 1; foo;" => P(Int(1)), - "let foo = 1 + 1; foo;" => P(Int(2)), - ]; -} - -#[test] -fn dot_expressions() { - assert_parse_cases![ - "let foo = [0,1,2]; foo.0;" => P(Int(0)), - "let foo = [0,1,2]; foo.2;" => P(Int(2)), - "let tpl = { foo = 1 }; tpl.foo;" => P(Int(1)), - "let tpl = { foo = { bar = 2 } }; tpl.foo.bar;" => P(Int(2)), - "let tpl = { foo = [ 3 ] }; tpl.foo.0;" => P(Int(3)), - ]; -} - -#[test] -fn simple_not_expr() { - assert_parse_cases!( - "not 1==1;" => P(Bool(false)), - "not 1!=1;" => P(Bool(true)), - ) -} - -#[test] -fn simple_render_operation() { - assert_cases![ - vec![ - Val(Int(1)), - Render, - ] => P(Str("1".to_owned())), - vec![ - Val(Float(1.1)), - Render, - ] => P(Str("1.1".to_owned())), - vec![ - Val(Bool(true)), - Render, - ] => P(Str("true".to_owned())), - vec![ - Val(Bool(false)), - Render, - ] => P(Str("false".to_owned())), - vec![ - Val(Empty), - Render, - ] => P(Str("NULL".to_owned())), - vec![ - Val(Str("foo".to_owned())), - Render, - ] => P(Str("foo".to_owned())), - ]; -} - -#[test] -fn simple_format_expressions() { - assert_parse_cases![ - "\"@\" % (1);" => P(Str("1".to_owned())), - "\"@\" % (1.1);" => P(Str("1.1".to_owned())), - "\"@\" % (\"foo\");" => P(Str("foo".to_owned())), - "\"@\" % (NULL);" => P(Str("NULL".to_owned())), - "\"@ @ @\" % (1, 2, 3);" => P(Str("1 2 3".to_owned())), - "\"@ \\\\@\" % (1);" => P(Str("1 @".to_owned())), - "\"@{item.num}\" % {num=1};" => P(Str("1".to_owned())), - "\"@{item.num()}\" % {num=func() => 1};" => P(Str("1".to_owned())), - ]; -} - -#[test] -fn simple_functions() { - assert_parse_cases![ - "let f = func(val) => val; f(1);" => P(Int(1)), - "let f = func(val1, val2) => val1 + val2; f(1, 1);" => P(Int(2)), - ]; -} - -#[test] -fn simple_modules() { - assert_parse_cases![ - "let m = module{} => { let v = 1; }; m{};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(1))))])), - "let m = module{} => (v) { let v = 1; }; m{};" => P(Int(1)), - "let m = module{val=NULL} => (v) { let v = mod.val; }; m{val=1};" => P(Int(1)), - "let m = module{val=NULL} => (v) { let v = mod.val + 1; }; m{val=1};" => P(Int(2)), - ]; -} - -#[test] -fn tuple_copies() { - assert_parse_cases![ - "let tpl = { v = 1, }; tpl{};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(1))))])), - "let tpl = { v = 1, }; tpl{v=2};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(2))))])), - // Tests for Self lookups - "let tpl = { v = 1, w = 2}; tpl{v=self.w, x=self.v};" => C(Tuple(vec![ - ("v".to_owned(), Rc::new(P(Int(2)))), - ("w".to_owned(), Rc::new(P(Int(2)))), - ("x".to_owned(), Rc::new(P(Int(1)))), - ])), - // the deep copy case identity copy - "let tpl = { v = 1, inner = { w = 2 }}; tpl{inner=self.inner{}};" => C(Tuple(vec![ - ("v".to_owned(), Rc::new(P(Int(1)))), - ("inner".to_owned(), Rc::new(C(Tuple(vec![ - ("w".to_owned(), Rc::new(P(Int(2)))), - ])))), - ])), - // the deep copy case modifications - "let tpl = { v = 1, inner = { w = 2 }}; tpl{inner=self.inner{w=3}};" => C(Tuple(vec![ - ("v".to_owned(), Rc::new(P(Int(1)))), - ("inner".to_owned(), Rc::new(C(Tuple(vec![ - ("w".to_owned(), Rc::new(P(Int(3)))), - ])))), - ])), - ]; -} - -#[test] -fn simple_range() { - assert_parse_cases![ - "1:2;" => C(List(vec![ - Rc::new(P(Int(1))), - Rc::new(P(Int(2))), - ])), - "0:2:4;" => C(List(vec![ - Rc::new(P(Int(0))), - Rc::new(P(Int(2))), - Rc::new(P(Int(4))), - ])), - "0:3:4;" => C(List(vec![ - Rc::new(P(Int(0))), - Rc::new(P(Int(3))), - ])), - ]; -} - -#[test] -fn simple_selects() { - assert_parse_cases![ - "select \"foo\", { foo = 1, bar = 2, };" => P(Int(1)), - "select \"bar\", { foo = 1, bar = 2, };" => P(Int(2)), - "select \"quux\", 3, { foo = 1, bar = 2, };" => P(Int(3)), - "select true, { true = 1, false = 2, };" => P(Int(1)), - ]; -} - -#[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)), - "{ok = func() => true}.ok();" => P(Bool(true)), - ]; -} - -#[test] -fn simple_trace() { - 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); - let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new()))); - let mut vm = VM::new(ops.clone(), env); - vm.run().unwrap(); - assert_eq!(vm.last.unwrap().0, Rc::new(P(Int(2)))); - let err_out = &vm.env.borrow().stderr; - assert_eq!( - String::from_utf8_lossy(err_out).to_owned(), - "TRACE: 1 + 1 = 2 at line: 1 column: 7\n" - ); -} - -#[test] -fn simple_maps() { - assert_parse_cases![ - "map(func(el) => el, [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(1))), - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - ])), - "map(func(el) => el + 1, [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - Rc::new(P(Int(4))), - ])), - "map(func(el) => el, \"foo\");" => P(Str("foo".to_owned())), - "map(func(el) => el + \"-\", \"foo\");" => P(Str("f-o-o-".to_owned())), - "map(func(k, v) => [k, v], {foo = 1});" => C(Tuple(vec![ - ("foo".to_owned(), Rc::new(P(Int(1)))), - ])), - "map(func(k, v) => [k, v + 1], {foo = 1});" => C(Tuple(vec![ - ("foo".to_owned(), Rc::new(P(Int(2)))), - ])), - ]; -} - -#[test] -fn simple_filters() { - assert_parse_cases![ - "filter(func(el) => true, [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(1))), - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - ])), - "filter(func(el) => false, [1,2,3]);" => C(List(vec![ - ])), - "filter(func(el) => el != 1, [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - ])), - "filter(func(el) => true, \"foo\");" => P(Str("foo".to_owned())), - "filter(func(el) => false, \"foo\");" => P(Str("".to_owned())), - "filter(func(el) => el != \"f\", \"foo\");" => P(Str("oo".to_owned())), - "filter(func(k, v) => true, {foo = 1});" => C(Tuple(vec![ - ("foo".to_owned(), Rc::new(P(Int(1)))), - ])), - "filter(func(k, v) => false, {foo = 1});" => C(Tuple(vec![ - ])), - "filter(func(k, v) => k != \"foo\", {foo = 1});" => C(Tuple(vec![ - ])), - "filter(func(k, v) => v != 1, {foo = 1});" => C(Tuple(vec![ - ])), - ]; -} - -#[test] -fn simple_reduces() { - assert_parse_cases![ - "reduce(func(acc, el) => acc + [el], [], [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(1))), - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - ])), - "reduce(func(acc, el) => acc + [el+1], [], [1,2,3]);" => C(List(vec![ - Rc::new(P(Int(2))), - Rc::new(P(Int(3))), - Rc::new(P(Int(4))), - ])), - "reduce(func(acc, s) => acc + s, \"\", \"foo\");" => P(Str("foo".to_owned())), - "reduce(func(acc, k, v) => acc + [[k, v]], [], {foo = 1});" => C(List(vec![ - Rc::new(C(List(vec![ - Rc::new(P(Str("foo".to_owned()))), - Rc::new(P(Int(1))), - ]))), - ])), - ]; -} - -// TODO(jwall): functions and modules comparable? diff --git a/src/build/opcode/translate.rs b/src/build/opcode/translate.rs index ebdf636..608d956 100644 --- a/src/build/opcode/translate.rs +++ b/src/build/opcode/translate.rs @@ -206,12 +206,12 @@ impl AST { ops.push(Op::Exist, def.pos.clone()); } BinaryExprType::DOT => { - // Dot expressions expect the left side to be pushed first - 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 *def.right { Expression::Copy(copy_def) => { + // Dot expressions expect the left side to be pushed first + Self::translate_expr(*def.left, &mut ops, root); // first handle the selector match copy_def.selector { Value::Str(sym) | Value::Symbol(sym) => { @@ -228,9 +228,13 @@ impl AST { } Expression::Call(call_def) => { // first push our arguments. + let count = call_def.arglist.len() as i64; for e in call_def.arglist { Self::translate_expr(e, &mut ops, root); } + ops.push(Op::Val(Primitive::Int(count)), call_def.pos.clone()); + // Dot expressions expect the left side to be pushed first + Self::translate_expr(*def.left, &mut ops, root); // then handle the selector let func_pos = call_def.funcref.pos().clone(); match call_def.funcref { @@ -247,6 +251,8 @@ impl AST { return; } Expression::Simple(Value::Symbol(name)) => { + // Dot expressions expect the left side to be pushed first + Self::translate_expr(*def.left, &mut ops, root); Self::translate_expr( Expression::Simple(Value::Str(name)), &mut ops, @@ -254,6 +260,8 @@ impl AST { ); } expr => { + // Dot expressions expect the left side to be pushed first + Self::translate_expr(*def.left, &mut ops, root); Self::translate_expr(expr, &mut ops, root); } } @@ -484,14 +492,15 @@ impl AST { ops.replace(i, Op::Jump(idx as i32)); } } - Expression::Call(def) => { - // first push our arguments. - for e in def.arglist { + Expression::Call(call_def) => { + let count = call_def.arglist.len() as i64; + for e in call_def.arglist { Self::translate_expr(e, &mut ops, root); } + ops.push(Op::Val(Primitive::Int(count)), call_def.pos.clone()); // then push the func reference - let func_pos = def.funcref.pos().clone(); - Self::translate_value(def.funcref, &mut ops, root); + let func_pos = call_def.funcref.pos().clone(); + Self::translate_value(call_def.funcref, &mut ops, root); ops.push(Op::FCall, func_pos); } Expression::Copy(def) => { diff --git a/src/build/opcode/vm.rs b/src/build/opcode/vm.rs index 796a6df..03d2e86 100644 --- a/src/build/opcode/vm.rs +++ b/src/build/opcode/vm.rs @@ -47,12 +47,14 @@ where { stack: Vec<(Rc, Position)>, symbols: Stack, + import_stack: Vec, runtime: runtime::Builtins, ops: OpPointer, pub env: Rc>>, pub last: Option<(Rc, Position)>, self_stack: Vec<(Rc, Position)>, reserved_words: BTreeSet<&'static str>, + out_lock: bool, } impl<'a, O, E> VM @@ -68,14 +70,20 @@ where Self { stack: Vec::new(), symbols: Stack::new(), + import_stack: Vec::new(), runtime: runtime::Builtins::new(), ops: ops, env: env, last: None, self_stack: Vec::new(), reserved_words: construct_reserved_word_set(), + out_lock: false, } } + pub fn with_import_stack(mut self, imports: Vec) -> Self { + self.import_stack = imports; + self + } pub fn enable_validate_mode(&mut self) { self.runtime.enable_validate_mode(); @@ -85,24 +93,28 @@ where Self { stack: Vec::new(), symbols: symbols, + import_stack: self.import_stack.clone(), runtime: self.runtime.clone(), ops: self.ops.clone(), env: self.env.clone(), last: self.last, self_stack: self.self_stack, reserved_words: self.reserved_words, + out_lock: self.out_lock, } } pub fn symbols_to_tuple(&self, include_mod: bool) -> Value { let mut flds = Vec::new(); + let mut pos_list = Vec::new(); for sym in self.symbols.symbol_list() { if include_mod || sym != "mod" { - let (val, _) = self.symbols.get(sym).unwrap().clone(); + let (val, pos) = self.symbols.get(sym).unwrap().clone(); + pos_list.push((pos.clone(), pos.clone())); flds.push((sym.clone(), val)); } } - return C(Tuple(flds)); + return C(Tuple(flds, pos_list)); } pub fn run(&mut self) -> Result<(), Error> { @@ -132,9 +144,9 @@ where Op::GtEq => self.op_gteq(pos)?, Op::LtEq => self.op_lteq(pos)?, // Add a Composite list value to the stack - Op::InitList => self.push(Rc::new(C(List(Vec::new()))), pos)?, + Op::InitList => self.push(Rc::new(C(List(Vec::new(), Vec::new()))), pos)?, // Add a composite tuple value to the stack - Op::InitTuple => self.push(Rc::new(C(Tuple(Vec::new()))), pos)?, + Op::InitTuple => self.push(Rc::new(C(Tuple(Vec::new(), Vec::new()))), pos)?, Op::Field => self.op_field()?, Op::Element => self.op_element()?, Op::Index => self.op_index(false, pos)?, @@ -182,8 +194,8 @@ where P(Bool(_)) => "bool", P(Str(_)) => "str", P(Empty) => "null", - C(Tuple(_)) => "tuple", - C(List(_)) => "list", + C(Tuple(_, _)) => "tuple", + C(List(_, _)) => "list", F(_) => "func", M(_) => "module", S(_) => "sym", @@ -308,12 +320,12 @@ where fn op_module(&'a mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> { let (mod_val, mod_val_pos) = self.pop()?; - let (result_ptr, flds) = match mod_val.as_ref() { - &C(Tuple(ref flds)) => (None, flds.clone()), + let (result_ptr, flds, pos_list) = match mod_val.as_ref() { + &C(Tuple(ref flds, ref pos_list)) => (None, flds.clone(), pos_list.clone()), &T(ptr) => { let (tpl_val, tpl_val_pos) = self.pop()?; - if let &C(Tuple(ref flds)) = tpl_val.as_ref() { - (Some(ptr), flds.clone()) + if let &C(Tuple(ref flds, ref pos_list)) = tpl_val.as_ref() { + (Some(ptr), flds.clone(), pos_list.clone()) } else { return Err(Error::new( format!("Expected tuple but got {:?}", tpl_val), @@ -357,6 +369,7 @@ where Rc::new(M(Module { ptr: ops, result_ptr: result_ptr, + flds_pos_list: pos_list, flds: flds, pkg_ptr: pkg_ptr, })), @@ -371,7 +384,7 @@ where let mut bindings = Vec::new(); // get imported symbols from stack let (list_val, args_pos) = self.pop()?; - if let &C(List(ref elems)) = list_val.as_ref() { + if let &C(List(ref elems, _)) = list_val.as_ref() { for e in elems { if let &S(ref sym) = e.as_ref() { bindings.push(sym.clone()); @@ -404,6 +417,7 @@ where f: &Func, stack: &mut Vec<(Rc, Position)>, env: Rc>>, + import_stack: &Vec, ) -> Result<(Rc, Position), Error> { let Func { ref ptr, @@ -411,7 +425,9 @@ where ref snapshot, } = f; // use the captured scope snapshot for the function. - let mut vm = Self::with_pointer(ptr.clone(), env).to_scoped(snapshot.clone()); + let mut vm = Self::with_pointer(ptr.clone(), env) + .to_scoped(snapshot.clone()) + .with_import_stack(import_stack.clone()); for nm in bindings.iter() { // now put each argument on our scope stack as a binding. // TODO(jwall): This should do a better error if there is @@ -426,7 +442,9 @@ where fn op_new_scope(&mut self, jp: i32, ptr: OpPointer) -> Result<(), Error> { let scope_snapshot = self.symbols.snapshot(); - let mut vm = Self::with_pointer(ptr, self.env.clone()).to_scoped(scope_snapshot); + let mut vm = Self::with_pointer(ptr, self.env.clone()) + .to_scoped(scope_snapshot) + .with_import_stack(self.import_stack.clone()); vm.run()?; let result = vm.pop()?; self.push(result.0, result.1)?; @@ -435,9 +453,32 @@ where } fn op_fcall(&mut self, pos: Position) -> Result<(), Error> { - let (f, _) = self.pop()?; + let (f, f_pos) = self.pop()?; + let (arg_length, _) = self.pop()?; if let &F(ref f) = f.as_ref() { - let (val, _) = Self::fcall_impl(f, &mut self.stack, self.env.clone())?; + if let &P(Int(arg_length)) = arg_length.as_ref() { + let arity = f.bindings.len() as i64; + if arg_length > arity { + return Err(Error::new( + format!( + "Func called with too many args expected {} args but got {}", + arity, arg_length + ), + pos, + )); + } + if arg_length < arity { + return Err(Error::new( + format!( + "Func called with too few args expected {} args but got {}", + arity, arg_length + ), + pos, + )); + } + } + let (val, _) = decorate_call!(f_pos => + Self::fcall_impl(f, &mut self.stack, self.env.clone(), &self.import_stack))?; self.push(val, pos.clone())?; } Ok(()) @@ -464,9 +505,19 @@ where } 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. + let (left, left_pos) = self.pop()?; + let (right, right_pos) = self.pop()?; + if left.type_name() != right.type_name() + && !(left.type_name() == "NULL" || right.type_name() == "NULL") + { + return Err(Error::new( + format!( + "Expected values of the same type but got {:?} at {} and {:?} at {} for expression", + left, left_pos, right, right_pos, + ), + pos, + )); + } self.push(Rc::new(P(Bool(left == right))), pos)?; Ok(()) } @@ -640,9 +691,9 @@ where // Add a Composite field value to a tuple on the stack // get value from stack //dbg!(&self.stack); - let (val, _) = self.pop()?; + let (val, val_pos) = self.pop()?; // get name from stack. - let (name_val, _) = self.pop()?; + let (name_val, name_pos) = self.pop()?; let name = if let &S(ref s) | &P(Str(ref s)) = name_val.as_ref() { s } else { @@ -652,12 +703,20 @@ where }; // get composite tuple from stack let (tpl, tpl_pos) = self.pop()?; - if let &C(Tuple(ref flds)) = tpl.as_ref() { + if let &C(Tuple(ref flds, ref pos_list)) = tpl.as_ref() { // add name and value to tuple let mut flds = flds.clone(); - self.merge_field_into_tuple(&mut flds, name.clone(), val)?; + let mut pos_list = pos_list.clone(); + self.merge_field_into_tuple( + &mut flds, + &mut pos_list, + name.clone(), + &name_pos, + val, + &val_pos, + )?; // place composite tuple back on stack - self.push(Rc::new(C(Tuple(flds))), tpl_pos)?; + self.push(Rc::new(C(Tuple(flds, pos_list))), tpl_pos)?; } else { unreachable!(); }; @@ -666,15 +725,17 @@ where fn op_element(&mut self) -> Result<(), Error> { // get element from stack. - let (val, _) = self.pop()?; + let (val, val_pos) = self.pop()?; // get next value. It should be a Composite list. let (list, pos) = self.pop()?; - if let &C(List(ref elems)) = list.as_ref() { + if let &C(List(ref elems, ref pos_list)) = list.as_ref() { // add value to list let mut elems = elems.clone(); elems.push(val); + let mut pos_list = pos_list.clone(); + pos_list.push(val_pos); // Add that value to the list and put list back on stack. - self.push(Rc::new(C(List(elems))), pos)?; + self.push(Rc::new(C(List(elems, pos_list))), pos)?; } else { unreachable!(); }; @@ -696,7 +757,7 @@ where let (left, _) = self.pop()?; match right.as_ref() { &P(Int(i)) => { - if let &C(List(ref elems)) = left.as_ref() { + if let &C(List(ref elems, _)) = left.as_ref() { if i < (elems.len() as i64) && i >= 0 { self.push(elems[i as usize].clone(), right_pos)?; return Ok(()); @@ -704,7 +765,7 @@ where } } &P(Str(ref s)) => { - if let &C(Tuple(ref flds)) = left.as_ref() { + if let &C(Tuple(ref flds, _)) = left.as_ref() { for &(ref key, ref val) in flds.iter() { if key == s { self.push(val.clone(), right_pos)?; @@ -731,7 +792,7 @@ where let (right, right_pos) = self.pop()?; let (left, left_pos) = self.pop()?; match left.as_ref() { - &C(Tuple(ref flds)) => { + &C(Tuple(ref flds, _)) => { if let &P(Str(ref name)) = right.as_ref() { for (ref nm, _) in flds { if nm == name { @@ -746,7 +807,7 @@ where )); } } - &C(List(ref elems)) => { + &C(List(ref elems, _)) => { for e in elems { if e == &right { self.push(Rc::new(P(Bool(true))), pos)?; @@ -773,53 +834,97 @@ where fn op_copy(&mut self, pos: Position) -> Result<(), Error> { // This value should always be a tuple - let (override_val, _) = self.pop()?; - // get targett value. It should be a Module or Tuple. + let (override_val, val_pos) = self.pop()?; + // get target value. It should be a Module or Tuple. let (tgt, tgt_pos) = self.pop()?; - let overrides = if let &C(Tuple(ref oflds)) = override_val.as_ref() { - oflds.clone() - } else { - unreachable!(); - }; + let (overrides, override_pos_list) = + if let &C(Tuple(ref oflds, ref pos_list)) = override_val.as_ref() { + (oflds.clone(), pos_list.clone()) + } else { + unreachable!(); + }; match tgt.as_ref() { - &C(Tuple(ref flds)) => { + &C(Tuple(ref flds, ref pos_list)) => { let mut flds = flds.clone(); + let mut pos_list = pos_list.clone(); + let mut counter = 0; for (name, val) in overrides { - self.merge_field_into_tuple(&mut flds, name, val)?; + let name_pos = override_pos_list[counter].0.clone(); + let val_pos = override_pos_list[counter].1.clone(); + self.merge_field_into_tuple( + &mut flds, + &mut pos_list, + name, + &name_pos, + val, + &val_pos, + )?; + counter += 1; } // Put the copy on the Stack - self.push(Rc::new(C(Tuple(flds))), tgt_pos.clone())?; + self.push(Rc::new(C(Tuple(flds, pos_list))), tgt_pos.clone())?; self.last = Some((tgt.clone(), tgt_pos)); } &M(Module { ref ptr, ref result_ptr, ref flds, + ref flds_pos_list, ref pkg_ptr, }) => { let this = M(Module { ptr: ptr.clone(), result_ptr: result_ptr.clone(), flds: flds.clone(), + flds_pos_list: flds_pos_list.clone(), pkg_ptr: pkg_ptr.clone(), }); let mut flds = flds.clone(); + let mut flds_pos_list = flds_pos_list.clone(); + let mut counter = 0; for (name, val) in overrides { - self.merge_field_into_tuple(&mut flds, name, val)?; + let name_pos = override_pos_list[counter].0.clone(); + let val_pos = override_pos_list[counter].1.clone(); + self.merge_field_into_tuple( + &mut flds, + &mut flds_pos_list, + name, + &name_pos, + val, + &val_pos, + )?; + counter += 1; } - self.merge_field_into_tuple(&mut flds, "this".to_owned(), Rc::new(this))?; + self.merge_field_into_tuple( + &mut flds, + &mut flds_pos_list, + "this".to_owned(), + &pos, + Rc::new(this), + &val_pos, + )?; if let Some(ptr) = pkg_ptr { - let mut pkg_vm = Self::with_pointer(ptr.clone(), self.env.clone()); + let mut pkg_vm = Self::with_pointer(ptr.clone(), self.env.clone()) + .with_import_stack(self.import_stack.clone()); pkg_vm.run()?; - let (pkg_func, _) = pkg_vm.pop()?; - self.merge_field_into_tuple(&mut flds, "pkg".to_owned(), pkg_func)?; + let (pkg_func, val_pos) = pkg_vm.pop()?; + self.merge_field_into_tuple( + &mut flds, + &mut flds_pos_list, + "pkg".to_owned(), + &pos, + pkg_func, + &val_pos, + )?; } - let mut vm = Self::with_pointer(ptr.clone(), self.env.clone()); + // TODO(jwall): We should have a notion of a call stack here. + let mut vm = Self::with_pointer(ptr.clone(), self.env.clone()) + .with_import_stack(self.import_stack.clone()); vm.push(Rc::new(S("mod".to_owned())), pos.clone())?; - vm.push(Rc::new(C(Tuple(flds))), pos.clone())?; - vm.run()?; + vm.push(Rc::new(C(Tuple(flds, flds_pos_list))), pos.clone())?; + decorate_call!(pos => vm.run())?; if let Some(ptr) = result_ptr { vm.ops.jump(ptr.clone())?; vm.run()?; @@ -831,7 +936,7 @@ where } _ => { return Err(Error::new( - format!("Expected a Tuple or a Module but got {:?}", tgt), + format!("Expected a Tuple or Module but got {:?}", tgt), pos, )); } @@ -842,16 +947,36 @@ where fn merge_field_into_tuple( &self, src_fields: &'a mut Vec<(String, Rc)>, + pos_fields: &'a mut Vec<(Position, Position)>, name: String, + name_pos: &Position, value: Rc, + val_pos: &Position, ) -> Result<(), Error> { + let mut counter = 0; for fld in src_fields.iter_mut() { if fld.0 == name { + if fld.1.type_name() != value.type_name() + && !(fld.1.type_name() == "NULL" || value.type_name() == "NULL") + { + return Err(Error::new( + format!( + "Expected type {} for field {} but got ({})", + fld.1.type_name(), + name, + value.type_name(), + ), + val_pos.clone(), + )); + } + pos_fields[counter].1 = val_pos.clone(); fld.1 = value; return Ok(()); } + counter += 1; } src_fields.push((name, value)); + pos_fields.push((name_pos.clone(), val_pos.clone())); Ok(()) } @@ -975,15 +1100,26 @@ where ns.push_str(&ss); P(Str(ns)) } - (C(List(ref left_list)), C(List(ref right_list))) => { - let mut new_list = Vec::with_capacity(left_list.len() + right_list.len()); + ( + C(List(ref left_list, ref left_pos_list)), + C(List(ref right_list, ref right_pos_list)), + ) => { + let cap = left_list.len() + right_list.len(); + let mut new_list = Vec::with_capacity(cap); + let mut new_pos_list = Vec::with_capacity(cap); + let mut counter = 0; for v in left_list.iter() { new_list.push(v.clone()); + new_pos_list.push(left_pos_list[counter].clone()); + counter += 1; } + counter = 0; for v in right_list.iter() { new_list.push(v.clone()); + new_pos_list.push(right_pos_list[counter].clone()); + counter += 1; } - C(List(new_list)) + C(List(new_list, new_pos_list)) } _ => { return Err(Error::new( @@ -1000,6 +1136,7 @@ where h, &mut self.stack, self.env.clone(), + &mut self.import_stack, pos, ) } diff --git a/src/error.rs b/src/error.rs index 45c2655..679d43e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -112,11 +112,7 @@ impl BuildError { Some(ref pb) => pb.to_string_lossy().to_string(), None => "".to_string(), }; - write!( - w, - "{}: {} at {} line: {}, column: {}", - self.err_type, self.msg, file, pos.line, pos.column - )?; + write!(w, "{}: {} at {}", self.err_type, self.msg, pos,)?; } else { write!(w, "{}: {}", self.err_type, self.msg)?; }