mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-24 18:39:50 -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 {
|
impl std::fmt::Display for Position {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
if let Some(ref file) = self.file {
|
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)
|
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() {
|
fn test_assert_just_keyword_compile_failures() {
|
||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"assert ",
|
"assert ",
|
||||||
vec![
|
vec![Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at line: 1 column: 8").unwrap()],
|
||||||
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
|
|
||||||
.unwrap(),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,9 +171,8 @@ fn test_assert_partial_tuple_compile_failures() {
|
|||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"assert {",
|
"assert {",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at <eval> line: 1, column: 8")
|
Regex::new(r"Expected Tuple \{ok=<bool>, desc=<str>\} at line: 1 column: 8").unwrap(),
|
||||||
.unwrap(),
|
Regex::new(r"Expected \(\}\) but got \(\) at line: 1 column: 9").unwrap(),
|
||||||
Regex::new(r"Expected \(\}\) but got \(\) at <eval> line: 1, column: 9").unwrap(),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -187,7 +183,7 @@ fn test_assert_partial_tuple_missing_ok_compile_failures() {
|
|||||||
"assert {};",
|
"assert {};",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
|
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, };",
|
"assert { ok = 1, };",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(),
|
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, };",
|
"assert { ok=true, };",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
|
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 };",
|
"assert { ok=true, desc = 1 };",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(),
|
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() {
|
fn test_import_missing_path_compile_failure() {
|
||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"import ;",
|
"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() {
|
fn test_import_path_not_a_string_compile_failure() {
|
||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"import 1;",
|
"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() {
|
fn test_binary_operator_missing_operand_compile_failure() {
|
||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"1 +",
|
"1 +",
|
||||||
vec![
|
vec![Regex::new(r"Missing operand for binary expression at line: 1 column: 4").unwrap()],
|
||||||
Regex::new(r"Missing operand for binary expression at <eval> line: 1, column: 4")
|
|
||||||
.unwrap(),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,10 +350,10 @@ fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
|
|||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"1 == \"foo\";",
|
"1 == \"foo\";",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(
|
Regex::new(r"Expected values of the same type").unwrap(),
|
||||||
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",
|
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(),
|
||||||
.unwrap(),
|
Regex::new(r"for expression at line: 1 column: 1").unwrap(),
|
||||||
Regex::new(r"line: 1 column: 1").unwrap(),
|
Regex::new(r"line: 1 column: 1").unwrap(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -372,7 +365,7 @@ fn test_incomplete_tuple_compile_failure() {
|
|||||||
"{;",
|
"{;",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
|
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",
|
"{foo",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
|
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=",
|
"{foo=",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Not a Bareword").unwrap(),
|
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\" %",
|
"\"foo\" %",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected format arguments").unwrap(),
|
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",
|
"\"foo\" % (1",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(\)\) but got \(\)").unwrap(),
|
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",
|
"[1",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(\]\) but got \(\)").unwrap(),
|
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",
|
"out",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected converter name").unwrap(),
|
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",
|
"out json",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected Expression to export").unwrap(),
|
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 {};",
|
"out json {};\nout json {};",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"You can only have one output per file").unwrap(),
|
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 ",
|
"let ",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected name for binding").unwrap(),
|
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 ",
|
"let foo ",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(=\) but got \(\)").unwrap(),
|
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 =",
|
"let foo =",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected Expression to bind").unwrap(),
|
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",
|
"let foo = 1",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected \(;\) but got \(\)").unwrap(),
|
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(
|
assert_build_failure(
|
||||||
"let foo = 1;\nfoo{};",
|
"let foo = 1;\nfoo{};",
|
||||||
vec![
|
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(),
|
Regex::new(r"line: 2 column: 1").unwrap(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -525,8 +518,8 @@ fn test_copy_expression_wrong_field_type_compile_failure() {
|
|||||||
assert_build_failure(
|
assert_build_failure(
|
||||||
"let foo = {bar=1};\nfoo{bar=[]};",
|
"let foo = {bar=1};\nfoo{bar=[]};",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Expected type Integer for field bar but got \(List\)").unwrap(),
|
Regex::new(r"Expected type Int for field bar but got \(List\)").unwrap(),
|
||||||
Regex::new(r"at <eval> line: 2, column: 5").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);",
|
"let foo = func() => 1;\nfoo(1);",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Func called with too many args").unwrap(),
|
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(
|
assert_build_failure(
|
||||||
"let foo = func(i) => 1 + i;\nfoo(\"bar\");",
|
"let foo = func(i) => 1 + i;\nfoo(\"bar\");",
|
||||||
vec![
|
vec![
|
||||||
Regex::new(r"Func evaluation failed").unwrap(),
|
Regex::new(r"Expected Int but got String\(bar\)").unwrap(),
|
||||||
Regex::new(r"at <eval> line: 1, column: 26").unwrap(),
|
Regex::new(r"at line: 1 column: 26").unwrap(),
|
||||||
Regex::new(r"Expected Integer but got \(.bar.\)").unwrap(),
|
Regex::new(r"line: 2 column: 1").unwrap(),
|
||||||
Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use super::Composite;
|
use super::Composite;
|
||||||
@ -30,14 +29,14 @@ impl fmt::Debug for Value {
|
|||||||
P(Float(v)) => write!(w, "Float({})", v),
|
P(Float(v)) => write!(w, "Float({})", v),
|
||||||
P(Str(v)) => write!(w, "String({})", v),
|
P(Str(v)) => write!(w, "String({})", v),
|
||||||
P(Empty) => write!(w, "NULL"),
|
P(Empty) => write!(w, "NULL"),
|
||||||
C(List(ref els)) => {
|
C(List(ref els, _)) => {
|
||||||
write!(w, "List[")?;
|
write!(w, "List[")?;
|
||||||
for e in els {
|
for e in els {
|
||||||
write!(w, "{:?},", e)?;
|
write!(w, "{:?},", e)?;
|
||||||
}
|
}
|
||||||
write!(w, "]")
|
write!(w, "]")
|
||||||
}
|
}
|
||||||
C(Tuple(ref flds)) => {
|
C(Tuple(ref flds, _)) => {
|
||||||
write!(w, "Tuple(")?;
|
write!(w, "Tuple(")?;
|
||||||
for (k, v) in flds {
|
for (k, v) in flds {
|
||||||
write!(w, "\"{}\"={:?},", k, v)?;
|
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.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::cache;
|
use super::cache;
|
||||||
@ -40,6 +40,7 @@ where
|
|||||||
pub stdout: Stdout,
|
pub stdout: Stdout,
|
||||||
pub stderr: Stderr,
|
pub stderr: Stderr,
|
||||||
pub env_vars: BTreeMap<String, String>, // Environment Variables
|
pub env_vars: BTreeMap<String, String>, // Environment Variables
|
||||||
|
pub out_lock: BTreeSet<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
|
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(),
|
importer_registry: ImporterRegistry::make_registry(),
|
||||||
stdout: out,
|
stdout: out,
|
||||||
stderr: err,
|
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) {
|
pub fn record_assert_result(&mut self, desc: &str, ok: bool) {
|
||||||
self.assert_results.record_assert_result(desc, ok);
|
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 {
|
pub struct Error {
|
||||||
message: String,
|
message: String,
|
||||||
pos: Option<Position>,
|
pos: Option<Position>,
|
||||||
|
call_stack: Vec<Position>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -29,6 +30,7 @@ impl Error {
|
|||||||
Self {
|
Self {
|
||||||
message: msg,
|
message: msg,
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
|
call_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,14 +38,29 @@ impl Error {
|
|||||||
self.pos = Some(pos);
|
self.pos = Some(pos);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_call_stack(&mut self, pos: Position) {
|
||||||
|
self.call_stack.push(pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! decorate_error {
|
macro_rules! decorate_error {
|
||||||
($pos:expr => $result:expr) => {
|
($pos:expr => $result:expr) => {
|
||||||
match $result {
|
match $result {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(e) => {
|
Err(e) => Err(e.with_pos($pos.clone())),
|
||||||
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 {
|
Error {
|
||||||
message: format!("{}", e),
|
message: format!("{}", e),
|
||||||
pos: None,
|
pos: None,
|
||||||
|
call_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +85,7 @@ impl From<std::io::Error> for Error {
|
|||||||
Error {
|
Error {
|
||||||
message: msg,
|
message: msg,
|
||||||
pos: None,
|
pos: None,
|
||||||
|
call_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,10 +93,16 @@ impl From<std::io::Error> for Error {
|
|||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(ref pos) = self.pos {
|
if let Some(ref pos) = self.pos {
|
||||||
write!(f, "{} at {}", self.message, pos)
|
write!(f, "{} at {}", self.message, pos)?;
|
||||||
} else {
|
} 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 cache;
|
||||||
mod debug;
|
mod debug;
|
||||||
|
mod display;
|
||||||
pub mod environment;
|
pub mod environment;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod error;
|
mod error;
|
||||||
@ -55,8 +56,8 @@ impl Value {
|
|||||||
P(Str(_)) => "String",
|
P(Str(_)) => "String",
|
||||||
P(Bool(_)) => "Bool",
|
P(Bool(_)) => "Bool",
|
||||||
P(Empty) => "NULL",
|
P(Empty) => "NULL",
|
||||||
C(List(_)) => "List",
|
C(List(_, _)) => "List",
|
||||||
C(Tuple(_)) => "Tuple",
|
C(Tuple(_, _)) => "Tuple",
|
||||||
F(_) => "Func",
|
F(_) => "Func",
|
||||||
M(_) => "Func",
|
M(_) => "Func",
|
||||||
T(_) => "Expression",
|
T(_) => "Expression",
|
||||||
@ -79,8 +80,8 @@ impl From<&Primitive> for String {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Composite {
|
pub enum Composite {
|
||||||
List(Vec<Rc<Value>>),
|
List(Vec<Rc<Value>>, Vec<Position>),
|
||||||
Tuple(Vec<(String, Rc<Value>)>),
|
Tuple(Vec<(String, Rc<Value>)>, Vec<(Position, Position)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
use Composite::{List, Tuple};
|
use Composite::{List, Tuple};
|
||||||
@ -89,7 +90,7 @@ impl From<&Composite> for String {
|
|||||||
fn from(c: &Composite) -> Self {
|
fn from(c: &Composite) -> Self {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
match c {
|
match c {
|
||||||
&List(ref elems) => {
|
&List(ref elems, _) => {
|
||||||
buf.push_str("[");
|
buf.push_str("[");
|
||||||
for e in elems.iter() {
|
for e in elems.iter() {
|
||||||
let val: String = e.as_ref().into();
|
let val: String = e.as_ref().into();
|
||||||
@ -98,7 +99,7 @@ impl From<&Composite> for String {
|
|||||||
}
|
}
|
||||||
buf.push_str("]");
|
buf.push_str("]");
|
||||||
}
|
}
|
||||||
&Tuple(ref flds) => {
|
&Tuple(ref flds, _) => {
|
||||||
buf.push_str("{");
|
buf.push_str("{");
|
||||||
for &(ref k, ref v) in flds.iter() {
|
for &(ref k, ref v) in flds.iter() {
|
||||||
buf.push_str(&k);
|
buf.push_str(&k);
|
||||||
@ -126,6 +127,7 @@ pub struct Module {
|
|||||||
ptr: OpPointer,
|
ptr: OpPointer,
|
||||||
result_ptr: Option<usize>,
|
result_ptr: Option<usize>,
|
||||||
flds: Vec<(String, Rc<Value>)>,
|
flds: Vec<(String, Rc<Value>)>,
|
||||||
|
flds_pos_list: Vec<(Position, Position)>,
|
||||||
pkg_ptr: Option<OpPointer>,
|
pkg_ptr: Option<OpPointer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,8 +247,8 @@ impl PartialEq for Value {
|
|||||||
fn eq(&self, other: &Value) -> bool {
|
fn eq(&self, other: &Value) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(P(left), P(right)) => left == right,
|
(P(left), P(right)) => left == right,
|
||||||
(C(List(left)), C(List(right))) => left == right,
|
(C(List(left, _)), C(List(right, _))) => left == right,
|
||||||
(C(Tuple(left)), C(Tuple(right))) => {
|
(C(Tuple(left, _)), C(Tuple(right, _))) => {
|
||||||
if left.len() != right.len() {
|
if left.len() != right.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -293,7 +295,7 @@ impl From<&Value> for Val {
|
|||||||
P(Float(f)) => Val::Float(*f),
|
P(Float(f)) => Val::Float(*f),
|
||||||
P(Str(s)) => Val::Str(s.clone()),
|
P(Str(s)) => Val::Str(s.clone()),
|
||||||
P(Bool(b)) => Val::Boolean(*b),
|
P(Bool(b)) => Val::Boolean(*b),
|
||||||
C(Tuple(fs)) => {
|
C(Tuple(fs, _)) => {
|
||||||
let mut flds = Vec::new();
|
let mut flds = Vec::new();
|
||||||
for &(ref k, ref v) in fs.iter() {
|
for &(ref k, ref v) in fs.iter() {
|
||||||
let v = v.clone();
|
let v = v.clone();
|
||||||
@ -301,7 +303,7 @@ impl From<&Value> for Val {
|
|||||||
}
|
}
|
||||||
Val::Tuple(flds)
|
Val::Tuple(flds)
|
||||||
}
|
}
|
||||||
C(List(elems)) => {
|
C(List(elems, _)) => {
|
||||||
let mut els = Vec::new();
|
let mut els = Vec::new();
|
||||||
for e in elems.iter() {
|
for e in elems.iter() {
|
||||||
let e = e.clone();
|
let e = e.clone();
|
||||||
@ -336,30 +338,35 @@ impl From<&Val> for Value {
|
|||||||
Val::Empty => P(Empty),
|
Val::Empty => P(Empty),
|
||||||
Val::List(els) => {
|
Val::List(els) => {
|
||||||
let mut lst = Vec::new();
|
let mut lst = Vec::new();
|
||||||
|
let mut positions = Vec::new();
|
||||||
for e in els.iter() {
|
for e in els.iter() {
|
||||||
let e = e.clone();
|
let e = e.clone();
|
||||||
lst.push(Rc::new(e.into()));
|
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) => {
|
Val::Tuple(flds) => {
|
||||||
let mut field_list = Vec::new();
|
let mut field_list = Vec::new();
|
||||||
|
let mut positions = Vec::new();
|
||||||
for &(ref key, ref val) in flds.iter() {
|
for &(ref key, ref val) in flds.iter() {
|
||||||
let val = val.clone();
|
let val = val.clone();
|
||||||
field_list.push((key.clone(), Rc::new(val.into())));
|
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) => {
|
Val::Env(flds) => {
|
||||||
let mut field_list = Vec::new();
|
let mut field_list = Vec::new();
|
||||||
|
let mut positions = Vec::new();
|
||||||
for &(ref key, ref val) in flds.iter() {
|
for &(ref key, ref val) in flds.iter() {
|
||||||
field_list.push((key.clone(), Rc::new(P(Str(val.clone())))));
|
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,
|
h: Hook,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &mut Vec<String>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -73,14 +74,14 @@ impl Builtins {
|
|||||||
E: std::io::Write,
|
E: std::io::Write,
|
||||||
{
|
{
|
||||||
match h {
|
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::Include => self.include(stack, env, pos),
|
||||||
Hook::Assert => self.assert(stack, env),
|
Hook::Assert => self.assert(stack, env),
|
||||||
Hook::Convert => self.convert(stack, env, pos),
|
Hook::Convert => self.convert(stack, env, pos),
|
||||||
Hook::Out => self.out(path, stack, env, pos),
|
Hook::Out => self.out(path, stack, env, pos),
|
||||||
Hook::Map => self.map(stack, env, pos),
|
Hook::Map => self.map(stack, env, import_stack, pos),
|
||||||
Hook::Filter => self.filter(stack, env, pos),
|
Hook::Filter => self.filter(stack, env, import_stack, pos),
|
||||||
Hook::Reduce => self.reduce(stack, env, pos),
|
Hook::Reduce => self.reduce(stack, env, import_stack, pos),
|
||||||
Hook::Regex => self.regex(stack, pos),
|
Hook::Regex => self.regex(stack, pos),
|
||||||
Hook::Range => self.range(stack, pos),
|
Hook::Range => self.range(stack, pos),
|
||||||
Hook::Trace(pos) => self.trace(stack, pos, env),
|
Hook::Trace(pos) => self.trace(stack, pos, env),
|
||||||
@ -135,18 +136,11 @@ impl Builtins {
|
|||||||
Ok(contents)
|
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>(
|
fn import<O, E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &mut Vec<String>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -156,6 +150,15 @@ impl Builtins {
|
|||||||
let path = stack.pop();
|
let path = stack.pop();
|
||||||
if let Some((val, path_pos)) = path {
|
if let Some((val, path_pos)) = path {
|
||||||
if let &Value::P(Str(ref path)) = val.as_ref() {
|
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();
|
let mut borrowed_env = env.borrow_mut();
|
||||||
match borrowed_env.get_cached_path_val(path) {
|
match borrowed_env.get_cached_path_val(path) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
@ -250,32 +253,46 @@ impl Builtins {
|
|||||||
O: std::io::Write,
|
O: std::io::Write,
|
||||||
E: std::io::Write,
|
E: std::io::Write,
|
||||||
{
|
{
|
||||||
let tuple = stack.pop();
|
if let Some((tuple, tpl_pos)) = stack.pop() {
|
||||||
if let Some((val, tpl_pos)) = tuple.clone() {
|
if let &Value::C(Tuple(ref tuple_flds, _)) = tuple.as_ref() {
|
||||||
if let &Value::C(Tuple(ref tuple)) = val.as_ref() {
|
|
||||||
// look for the description field
|
// look for the description field
|
||||||
let mut desc = None;
|
let mut desc = None;
|
||||||
// look for the ok field.
|
// look for the ok field.
|
||||||
let mut ok = None;
|
let mut ok = None;
|
||||||
for &(ref name, ref val) in tuple.iter() {
|
for &(ref name, ref val) in tuple_flds.iter() {
|
||||||
if name == "desc" {
|
if name == "desc" {
|
||||||
desc = Some(val.clone());
|
desc = Some(val.as_ref());
|
||||||
}
|
}
|
||||||
if name == "ok" {
|
if name == "ok" {
|
||||||
ok = Some(val.clone());
|
ok = Some(val.as_ref());
|
||||||
}
|
|
||||||
}
|
|
||||||
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(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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!(
|
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
|
tuple, tpl_pos
|
||||||
);
|
);
|
||||||
env.borrow_mut().record_assert_result(&msg, false);
|
env.borrow_mut().record_assert_result(&msg, false);
|
||||||
@ -297,8 +314,20 @@ impl Builtins {
|
|||||||
E: std::io::Write,
|
E: std::io::Write,
|
||||||
{
|
{
|
||||||
let mut writer: Box<dyn std::io::Write> = if let Some(path) = path {
|
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)?)
|
Box::new(File::create(path)?)
|
||||||
} else {
|
} 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())
|
Box::new(std::io::stdout())
|
||||||
};
|
};
|
||||||
let val = stack.pop();
|
let val = stack.pop();
|
||||||
@ -306,7 +335,7 @@ impl Builtins {
|
|||||||
let val = val.into();
|
let val = val.into();
|
||||||
let c_type = stack.pop();
|
let c_type = stack.pop();
|
||||||
if let Some((c_type_val, c_type_pos)) = c_type {
|
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 Some(c) = env.borrow().converter_registry.get_converter(c_type) {
|
||||||
if let Err(e) = c.convert(Rc::new(val), &mut writer) {
|
if let Err(e) = c.convert(Rc::new(val), &mut writer) {
|
||||||
return Err(Error::new(format!("{}", e), pos.clone()));
|
return Err(Error::new(format!("{}", e), pos.clone()));
|
||||||
@ -377,6 +406,7 @@ impl Builtins {
|
|||||||
&self,
|
&self,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &Vec<String>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -403,24 +433,35 @@ impl Builtins {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match list.as_ref() {
|
match list.as_ref() {
|
||||||
&C(List(ref elems)) => {
|
&C(List(ref elems, ref elems_pos_list)) => {
|
||||||
let mut result_elems = Vec::new();
|
let mut result_elems = Vec::new();
|
||||||
|
let mut pos_elems = Vec::new();
|
||||||
|
let mut counter = 0;
|
||||||
for e in elems.iter() {
|
for e in elems.iter() {
|
||||||
// push function argument on the stack.
|
// 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.
|
// 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);
|
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();
|
let mut new_fields = Vec::new();
|
||||||
for (ref name, ref val) in _flds {
|
let mut new_flds_pos_list = Vec::new();
|
||||||
stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone()));
|
let mut counter = 0;
|
||||||
stack.push((val.clone(), list_pos.clone()));
|
for (ref name, ref val) in flds {
|
||||||
let (result, result_pos) = VM::fcall_impl(f, stack, env.clone())?;
|
let name_pos = flds_pos_list[counter].0.clone();
|
||||||
if let &C(List(ref fval)) = result.as_ref() {
|
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.
|
// we expect them to be a list of exactly 2 items.
|
||||||
if fval.len() != 2 {
|
if fval.len() != 2 {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
@ -437,17 +478,21 @@ impl Builtins {
|
|||||||
result_pos,
|
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()));
|
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)) => {
|
&P(Str(ref s)) => {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
||||||
// call function and push it's result on the stack.
|
// 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() {
|
if let &P(Str(ref s)) = result.as_ref() {
|
||||||
buf.push_str(s);
|
buf.push_str(s);
|
||||||
} else {
|
} else {
|
||||||
@ -473,6 +518,7 @@ impl Builtins {
|
|||||||
&self,
|
&self,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &Vec<String>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -499,47 +545,65 @@ impl Builtins {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match list.as_ref() {
|
match list.as_ref() {
|
||||||
&C(List(ref elems)) => {
|
&C(List(ref elems, ref elems_pos_list)) => {
|
||||||
let mut result_elems = Vec::new();
|
let mut result_elems = Vec::new();
|
||||||
|
let mut pos_elems = Vec::new();
|
||||||
|
let mut counter = 0;
|
||||||
for e in elems.iter() {
|
for e in elems.iter() {
|
||||||
// push function argument on the stack.
|
// 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.
|
// 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
|
// Check for empty or boolean results and only push e back in
|
||||||
// if they are non empty and true
|
// if they are non empty and true
|
||||||
|
counter += 1;
|
||||||
match condition.as_ref() {
|
match condition.as_ref() {
|
||||||
&P(Empty) | &P(Bool(false)) => {
|
&P(Empty) | &P(Bool(false)) => {
|
||||||
continue;
|
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();
|
let mut new_fields = Vec::new();
|
||||||
for (ref name, ref val) in _flds {
|
let mut new_flds_pos_list = Vec::new();
|
||||||
stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone()));
|
let mut counter = 0;
|
||||||
stack.push((val.clone(), list_pos.clone()));
|
for (ref name, ref val) in flds {
|
||||||
let (condition, _) = VM::fcall_impl(f, stack, env.clone())?;
|
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
|
// Check for empty or boolean results and only push e back in
|
||||||
// if they are non empty and true
|
// if they are non empty and true
|
||||||
|
counter += 1;
|
||||||
match condition.as_ref() {
|
match condition.as_ref() {
|
||||||
&P(Empty) | &P(Bool(false)) => {
|
&P(Empty) | &P(Bool(false)) => {
|
||||||
continue;
|
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)) => {
|
&P(Str(ref s)) => {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
||||||
// call function and push it's result on the stack.
|
// 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
|
// Check for empty or boolean results and only push c back in
|
||||||
// if they are non empty and true
|
// if they are non empty and true
|
||||||
match condition.as_ref() {
|
match condition.as_ref() {
|
||||||
@ -600,6 +664,7 @@ impl Builtins {
|
|||||||
&self,
|
&self,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &Vec<String>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -632,36 +697,46 @@ impl Builtins {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match list.as_ref() {
|
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() {
|
for e in elems.iter() {
|
||||||
|
let e_pos = elems_pos_list[counter].clone();
|
||||||
// push function arguments on the stack.
|
// push function arguments on the stack.
|
||||||
stack.push((acc.clone(), acc_pos.clone()));
|
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.
|
// 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 = new_acc;
|
||||||
acc_pos = new_acc_pos;
|
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() {
|
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.
|
// push function arguments on the stack.
|
||||||
stack.push((acc.clone(), acc_pos.clone()));
|
stack.push((acc.clone(), acc_pos.clone()));
|
||||||
stack.push((Rc::new(P(Str(name.clone()))), list_pos.clone()));
|
stack.push((Rc::new(P(Str(name.clone()))), name_pos));
|
||||||
stack.push((val.clone(), list_pos.clone()));
|
stack.push((val.clone(), val_pos));
|
||||||
// call function and push it's result on the stack.
|
// 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 = new_acc;
|
||||||
acc_pos = new_acc_pos;
|
acc_pos = new_acc_pos;
|
||||||
|
counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&P(Str(ref _s)) => {
|
&P(Str(ref s)) => {
|
||||||
for c in _s.chars() {
|
for c in s.chars() {
|
||||||
// push function arguments on the stack.
|
// push function arguments on the stack.
|
||||||
stack.push((acc.clone(), acc_pos.clone()));
|
stack.push((acc.clone(), acc_pos.clone()));
|
||||||
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
stack.push((Rc::new(P(Str(c.to_string()))), list_pos.clone()));
|
||||||
// call function and push it's result on the stack.
|
// 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 = new_acc;
|
||||||
acc_pos = new_acc_pos;
|
acc_pos = new_acc_pos;
|
||||||
}
|
}
|
||||||
@ -701,6 +776,7 @@ impl Builtins {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut elems = Vec::new();
|
let mut elems = Vec::new();
|
||||||
|
let mut pos_list = Vec::new();
|
||||||
match (start.as_ref(), step.as_ref(), end.as_ref()) {
|
match (start.as_ref(), step.as_ref(), end.as_ref()) {
|
||||||
(&P(Int(start)), &P(Int(step)), &P(Int(end))) => {
|
(&P(Int(start)), &P(Int(step)), &P(Int(end))) => {
|
||||||
let mut num = start;
|
let mut num = start;
|
||||||
@ -709,6 +785,7 @@ impl Builtins {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
elems.push(Rc::new(P(Int(num))));
|
elems.push(Rc::new(P(Int(num))));
|
||||||
|
pos_list.push(pos.clone());
|
||||||
num += step;
|
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(())
|
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());
|
ops.push(Op::Exist, def.pos.clone());
|
||||||
}
|
}
|
||||||
BinaryExprType::DOT => {
|
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
|
// Symbols on the right side should be converted to strings to satisfy
|
||||||
// the Index operation contract.
|
// the Index operation contract.
|
||||||
match *def.right {
|
match *def.right {
|
||||||
Expression::Copy(copy_def) => {
|
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
|
// first handle the selector
|
||||||
match copy_def.selector {
|
match copy_def.selector {
|
||||||
Value::Str(sym) | Value::Symbol(sym) => {
|
Value::Str(sym) | Value::Symbol(sym) => {
|
||||||
@ -228,9 +228,13 @@ impl AST {
|
|||||||
}
|
}
|
||||||
Expression::Call(call_def) => {
|
Expression::Call(call_def) => {
|
||||||
// first push our arguments.
|
// first push our arguments.
|
||||||
|
let count = call_def.arglist.len() as i64;
|
||||||
for e in call_def.arglist {
|
for e in call_def.arglist {
|
||||||
Self::translate_expr(e, &mut ops, root);
|
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
|
// then handle the selector
|
||||||
let func_pos = call_def.funcref.pos().clone();
|
let func_pos = call_def.funcref.pos().clone();
|
||||||
match call_def.funcref {
|
match call_def.funcref {
|
||||||
@ -247,6 +251,8 @@ impl AST {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Expression::Simple(Value::Symbol(name)) => {
|
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(
|
Self::translate_expr(
|
||||||
Expression::Simple(Value::Str(name)),
|
Expression::Simple(Value::Str(name)),
|
||||||
&mut ops,
|
&mut ops,
|
||||||
@ -254,6 +260,8 @@ impl AST {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
expr => {
|
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);
|
Self::translate_expr(expr, &mut ops, root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,14 +492,15 @@ impl AST {
|
|||||||
ops.replace(i, Op::Jump(idx as i32));
|
ops.replace(i, Op::Jump(idx as i32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call(def) => {
|
Expression::Call(call_def) => {
|
||||||
// first push our arguments.
|
let count = call_def.arglist.len() as i64;
|
||||||
for e in def.arglist {
|
for e in call_def.arglist {
|
||||||
Self::translate_expr(e, &mut ops, root);
|
Self::translate_expr(e, &mut ops, root);
|
||||||
}
|
}
|
||||||
|
ops.push(Op::Val(Primitive::Int(count)), call_def.pos.clone());
|
||||||
// then push the func reference
|
// then push the func reference
|
||||||
let func_pos = def.funcref.pos().clone();
|
let func_pos = call_def.funcref.pos().clone();
|
||||||
Self::translate_value(def.funcref, &mut ops, root);
|
Self::translate_value(call_def.funcref, &mut ops, root);
|
||||||
ops.push(Op::FCall, func_pos);
|
ops.push(Op::FCall, func_pos);
|
||||||
}
|
}
|
||||||
Expression::Copy(def) => {
|
Expression::Copy(def) => {
|
||||||
|
@ -47,12 +47,14 @@ where
|
|||||||
{
|
{
|
||||||
stack: Vec<(Rc<Value>, Position)>,
|
stack: Vec<(Rc<Value>, Position)>,
|
||||||
symbols: Stack,
|
symbols: Stack,
|
||||||
|
import_stack: Vec<String>,
|
||||||
runtime: runtime::Builtins,
|
runtime: runtime::Builtins,
|
||||||
ops: OpPointer,
|
ops: OpPointer,
|
||||||
pub env: Rc<RefCell<Environment<O, E>>>,
|
pub env: Rc<RefCell<Environment<O, E>>>,
|
||||||
pub last: Option<(Rc<Value>, Position)>,
|
pub last: Option<(Rc<Value>, Position)>,
|
||||||
self_stack: Vec<(Rc<Value>, Position)>,
|
self_stack: Vec<(Rc<Value>, Position)>,
|
||||||
reserved_words: BTreeSet<&'static str>,
|
reserved_words: BTreeSet<&'static str>,
|
||||||
|
out_lock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, O, E> VM<O, E>
|
impl<'a, O, E> VM<O, E>
|
||||||
@ -68,14 +70,20 @@ where
|
|||||||
Self {
|
Self {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
symbols: Stack::new(),
|
symbols: Stack::new(),
|
||||||
|
import_stack: Vec::new(),
|
||||||
runtime: runtime::Builtins::new(),
|
runtime: runtime::Builtins::new(),
|
||||||
ops: ops,
|
ops: ops,
|
||||||
env: env,
|
env: env,
|
||||||
last: None,
|
last: None,
|
||||||
self_stack: Vec::new(),
|
self_stack: Vec::new(),
|
||||||
reserved_words: construct_reserved_word_set(),
|
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) {
|
pub fn enable_validate_mode(&mut self) {
|
||||||
self.runtime.enable_validate_mode();
|
self.runtime.enable_validate_mode();
|
||||||
@ -85,24 +93,28 @@ where
|
|||||||
Self {
|
Self {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
symbols: symbols,
|
symbols: symbols,
|
||||||
|
import_stack: self.import_stack.clone(),
|
||||||
runtime: self.runtime.clone(),
|
runtime: self.runtime.clone(),
|
||||||
ops: self.ops.clone(),
|
ops: self.ops.clone(),
|
||||||
env: self.env.clone(),
|
env: self.env.clone(),
|
||||||
last: self.last,
|
last: self.last,
|
||||||
self_stack: self.self_stack,
|
self_stack: self.self_stack,
|
||||||
reserved_words: self.reserved_words,
|
reserved_words: self.reserved_words,
|
||||||
|
out_lock: self.out_lock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn symbols_to_tuple(&self, include_mod: bool) -> Value {
|
pub fn symbols_to_tuple(&self, include_mod: bool) -> Value {
|
||||||
let mut flds = Vec::new();
|
let mut flds = Vec::new();
|
||||||
|
let mut pos_list = Vec::new();
|
||||||
for sym in self.symbols.symbol_list() {
|
for sym in self.symbols.symbol_list() {
|
||||||
if include_mod || sym != "mod" {
|
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));
|
flds.push((sym.clone(), val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return C(Tuple(flds));
|
return C(Tuple(flds, pos_list));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<(), Error> {
|
pub fn run(&mut self) -> Result<(), Error> {
|
||||||
@ -132,9 +144,9 @@ where
|
|||||||
Op::GtEq => self.op_gteq(pos)?,
|
Op::GtEq => self.op_gteq(pos)?,
|
||||||
Op::LtEq => self.op_lteq(pos)?,
|
Op::LtEq => self.op_lteq(pos)?,
|
||||||
// Add a Composite list value to the stack
|
// 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
|
// 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::Field => self.op_field()?,
|
||||||
Op::Element => self.op_element()?,
|
Op::Element => self.op_element()?,
|
||||||
Op::Index => self.op_index(false, pos)?,
|
Op::Index => self.op_index(false, pos)?,
|
||||||
@ -182,8 +194,8 @@ where
|
|||||||
P(Bool(_)) => "bool",
|
P(Bool(_)) => "bool",
|
||||||
P(Str(_)) => "str",
|
P(Str(_)) => "str",
|
||||||
P(Empty) => "null",
|
P(Empty) => "null",
|
||||||
C(Tuple(_)) => "tuple",
|
C(Tuple(_, _)) => "tuple",
|
||||||
C(List(_)) => "list",
|
C(List(_, _)) => "list",
|
||||||
F(_) => "func",
|
F(_) => "func",
|
||||||
M(_) => "module",
|
M(_) => "module",
|
||||||
S(_) => "sym",
|
S(_) => "sym",
|
||||||
@ -308,12 +320,12 @@ where
|
|||||||
|
|
||||||
fn op_module(&'a mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> {
|
fn op_module(&'a mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> {
|
||||||
let (mod_val, mod_val_pos) = self.pop()?;
|
let (mod_val, mod_val_pos) = self.pop()?;
|
||||||
let (result_ptr, flds) = match mod_val.as_ref() {
|
let (result_ptr, flds, pos_list) = match mod_val.as_ref() {
|
||||||
&C(Tuple(ref flds)) => (None, flds.clone()),
|
&C(Tuple(ref flds, ref pos_list)) => (None, flds.clone(), pos_list.clone()),
|
||||||
&T(ptr) => {
|
&T(ptr) => {
|
||||||
let (tpl_val, tpl_val_pos) = self.pop()?;
|
let (tpl_val, tpl_val_pos) = self.pop()?;
|
||||||
if let &C(Tuple(ref flds)) = tpl_val.as_ref() {
|
if let &C(Tuple(ref flds, ref pos_list)) = tpl_val.as_ref() {
|
||||||
(Some(ptr), flds.clone())
|
(Some(ptr), flds.clone(), pos_list.clone())
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
format!("Expected tuple but got {:?}", tpl_val),
|
format!("Expected tuple but got {:?}", tpl_val),
|
||||||
@ -357,6 +369,7 @@ where
|
|||||||
Rc::new(M(Module {
|
Rc::new(M(Module {
|
||||||
ptr: ops,
|
ptr: ops,
|
||||||
result_ptr: result_ptr,
|
result_ptr: result_ptr,
|
||||||
|
flds_pos_list: pos_list,
|
||||||
flds: flds,
|
flds: flds,
|
||||||
pkg_ptr: pkg_ptr,
|
pkg_ptr: pkg_ptr,
|
||||||
})),
|
})),
|
||||||
@ -371,7 +384,7 @@ where
|
|||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
// get imported symbols from stack
|
// get imported symbols from stack
|
||||||
let (list_val, args_pos) = self.pop()?;
|
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 {
|
for e in elems {
|
||||||
if let &S(ref sym) = e.as_ref() {
|
if let &S(ref sym) = e.as_ref() {
|
||||||
bindings.push(sym.clone());
|
bindings.push(sym.clone());
|
||||||
@ -404,6 +417,7 @@ where
|
|||||||
f: &Func,
|
f: &Func,
|
||||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||||
env: Rc<RefCell<Environment<O, E>>>,
|
env: Rc<RefCell<Environment<O, E>>>,
|
||||||
|
import_stack: &Vec<String>,
|
||||||
) -> Result<(Rc<Value>, Position), Error> {
|
) -> Result<(Rc<Value>, Position), Error> {
|
||||||
let Func {
|
let Func {
|
||||||
ref ptr,
|
ref ptr,
|
||||||
@ -411,7 +425,9 @@ where
|
|||||||
ref snapshot,
|
ref snapshot,
|
||||||
} = f;
|
} = f;
|
||||||
// use the captured scope snapshot for the function.
|
// 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() {
|
for nm in bindings.iter() {
|
||||||
// now put each argument on our scope stack as a binding.
|
// now put each argument on our scope stack as a binding.
|
||||||
// TODO(jwall): This should do a better error if there is
|
// 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> {
|
fn op_new_scope(&mut self, jp: i32, ptr: OpPointer) -> Result<(), Error> {
|
||||||
let scope_snapshot = self.symbols.snapshot();
|
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()?;
|
vm.run()?;
|
||||||
let result = vm.pop()?;
|
let result = vm.pop()?;
|
||||||
self.push(result.0, result.1)?;
|
self.push(result.0, result.1)?;
|
||||||
@ -435,9 +453,32 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn op_fcall(&mut self, pos: Position) -> Result<(), Error> {
|
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() {
|
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())?;
|
self.push(val, pos.clone())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -464,9 +505,19 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn op_equal(&mut self, pos: Position) -> Result<(), Error> {
|
fn op_equal(&mut self, pos: Position) -> Result<(), Error> {
|
||||||
let (left, _) = self.pop()?;
|
let (left, left_pos) = self.pop()?;
|
||||||
let (right, _) = self.pop()?;
|
let (right, right_pos) = self.pop()?;
|
||||||
// FIXME(jwall): We need to enforce our equality rules here.
|
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)?;
|
self.push(Rc::new(P(Bool(left == right))), pos)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -640,9 +691,9 @@ where
|
|||||||
// Add a Composite field value to a tuple on the stack
|
// Add a Composite field value to a tuple on the stack
|
||||||
// get value from stack
|
// get value from stack
|
||||||
//dbg!(&self.stack);
|
//dbg!(&self.stack);
|
||||||
let (val, _) = self.pop()?;
|
let (val, val_pos) = self.pop()?;
|
||||||
// get name from stack.
|
// 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() {
|
let name = if let &S(ref s) | &P(Str(ref s)) = name_val.as_ref() {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
@ -652,12 +703,20 @@ where
|
|||||||
};
|
};
|
||||||
// get composite tuple from stack
|
// get composite tuple from stack
|
||||||
let (tpl, tpl_pos) = self.pop()?;
|
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
|
// add name and value to tuple
|
||||||
let mut flds = flds.clone();
|
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
|
// 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 {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
@ -666,15 +725,17 @@ where
|
|||||||
|
|
||||||
fn op_element(&mut self) -> Result<(), Error> {
|
fn op_element(&mut self) -> Result<(), Error> {
|
||||||
// get element from stack.
|
// get element from stack.
|
||||||
let (val, _) = self.pop()?;
|
let (val, val_pos) = self.pop()?;
|
||||||
// get next value. It should be a Composite list.
|
// get next value. It should be a Composite list.
|
||||||
let (list, pos) = self.pop()?;
|
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
|
// add value to list
|
||||||
let mut elems = elems.clone();
|
let mut elems = elems.clone();
|
||||||
elems.push(val);
|
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.
|
// 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 {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
@ -696,7 +757,7 @@ where
|
|||||||
let (left, _) = self.pop()?;
|
let (left, _) = self.pop()?;
|
||||||
match right.as_ref() {
|
match right.as_ref() {
|
||||||
&P(Int(i)) => {
|
&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 {
|
if i < (elems.len() as i64) && i >= 0 {
|
||||||
self.push(elems[i as usize].clone(), right_pos)?;
|
self.push(elems[i as usize].clone(), right_pos)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -704,7 +765,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&P(Str(ref s)) => {
|
&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() {
|
for &(ref key, ref val) in flds.iter() {
|
||||||
if key == s {
|
if key == s {
|
||||||
self.push(val.clone(), right_pos)?;
|
self.push(val.clone(), right_pos)?;
|
||||||
@ -731,7 +792,7 @@ where
|
|||||||
let (right, right_pos) = self.pop()?;
|
let (right, right_pos) = self.pop()?;
|
||||||
let (left, left_pos) = self.pop()?;
|
let (left, left_pos) = self.pop()?;
|
||||||
match left.as_ref() {
|
match left.as_ref() {
|
||||||
&C(Tuple(ref flds)) => {
|
&C(Tuple(ref flds, _)) => {
|
||||||
if let &P(Str(ref name)) = right.as_ref() {
|
if let &P(Str(ref name)) = right.as_ref() {
|
||||||
for (ref nm, _) in flds {
|
for (ref nm, _) in flds {
|
||||||
if nm == name {
|
if nm == name {
|
||||||
@ -746,7 +807,7 @@ where
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&C(List(ref elems)) => {
|
&C(List(ref elems, _)) => {
|
||||||
for e in elems {
|
for e in elems {
|
||||||
if e == &right {
|
if e == &right {
|
||||||
self.push(Rc::new(P(Bool(true))), pos)?;
|
self.push(Rc::new(P(Bool(true))), pos)?;
|
||||||
@ -773,53 +834,97 @@ where
|
|||||||
|
|
||||||
fn op_copy(&mut self, pos: Position) -> Result<(), Error> {
|
fn op_copy(&mut self, pos: Position) -> Result<(), Error> {
|
||||||
// This value should always be a tuple
|
// This value should always be a tuple
|
||||||
let (override_val, _) = self.pop()?;
|
let (override_val, val_pos) = self.pop()?;
|
||||||
// get targett value. It should be a Module or Tuple.
|
// get target value. It should be a Module or Tuple.
|
||||||
let (tgt, tgt_pos) = self.pop()?;
|
let (tgt, tgt_pos) = self.pop()?;
|
||||||
let overrides = if let &C(Tuple(ref oflds)) = override_val.as_ref() {
|
let (overrides, override_pos_list) =
|
||||||
oflds.clone()
|
if let &C(Tuple(ref oflds, ref pos_list)) = override_val.as_ref() {
|
||||||
} else {
|
(oflds.clone(), pos_list.clone())
|
||||||
unreachable!();
|
} else {
|
||||||
};
|
unreachable!();
|
||||||
|
};
|
||||||
match tgt.as_ref() {
|
match tgt.as_ref() {
|
||||||
&C(Tuple(ref flds)) => {
|
&C(Tuple(ref flds, ref pos_list)) => {
|
||||||
let mut flds = flds.clone();
|
let mut flds = flds.clone();
|
||||||
|
let mut pos_list = pos_list.clone();
|
||||||
|
let mut counter = 0;
|
||||||
for (name, val) in overrides {
|
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
|
// 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));
|
self.last = Some((tgt.clone(), tgt_pos));
|
||||||
}
|
}
|
||||||
&M(Module {
|
&M(Module {
|
||||||
ref ptr,
|
ref ptr,
|
||||||
ref result_ptr,
|
ref result_ptr,
|
||||||
ref flds,
|
ref flds,
|
||||||
|
ref flds_pos_list,
|
||||||
ref pkg_ptr,
|
ref pkg_ptr,
|
||||||
}) => {
|
}) => {
|
||||||
let this = M(Module {
|
let this = M(Module {
|
||||||
ptr: ptr.clone(),
|
ptr: ptr.clone(),
|
||||||
result_ptr: result_ptr.clone(),
|
result_ptr: result_ptr.clone(),
|
||||||
flds: flds.clone(),
|
flds: flds.clone(),
|
||||||
|
flds_pos_list: flds_pos_list.clone(),
|
||||||
pkg_ptr: pkg_ptr.clone(),
|
pkg_ptr: pkg_ptr.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut flds = flds.clone();
|
let mut flds = flds.clone();
|
||||||
|
let mut flds_pos_list = flds_pos_list.clone();
|
||||||
|
let mut counter = 0;
|
||||||
for (name, val) in overrides {
|
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 {
|
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()?;
|
pkg_vm.run()?;
|
||||||
let (pkg_func, _) = pkg_vm.pop()?;
|
let (pkg_func, val_pos) = pkg_vm.pop()?;
|
||||||
self.merge_field_into_tuple(&mut flds, "pkg".to_owned(), pkg_func)?;
|
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(S("mod".to_owned())), pos.clone())?;
|
||||||
vm.push(Rc::new(C(Tuple(flds))), pos.clone())?;
|
vm.push(Rc::new(C(Tuple(flds, flds_pos_list))), pos.clone())?;
|
||||||
vm.run()?;
|
decorate_call!(pos => vm.run())?;
|
||||||
if let Some(ptr) = result_ptr {
|
if let Some(ptr) = result_ptr {
|
||||||
vm.ops.jump(ptr.clone())?;
|
vm.ops.jump(ptr.clone())?;
|
||||||
vm.run()?;
|
vm.run()?;
|
||||||
@ -831,7 +936,7 @@ where
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
format!("Expected a Tuple or a Module but got {:?}", tgt),
|
format!("Expected a Tuple or Module but got {:?}", tgt),
|
||||||
pos,
|
pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -842,16 +947,36 @@ where
|
|||||||
fn merge_field_into_tuple(
|
fn merge_field_into_tuple(
|
||||||
&self,
|
&self,
|
||||||
src_fields: &'a mut Vec<(String, Rc<Value>)>,
|
src_fields: &'a mut Vec<(String, Rc<Value>)>,
|
||||||
|
pos_fields: &'a mut Vec<(Position, Position)>,
|
||||||
name: String,
|
name: String,
|
||||||
|
name_pos: &Position,
|
||||||
value: Rc<Value>,
|
value: Rc<Value>,
|
||||||
|
val_pos: &Position,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
let mut counter = 0;
|
||||||
for fld in src_fields.iter_mut() {
|
for fld in src_fields.iter_mut() {
|
||||||
if fld.0 == name {
|
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;
|
fld.1 = value;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
counter += 1;
|
||||||
}
|
}
|
||||||
src_fields.push((name, value));
|
src_fields.push((name, value));
|
||||||
|
pos_fields.push((name_pos.clone(), val_pos.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -975,15 +1100,26 @@ where
|
|||||||
ns.push_str(&ss);
|
ns.push_str(&ss);
|
||||||
P(Str(ns))
|
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() {
|
for v in left_list.iter() {
|
||||||
new_list.push(v.clone());
|
new_list.push(v.clone());
|
||||||
|
new_pos_list.push(left_pos_list[counter].clone());
|
||||||
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
counter = 0;
|
||||||
for v in right_list.iter() {
|
for v in right_list.iter() {
|
||||||
new_list.push(v.clone());
|
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(
|
return Err(Error::new(
|
||||||
@ -1000,6 +1136,7 @@ where
|
|||||||
h,
|
h,
|
||||||
&mut self.stack,
|
&mut self.stack,
|
||||||
self.env.clone(),
|
self.env.clone(),
|
||||||
|
&mut self.import_stack,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -112,11 +112,7 @@ impl BuildError {
|
|||||||
Some(ref pb) => pb.to_string_lossy().to_string(),
|
Some(ref pb) => pb.to_string_lossy().to_string(),
|
||||||
None => "<eval>".to_string(),
|
None => "<eval>".to_string(),
|
||||||
};
|
};
|
||||||
write!(
|
write!(w, "{}: {} at {}", self.err_type, self.msg, pos,)?;
|
||||||
w,
|
|
||||||
"{}: {} at {} line: {}, column: {}",
|
|
||||||
self.err_type, self.msg, file, pos.line, pos.column
|
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
write!(w, "{}: {}", self.err_type, self.msg)?;
|
write!(w, "{}: {}", self.err_type, self.msg)?;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user