mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: All unit tests pass.
This commit is contained in:
parent
092d510feb
commit
ac4dc2addd
@ -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)
|
||||
}
|
||||
|
@ -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(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
52
src/build/opcode/display.rs
Normal file
52
src/build/opcode/display.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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?
|
@ -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) => {
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user