DEV: All unit tests pass.

This commit is contained in:
Jeremy Wall 2019-09-01 19:21:02 -05:00
parent 092d510feb
commit ac4dc2addd
12 changed files with 505 additions and 1110 deletions

View File

@ -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)
}

View File

@ -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=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
.unwrap(),
],
vec![Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} 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=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
.unwrap(),
Regex::new(r"Expected \(\}\) but got \(\) at <eval> line: 1, column: 9").unwrap(),
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> 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 <eval> line: 1, column: 26").unwrap(),
Regex::new(r"Expected Integer but got \(.bar.\)").unwrap(),
Regex::new(r"at <eval> 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(),
],
)
}

View File

@ -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)?;

View File

@ -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, "<Func>"),
M(_) => write!(w, "<Module>"),
T(_) => write!(w, "<Expression>"),
S(v) => write!(w, "{}", v),
}
}
}

View File

@ -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<String, String>, // Environment Variables
pub out_lock: BTreeSet<PathBuf>,
}
impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
@ -57,6 +58,7 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
importer_registry: ImporterRegistry::make_registry(),
stdout: out,
stderr: err,
out_lock: BTreeSet::new(),
}
}
@ -93,4 +95,16 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
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<P: AsRef<Path>>(&self, path: P) -> bool {
self.out_lock.contains(path.as_ref())
}
pub fn set_out_lock_for_path<P: Into<PathBuf>>(&mut self, path: P) {
self.out_lock.insert(path.into());
}
pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) {
self.out_lock.remove(path.as_ref());
}
}

View File

@ -22,6 +22,7 @@ use crate::ast::Position;
pub struct Error {
message: String,
pos: Option<Position>,
call_stack: Vec<Position>,
}
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<regex::Error> for Error {
Error {
message: format!("{}", e),
pos: None,
call_stack: Vec::new(),
}
}
}
@ -67,6 +85,7 @@ impl From<std::io::Error> for Error {
Error {
message: msg,
pos: None,
call_stack: Vec::new(),
}
}
}
@ -74,10 +93,16 @@ impl From<std::io::Error> 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(())
}
}

View File

@ -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<Rc<Value>>),
Tuple(Vec<(String, Rc<Value>)>),
List(Vec<Rc<Value>>, Vec<Position>),
Tuple(Vec<(String, Rc<Value>)>, 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<usize>,
flds: Vec<(String, Rc<Value>)>,
flds_pos_list: Vec<(Position, Position)>,
pkg_ptr: Option<OpPointer>,
}
@ -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;
}

View File

@ -66,6 +66,7 @@ impl Builtins {
h: Hook,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &mut Vec<String>,
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<O, E>(
&mut self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &mut Vec<String>,
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<dyn std::io::Write> = 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<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &Vec<String>,
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<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &Vec<String>,
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<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &Vec<String>,
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(())
}

View File

@ -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?

View File

@ -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) => {

View File

@ -47,12 +47,14 @@ where
{
stack: Vec<(Rc<Value>, Position)>,
symbols: Stack,
import_stack: Vec<String>,
runtime: runtime::Builtins,
ops: OpPointer,
pub env: Rc<RefCell<Environment<O, E>>>,
pub last: Option<(Rc<Value>, Position)>,
self_stack: Vec<(Rc<Value>, Position)>,
reserved_words: BTreeSet<&'static str>,
out_lock: bool,
}
impl<'a, O, E> VM<O, E>
@ -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<String>) -> 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<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
import_stack: &Vec<String>,
) -> Result<(Rc<Value>, 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<Value>)>,
pos_fields: &'a mut Vec<(Position, Position)>,
name: String,
name_pos: &Position,
value: Rc<Value>,
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,
)
}

View File

@ -112,11 +112,7 @@ impl BuildError {
Some(ref pb) => pb.to_string_lossy().to_string(),
None => "<eval>".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)?;
}