mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Integration tests all pass now.
This commit is contained in:
parent
2b64c2b4e0
commit
b3fd37a6b5
@ -69,4 +69,15 @@ let name = "foo";
|
||||
assert t.ok{
|
||||
test = (name) in {foo="foo"},
|
||||
desc = "bareword collisions with field names still works for `in` operator",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = "foo" in ["foo"],
|
||||
desc = "List presence checks work",
|
||||
};
|
||||
|
||||
let foo_string = "foo";
|
||||
assert t.ok{
|
||||
test = foo_string in ["foo"],
|
||||
desc = "List presence checks work",
|
||||
};
|
@ -190,7 +190,7 @@ where
|
||||
pub fn eval_stmts(&mut self, ast: Vec<Statement>) -> BuildResult {
|
||||
// We should probably stash this in an op_cache somewhere?
|
||||
let ops = translate::AST::translate(ast, &self.working_dir);
|
||||
let mut vm = VM::new(Rc::new(ops), self.environment.clone());
|
||||
let mut vm = VM::new(Rc::new(ops), self.environment.clone(), &self.working_dir);
|
||||
if self.validate_mode {
|
||||
vm.enable_validate_mode();
|
||||
}
|
||||
@ -227,7 +227,11 @@ where
|
||||
&mut ops_map,
|
||||
&self.working_dir,
|
||||
);
|
||||
let mut vm = VM::new(Rc::new(ops_map), self.environment.clone());
|
||||
let mut vm = VM::new(
|
||||
Rc::new(ops_map),
|
||||
self.environment.clone(),
|
||||
&self.working_dir,
|
||||
);
|
||||
if self.validate_mode {
|
||||
vm.enable_validate_mode();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use super::OpPointer;
|
||||
|
||||
/// A Cache of Op codes.
|
||||
pub struct Ops {
|
||||
ops: BTreeMap<String, Rc<PositionMap>>,
|
||||
ops: BTreeMap<PathBuf, Rc<PositionMap>>,
|
||||
}
|
||||
|
||||
impl Ops {
|
||||
@ -32,12 +32,12 @@ impl Ops {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry<'a, S: Into<String>>(&'a mut self, path: S) -> Entry<'a> {
|
||||
pub fn entry<'a, P: Into<PathBuf>>(&'a mut self, path: P) -> Entry<'a> {
|
||||
Entry(self.ops.entry(path.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Entry<'a>(btree_map::Entry<'a, String, Rc<PositionMap>>);
|
||||
pub struct Entry<'a>(btree_map::Entry<'a, PathBuf, Rc<PositionMap>>);
|
||||
|
||||
impl<'a> Entry<'a> {
|
||||
pub fn get_pointer_or_else<F: FnOnce() -> Result<PositionMap, Error>, P: Into<PathBuf>>(
|
||||
|
@ -70,14 +70,18 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
|
||||
self.val_cache.insert(path.clone(), val);
|
||||
}
|
||||
|
||||
pub fn get_ops_for_path(&mut self, path: &String) -> Result<OpPointer, Error> {
|
||||
self.op_cache.entry(path).get_pointer_or_else(
|
||||
pub fn get_ops_for_path<P>(&mut self, path: P) -> Result<OpPointer, Error>
|
||||
where
|
||||
P: Into<PathBuf> + Clone,
|
||||
{
|
||||
let path_copy = path.clone();
|
||||
self.op_cache.entry(path.clone()).get_pointer_or_else(
|
||||
|| {
|
||||
// FIXME(jwall): We need to do proper error handling here.
|
||||
let p = PathBuf::from(&path);
|
||||
let p = path.into();
|
||||
let root = p.parent().unwrap();
|
||||
// first we read in the file
|
||||
let mut f = File::open(&path)?;
|
||||
let mut f = File::open(&p)?;
|
||||
// then we parse it
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents)?;
|
||||
@ -88,7 +92,7 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
|
||||
let ops = super::translate::AST::translate(stmts, &root);
|
||||
Ok(ops)
|
||||
},
|
||||
&path,
|
||||
path_copy,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,6 @@ macro_rules! decorate_call {
|
||||
match $result {
|
||||
Ok(v) => Ok(v),
|
||||
Err(mut e) => {
|
||||
dbg!(&$pos);
|
||||
e.push_call_stack($pos.clone());
|
||||
Err(e)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
@ -29,7 +30,6 @@ use Composite::{List, Tuple};
|
||||
use Primitive::{Bool, Empty, Int, Str};
|
||||
|
||||
pub struct Builtins {
|
||||
working_dir: PathBuf,
|
||||
import_path: Vec<PathBuf>,
|
||||
validate_mode: bool,
|
||||
}
|
||||
@ -37,12 +37,7 @@ pub struct Builtins {
|
||||
impl Builtins {
|
||||
pub fn new() -> Self {
|
||||
// FIXME(jwall): This should probably be injected in.
|
||||
Self::with_working_dir(std::env::current_dir().unwrap())
|
||||
}
|
||||
|
||||
pub fn with_working_dir<P: Into<PathBuf>>(path: P) -> Self {
|
||||
Self {
|
||||
working_dir: path.into(),
|
||||
import_path: Vec::new(),
|
||||
validate_mode: false,
|
||||
}
|
||||
@ -50,7 +45,6 @@ impl Builtins {
|
||||
|
||||
pub fn clone(&self) -> Self {
|
||||
Self {
|
||||
working_dir: self.working_dir.clone(),
|
||||
import_path: self.import_path.clone(),
|
||||
validate_mode: self.validate_mode,
|
||||
}
|
||||
@ -60,22 +54,25 @@ impl Builtins {
|
||||
self.validate_mode = true;
|
||||
}
|
||||
|
||||
pub fn handle<P: AsRef<Path>, O, E>(
|
||||
pub fn handle<P, WP, O, E>(
|
||||
&mut self,
|
||||
path: Option<P>,
|
||||
h: Hook,
|
||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
import_stack: &mut Vec<String>,
|
||||
working_dir: WP,
|
||||
pos: Position,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
WP: Into<PathBuf> + Clone + Debug,
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
{
|
||||
match h {
|
||||
Hook::Import => self.import(stack, env, import_stack, pos),
|
||||
Hook::Include => self.include(stack, env, pos),
|
||||
Hook::Import => self.import(working_dir, stack, env, import_stack, pos),
|
||||
Hook::Include => self.include(working_dir, stack, env, pos),
|
||||
Hook::Assert => self.assert(stack, env),
|
||||
Hook::Convert => self.convert(stack, env, pos),
|
||||
Hook::Out => self.out(path, stack, env, pos),
|
||||
@ -88,15 +85,23 @@ impl Builtins {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_file<P: Into<PathBuf>>(
|
||||
fn normalize_path<P, BP>(
|
||||
&self,
|
||||
path: P,
|
||||
base_path: BP,
|
||||
use_import_path: bool,
|
||||
pos: Position,
|
||||
) -> Result<PathBuf, Error> {
|
||||
path: P,
|
||||
) -> Result<PathBuf, Error>
|
||||
where
|
||||
BP: Into<PathBuf>,
|
||||
P: Into<PathBuf>,
|
||||
{
|
||||
// Try a relative path first.
|
||||
let path = path.into();
|
||||
let mut normalized = self.working_dir.clone();
|
||||
// stdlib paths are special
|
||||
if path.starts_with("std/") {
|
||||
return Ok(path);
|
||||
}
|
||||
let mut normalized = base_path.into();
|
||||
if path.is_relative() {
|
||||
normalized.push(&path);
|
||||
// First see if the normalized file exists or not.
|
||||
@ -115,6 +120,23 @@ impl Builtins {
|
||||
} else {
|
||||
normalized = path;
|
||||
}
|
||||
Ok(normalized.canonicalize()?)
|
||||
}
|
||||
|
||||
fn find_file<P, BP>(
|
||||
&self,
|
||||
base_path: BP,
|
||||
path: P,
|
||||
use_import_path: bool,
|
||||
pos: Position,
|
||||
) -> Result<PathBuf, Error>
|
||||
where
|
||||
P: Into<PathBuf>,
|
||||
BP: Into<PathBuf>,
|
||||
{
|
||||
// Try a relative path first.
|
||||
// FIXME(jwall): Use import paths if desired.
|
||||
let normalized = self.normalize_path(base_path, use_import_path, path)?;
|
||||
match normalized.canonicalize() {
|
||||
Ok(p) => Ok(p),
|
||||
Err(_e) => Err(Error::new(
|
||||
@ -124,10 +146,11 @@ impl Builtins {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_file_as_string(&self, path: &str, pos: Position) -> Result<String, Error> {
|
||||
fn get_file_as_string<P: Into<PathBuf>>(&self, base_path: P, path: &str, pos: Position) -> Result<String, Error> {
|
||||
let sep = format!("{}", std::path::MAIN_SEPARATOR);
|
||||
let raw_path = path.replace("/", &sep);
|
||||
let normalized = self.find_file(raw_path, false, pos)?;
|
||||
// FIXME(jwall): import paths?
|
||||
let normalized = self.find_file(base_path, raw_path, false, pos)?;
|
||||
// TODO(jwall): Proper error here
|
||||
let mut f = File::open(normalized)?;
|
||||
let mut contents = String::new();
|
||||
@ -136,8 +159,9 @@ impl Builtins {
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
fn import<O, E>(
|
||||
fn import<P, O, E>(
|
||||
&mut self,
|
||||
base_path: P,
|
||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
import_stack: &mut Vec<String>,
|
||||
@ -146,31 +170,37 @@ impl Builtins {
|
||||
where
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
P: Into<PathBuf> + Clone + Debug,
|
||||
{
|
||||
let path = stack.pop();
|
||||
if let Some((val, path_pos)) = path {
|
||||
if let &Value::P(Str(ref path)) = val.as_ref() {
|
||||
// TODO(jwall): A bit hacky we should probably change import stacks to be pathbufs.
|
||||
let normalized = decorate_error!(path_pos => self.normalize_path(base_path, false, path))?;
|
||||
let path = normalized.to_string_lossy().to_string();
|
||||
if import_stack
|
||||
.iter()
|
||||
.find(|p| *p == path)
|
||||
.find(|p| *p == &path)
|
||||
.is_some() {
|
||||
return Err(Error::new(
|
||||
format!("You can only have one output per file"),
|
||||
format!("Import cycle detected: {} in {:?}", path, import_stack),
|
||||
pos));
|
||||
}
|
||||
import_stack.push(path.clone());
|
||||
let mut borrowed_env = env.borrow_mut();
|
||||
match borrowed_env.get_cached_path_val(path) {
|
||||
let val = {
|
||||
env.borrow_mut().get_cached_path_val(&path)
|
||||
};
|
||||
match val {
|
||||
Some(v) => {
|
||||
stack.push((v, path_pos));
|
||||
}
|
||||
None => {
|
||||
let op_pointer =
|
||||
decorate_error!(path_pos => borrowed_env.get_ops_for_path(path))?;
|
||||
let mut vm = VM::with_pointer(op_pointer, env.clone());
|
||||
decorate_error!(path_pos => env.borrow_mut().get_ops_for_path(&normalized))?;
|
||||
// TODO(jwall): What if we don't have a base path?
|
||||
let mut vm = VM::with_pointer(op_pointer, env.clone(), normalized.parent().unwrap());
|
||||
vm.run()?;
|
||||
let result = Rc::new(vm.symbols_to_tuple(true));
|
||||
borrowed_env.update_path_val(&path, result.clone());
|
||||
env.borrow_mut().update_path_val(&path, result.clone());
|
||||
stack.push((result, pos));
|
||||
}
|
||||
}
|
||||
@ -181,8 +211,9 @@ impl Builtins {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn include<O, E>(
|
||||
fn include<P, O, E>(
|
||||
&self,
|
||||
base_path: P,
|
||||
stack: &mut Vec<(Rc<Value>, Position)>,
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
pos: Position,
|
||||
@ -190,6 +221,7 @@ impl Builtins {
|
||||
where
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
P: Into<PathBuf> + Clone + Debug,
|
||||
{
|
||||
let path = stack.pop();
|
||||
let typ = stack.pop();
|
||||
@ -216,14 +248,14 @@ impl Builtins {
|
||||
};
|
||||
if typ == "str" {
|
||||
stack.push((
|
||||
Rc::new(P(Str(self.get_file_as_string(&path, pos.clone())?))),
|
||||
Rc::new(P(Str(self.get_file_as_string(base_path, &path, pos.clone())?))),
|
||||
pos.clone(),
|
||||
));
|
||||
} else {
|
||||
stack.push((
|
||||
Rc::new(match env.borrow().importer_registry.get_importer(&typ) {
|
||||
Some(importer) => {
|
||||
let contents = self.get_file_as_string(&path, pos.clone())?;
|
||||
let contents = self.get_file_as_string(base_path, &path, pos.clone())?;
|
||||
if contents.len() == 0 {
|
||||
eprintln!("including an empty file. Use NULL as the result");
|
||||
P(Empty)
|
||||
|
@ -13,7 +13,10 @@
|
||||
// limitations under the License.
|
||||
use std::path::Path;
|
||||
|
||||
use crate::ast::{BinaryExprType, Expression, FormatArgs, Position, Statement, Token, Value};
|
||||
use crate::ast::{
|
||||
BinaryExprType, BinaryOpDef, Expression, FormatArgs, Position, PositionedItem, SelectDef,
|
||||
Statement, Token, TokenType, Value,
|
||||
};
|
||||
use crate::ast::{FuncOpDef, TemplatePart};
|
||||
use crate::build::format::{ExpressionTemplate, SimpleTemplate, TemplateParser};
|
||||
use crate::build::opcode::Primitive;
|
||||
@ -186,18 +189,38 @@ impl AST {
|
||||
ops.push(Op::Mod, def.pos);
|
||||
}
|
||||
BinaryExprType::IN => {
|
||||
// Dot expressions expect the left side to be pushed first
|
||||
Self::translate_expr(*def.right, &mut ops, root);
|
||||
// Dot expressions expect the right side to be pushed first
|
||||
Self::translate_expr(*def.right.clone(), &mut ops, root);
|
||||
// Symbols on the left side should be converted to strings to satisfy
|
||||
// the Index operation contract.
|
||||
// FIXME(jwall): List checks.
|
||||
match *def.left {
|
||||
// FIXME(jwall): List checks should not use symbol translation.
|
||||
match *def.left.clone() {
|
||||
Expression::Simple(Value::Symbol(name)) => {
|
||||
Self::translate_expr(
|
||||
Expression::Simple(Value::Str(name)),
|
||||
&mut ops,
|
||||
root,
|
||||
);
|
||||
// We really just want an expression that turns a symbol
|
||||
// into a name if the subject is a tuple and doesn't
|
||||
// otherwise
|
||||
let new_expr = Expression::Select(SelectDef {
|
||||
val: Box::new(Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::IS,
|
||||
right: Box::new(Expression::Simple(Value::Str(
|
||||
PositionedItem::new(
|
||||
"tuple".to_owned(),
|
||||
def.left.pos().clone(),
|
||||
),
|
||||
))),
|
||||
left: def.right.clone(),
|
||||
pos: def.left.pos().clone(),
|
||||
})),
|
||||
default: Some(Box::new(Expression::Simple(Value::Symbol(
|
||||
name.clone(),
|
||||
)))),
|
||||
tuple: vec![(
|
||||
Token::new("true", TokenType::BAREWORD, def.right.pos()),
|
||||
Expression::Simple(Value::Str(name)),
|
||||
)],
|
||||
pos: def.left.pos().clone(),
|
||||
});
|
||||
Self::translate_expr(new_expr, &mut ops, root);
|
||||
}
|
||||
expr => {
|
||||
Self::translate_expr(expr, &mut ops, root);
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::Position;
|
||||
@ -45,6 +46,7 @@ where
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
{
|
||||
working_dir: PathBuf,
|
||||
stack: Vec<(Rc<Value>, Position)>,
|
||||
symbols: Stack,
|
||||
import_stack: Vec<String>,
|
||||
@ -54,7 +56,6 @@ where
|
||||
pub last: Option<(Rc<Value>, Position)>,
|
||||
self_stack: Vec<(Rc<Value>, Position)>,
|
||||
reserved_words: BTreeSet<&'static str>,
|
||||
out_lock: bool,
|
||||
}
|
||||
|
||||
impl<'a, O, E> VM<O, E>
|
||||
@ -62,12 +63,21 @@ where
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
{
|
||||
pub fn new(ops: Rc<PositionMap>, env: Rc<RefCell<Environment<O, E>>>) -> Self {
|
||||
Self::with_pointer(OpPointer::new(ops), env)
|
||||
pub fn new<P: Into<PathBuf>>(
|
||||
ops: Rc<PositionMap>,
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
working_dir: P,
|
||||
) -> Self {
|
||||
Self::with_pointer(OpPointer::new(ops), env, working_dir)
|
||||
}
|
||||
|
||||
pub fn with_pointer(ops: OpPointer, env: Rc<RefCell<Environment<O, E>>>) -> Self {
|
||||
pub fn with_pointer<P: Into<PathBuf>>(
|
||||
ops: OpPointer,
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
working_dir: P,
|
||||
) -> Self {
|
||||
Self {
|
||||
working_dir: working_dir.into(),
|
||||
stack: Vec::new(),
|
||||
symbols: Stack::new(),
|
||||
import_stack: Vec::new(),
|
||||
@ -77,9 +87,14 @@ where
|
||||
last: None,
|
||||
self_stack: Vec::new(),
|
||||
reserved_words: construct_reserved_word_set(),
|
||||
out_lock: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_new_pointer(mut self, ops: OpPointer) -> Self {
|
||||
self.ops = ops;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_import_stack(mut self, imports: Vec<String>) -> Self {
|
||||
self.import_stack = imports;
|
||||
self
|
||||
@ -89,21 +104,26 @@ where
|
||||
self.runtime.enable_validate_mode();
|
||||
}
|
||||
|
||||
pub fn to_scoped(self, symbols: Stack) -> Self {
|
||||
pub fn clean_copy(&self) -> Self {
|
||||
Self {
|
||||
working_dir: self.working_dir.clone(),
|
||||
stack: Vec::new(),
|
||||
symbols: symbols,
|
||||
import_stack: self.import_stack.clone(),
|
||||
symbols: Stack::new(),
|
||||
import_stack: Vec::new(),
|
||||
runtime: self.runtime.clone(),
|
||||
ops: self.ops.clone(),
|
||||
env: self.env.clone(),
|
||||
last: self.last,
|
||||
self_stack: self.self_stack,
|
||||
reserved_words: self.reserved_words,
|
||||
out_lock: self.out_lock,
|
||||
last: None,
|
||||
self_stack: self.self_stack.clone(),
|
||||
reserved_words: self.reserved_words.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scoped(mut self, symbols: Stack) -> Self {
|
||||
self.symbols = symbols;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn symbols_to_tuple(&self, include_mod: bool) -> Value {
|
||||
let mut flds = Vec::new();
|
||||
let mut pos_list = Vec::new();
|
||||
@ -183,6 +203,9 @@ where
|
||||
Op::PopSelf => self.op_pop_self()?,
|
||||
};
|
||||
}
|
||||
if let Some(p) = self.ops.path.as_ref() {
|
||||
self.import_stack.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -425,7 +448,7 @@ where
|
||||
ref snapshot,
|
||||
} = f;
|
||||
// use the captured scope snapshot for the function.
|
||||
let mut vm = Self::with_pointer(ptr.clone(), env)
|
||||
let mut vm = Self::with_pointer(ptr.clone(), env, std::env::current_dir()?)
|
||||
.to_scoped(snapshot.clone())
|
||||
.with_import_stack(import_stack.clone());
|
||||
for nm in bindings.iter() {
|
||||
@ -442,7 +465,9 @@ where
|
||||
|
||||
fn op_new_scope(&mut self, jp: i32, ptr: OpPointer) -> Result<(), Error> {
|
||||
let scope_snapshot = self.symbols.snapshot();
|
||||
let mut vm = Self::with_pointer(ptr, self.env.clone())
|
||||
let mut vm = self
|
||||
.clean_copy()
|
||||
.to_new_pointer(ptr)
|
||||
.to_scoped(scope_snapshot)
|
||||
.with_import_stack(self.import_stack.clone());
|
||||
vm.run()?;
|
||||
@ -809,7 +834,7 @@ where
|
||||
}
|
||||
&C(List(ref elems, _)) => {
|
||||
for e in elems {
|
||||
if e == &right {
|
||||
if dbg!(e) == dbg!(&right) {
|
||||
self.push(Rc::new(P(Bool(true))), pos)?;
|
||||
return Ok(());
|
||||
}
|
||||
@ -905,7 +930,9 @@ where
|
||||
&val_pos,
|
||||
)?;
|
||||
if let Some(ptr) = pkg_ptr {
|
||||
let mut pkg_vm = Self::with_pointer(ptr.clone(), self.env.clone())
|
||||
let mut pkg_vm = self
|
||||
.clean_copy()
|
||||
.to_new_pointer(ptr.clone())
|
||||
.with_import_stack(self.import_stack.clone());
|
||||
pkg_vm.run()?;
|
||||
let (pkg_func, val_pos) = pkg_vm.pop()?;
|
||||
@ -919,8 +946,9 @@ where
|
||||
)?;
|
||||
}
|
||||
|
||||
// TODO(jwall): We should have a notion of a call stack here.
|
||||
let mut vm = Self::with_pointer(ptr.clone(), self.env.clone())
|
||||
let mut vm = self
|
||||
.clean_copy()
|
||||
.to_new_pointer(ptr.clone())
|
||||
.with_import_stack(self.import_stack.clone());
|
||||
vm.push(Rc::new(S("mod".to_owned())), pos.clone())?;
|
||||
vm.push(Rc::new(C(Tuple(flds, flds_pos_list))), pos.clone())?;
|
||||
@ -1137,6 +1165,7 @@ where
|
||||
&mut self.stack,
|
||||
self.env.clone(),
|
||||
&mut self.import_stack,
|
||||
&self.working_dir,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ assert t.equal{
|
||||
|
||||
assert t.ok{
|
||||
test = tpl.has_fields{tpl={foo=1, bar=2}, fields=["foo", "bar"]},
|
||||
desc = "tuple has fields has foo and bar fields",
|
||||
desc = "tuple has foo and bar fields",
|
||||
};
|
||||
|
||||
assert t.not_ok{
|
||||
|
Loading…
x
Reference in New Issue
Block a user