mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
REFACTOR: Better scope handling.
This commit is contained in:
parent
2c9abddb61
commit
2b7c8e65f8
244
src/build/mod.rs
244
src/build/mod.rs
@ -28,6 +28,7 @@ use std::string::ToString;
|
|||||||
use simple_error;
|
use simple_error;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
use crate::build::scope::{Scope, ValueMap};
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::format;
|
use crate::format;
|
||||||
use crate::iter::OffsetStrIter;
|
use crate::iter::OffsetStrIter;
|
||||||
@ -35,6 +36,7 @@ use crate::parse::parse;
|
|||||||
|
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
|
pub mod scope;
|
||||||
|
|
||||||
pub use self::ir::Val;
|
pub use self::ir::Val;
|
||||||
|
|
||||||
@ -67,12 +69,13 @@ impl MacroDef {
|
|||||||
scope.entry(self.argdefs[i].clone()).or_insert(arg.clone());
|
scope.entry(self.argdefs[i].clone()).or_insert(arg.clone());
|
||||||
}
|
}
|
||||||
let mut b = parent_builder.clone_builder(root);
|
let mut b = parent_builder.clone_builder(root);
|
||||||
b.set_scope(scope);
|
b.set_build_output(scope);
|
||||||
let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new();
|
let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new();
|
||||||
for &(ref key, ref expr) in self.fields.iter() {
|
for &(ref key, ref expr) in self.fields.iter() {
|
||||||
// We clone the expressions here because this macro may be consumed
|
// We clone the expressions here because this macro may be consumed
|
||||||
// multiple times in the future.
|
// multiple times in the future.
|
||||||
let val = b.eval_expr(expr)?;
|
let scope = b.scope.spawn_child();
|
||||||
|
let val = b.eval_expr(expr, &scope)?;
|
||||||
result.push((key.into(), val.clone()));
|
result.push((key.into(), val.clone()));
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -82,9 +85,6 @@ impl MacroDef {
|
|||||||
/// The result of a build.
|
/// The result of a build.
|
||||||
type BuildResult = Result<(), Box<Error>>;
|
type BuildResult = Result<(), Box<Error>>;
|
||||||
|
|
||||||
/// Defines a set of values in a parsed file.
|
|
||||||
type ValueMap = HashMap<PositionedItem<String>, Rc<Val>>;
|
|
||||||
|
|
||||||
/// AssertCollector collects the results of assertions in the UCG AST.
|
/// AssertCollector collects the results of assertions in the UCG AST.
|
||||||
pub struct AssertCollector {
|
pub struct AssertCollector {
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
@ -99,7 +99,7 @@ pub struct FileBuilder<'a> {
|
|||||||
validate_mode: bool,
|
validate_mode: bool,
|
||||||
pub assert_collector: AssertCollector,
|
pub assert_collector: AssertCollector,
|
||||||
strict: bool,
|
strict: bool,
|
||||||
env: Rc<Val>,
|
scope: Scope,
|
||||||
// NOTE(jwall): We use interior mutability here because we need
|
// NOTE(jwall): We use interior mutability here because we need
|
||||||
// our asset cache to be shared by multiple different sub-builders.
|
// our asset cache to be shared by multiple different sub-builders.
|
||||||
// We use Rc to handle the reference counting for us and we use
|
// We use Rc to handle the reference counting for us and we use
|
||||||
@ -112,11 +112,6 @@ pub struct FileBuilder<'a> {
|
|||||||
// so multiple imports of the same file don't have to be parsed
|
// so multiple imports of the same file don't have to be parsed
|
||||||
// multiple times.
|
// multiple times.
|
||||||
assets: Rc<RefCell<assets::Cache>>,
|
assets: Rc<RefCell<assets::Cache>>,
|
||||||
/// build_output is our built output.
|
|
||||||
build_output: ValueMap,
|
|
||||||
/// last is the result of the last statement.
|
|
||||||
import_stack: Vec<String>,
|
|
||||||
pub stack: Option<Vec<Rc<Val>>>,
|
|
||||||
pub is_module: bool,
|
pub is_module: bool,
|
||||||
pub last: Option<Rc<Val>>,
|
pub last: Option<Rc<Val>>,
|
||||||
pub out_lock: Option<(String, Rc<Val>)>,
|
pub out_lock: Option<(String, Rc<Val>)>,
|
||||||
@ -146,37 +141,21 @@ impl<'a> FileBuilder<'a> {
|
|||||||
import_paths: &'a Vec<PathBuf>,
|
import_paths: &'a Vec<PathBuf>,
|
||||||
cache: Rc<RefCell<assets::Cache>>,
|
cache: Rc<RefCell<assets::Cache>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_with_scope(file, import_paths, cache, HashMap::new())
|
let env_vars: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let scope = scope::Scope::new(Rc::new(Val::Env(env_vars)));
|
||||||
|
Self::new_with_scope(file, import_paths, cache, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new Builder with a provided scope.
|
/// Constructs a new Builder with a provided scope.
|
||||||
pub fn new_with_scope<P: Into<PathBuf>>(
|
pub fn new_with_scope<P: Into<PathBuf>>(
|
||||||
root: P,
|
|
||||||
import_paths: &'a Vec<PathBuf>,
|
|
||||||
cache: Rc<RefCell<assets::Cache>>,
|
|
||||||
scope: ValueMap,
|
|
||||||
) -> Self {
|
|
||||||
let env_vars: Vec<(String, String)> = env::vars().collect();
|
|
||||||
Self::new_with_env_and_scope(
|
|
||||||
root,
|
|
||||||
import_paths,
|
|
||||||
cache,
|
|
||||||
scope,
|
|
||||||
Rc::new(Val::Env(env_vars)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_env_and_scope<P: Into<PathBuf>>(
|
|
||||||
file: P,
|
file: P,
|
||||||
import_paths: &'a Vec<PathBuf>,
|
import_paths: &'a Vec<PathBuf>,
|
||||||
cache: Rc<RefCell<assets::Cache>>,
|
cache: Rc<RefCell<assets::Cache>>,
|
||||||
scope: ValueMap,
|
scope: Scope,
|
||||||
env: Rc<Val>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let file = file.into();
|
let file = file.into();
|
||||||
FileBuilder {
|
FileBuilder {
|
||||||
// Our import stack is initialized with ourself.
|
// Our import stack is initialized with ourself.
|
||||||
import_stack: vec![file.to_string_lossy().to_string()],
|
|
||||||
file: file,
|
file: file,
|
||||||
import_path: import_paths,
|
import_path: import_paths,
|
||||||
validate_mode: false,
|
validate_mode: false,
|
||||||
@ -185,12 +164,10 @@ impl<'a> FileBuilder<'a> {
|
|||||||
summary: String::new(),
|
summary: String::new(),
|
||||||
failures: String::new(),
|
failures: String::new(),
|
||||||
},
|
},
|
||||||
env: env,
|
scope: scope,
|
||||||
strict: true,
|
strict: true,
|
||||||
assets: cache,
|
assets: cache,
|
||||||
build_output: scope,
|
|
||||||
out_lock: None,
|
out_lock: None,
|
||||||
stack: None,
|
|
||||||
is_module: false,
|
is_module: false,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
@ -198,8 +175,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
|
|
||||||
pub fn clone_builder<P: Into<PathBuf>>(&self, file: P) -> Self {
|
pub fn clone_builder<P: Into<PathBuf>>(&self, file: P) -> Self {
|
||||||
FileBuilder {
|
FileBuilder {
|
||||||
// Our import stack is initialized with ourself.
|
|
||||||
import_stack: self.import_stack.clone(),
|
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
import_path: self.import_path,
|
import_path: self.import_path,
|
||||||
validate_mode: false,
|
validate_mode: false,
|
||||||
@ -208,49 +183,45 @@ impl<'a> FileBuilder<'a> {
|
|||||||
summary: String::new(),
|
summary: String::new(),
|
||||||
failures: String::new(),
|
failures: String::new(),
|
||||||
},
|
},
|
||||||
env: self.env.clone(),
|
|
||||||
strict: true,
|
strict: true,
|
||||||
assets: self.assets.clone(),
|
assets: self.assets.clone(),
|
||||||
build_output: HashMap::new(),
|
scope: self.scope.spawn_clean(),
|
||||||
out_lock: None,
|
out_lock: None,
|
||||||
stack: None,
|
|
||||||
is_module: false,
|
is_module: false,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scope(&mut self, scope: ValueMap) {
|
pub fn set_build_output(&mut self, scope: ValueMap) {
|
||||||
self.build_output = scope;
|
self.scope.build_output = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_strict(&mut self, to: bool) {
|
pub fn set_strict(&mut self, to: bool) {
|
||||||
self.strict = to;
|
self.strict = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepend_import_stack(&mut self, imports: &Vec<String>) {
|
fn eval_tuple(
|
||||||
let mut new_stack = self.import_stack.clone();
|
&mut self,
|
||||||
new_stack.append(imports.clone().as_mut());
|
fields: &Vec<(Token, Expression)>,
|
||||||
self.import_stack = new_stack;
|
scope: &Scope,
|
||||||
}
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
|
||||||
fn eval_tuple(&mut self, fields: &Vec<(Token, Expression)>) -> Result<Rc<Val>, Box<Error>> {
|
|
||||||
let mut new_fields = Vec::<(PositionedItem<String>, Rc<Val>)>::new();
|
let mut new_fields = Vec::<(PositionedItem<String>, Rc<Val>)>::new();
|
||||||
for &(ref name, ref expr) in fields.iter() {
|
for &(ref name, ref expr) in fields.iter() {
|
||||||
let val = self.eval_expr(expr)?;
|
let val = self.eval_expr(expr, scope)?;
|
||||||
new_fields.push((name.into(), val));
|
new_fields.push((name.into(), val));
|
||||||
}
|
}
|
||||||
Ok(Rc::new(Val::Tuple(new_fields)))
|
Ok(Rc::new(Val::Tuple(new_fields)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_list(&mut self, def: &ListDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_list(&mut self, def: &ListDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
for expr in def.elems.iter() {
|
for expr in def.elems.iter() {
|
||||||
vals.push(self.eval_expr(expr)?);
|
vals.push(self.eval_expr(expr, scope)?);
|
||||||
}
|
}
|
||||||
Ok(Rc::new(Val::List(vals)))
|
Ok(Rc::new(Val::List(vals)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_value(&mut self, v: &Value) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_value(&mut self, v: &Value, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match v {
|
match v {
|
||||||
&Value::Empty(_) => Ok(Rc::new(Val::Empty)),
|
&Value::Empty(_) => Ok(Rc::new(Val::Empty)),
|
||||||
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
|
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
|
||||||
@ -258,17 +229,18 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))),
|
&Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))),
|
||||||
&Value::Str(ref s) => Ok(Rc::new(Val::Str(s.val.to_string()))),
|
&Value::Str(ref s) => Ok(Rc::new(Val::Str(s.val.to_string()))),
|
||||||
&Value::Symbol(ref s) => {
|
&Value::Symbol(ref s) => {
|
||||||
self.lookup_sym(&(s.into()))
|
scope
|
||||||
|
.lookup_sym(&(s.into()))
|
||||||
.ok_or(Box::new(error::BuildError::new(
|
.ok_or(Box::new(error::BuildError::new(
|
||||||
format!("Unable to find binding {}", s.val,),
|
format!("Unable to find binding {}", s.val,),
|
||||||
error::ErrorType::NoSuchSymbol,
|
error::ErrorType::NoSuchSymbol,
|
||||||
v.pos().clone(),
|
v.pos().clone(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
&Value::List(ref def) => self.eval_list(def),
|
&Value::List(ref def) => self.eval_list(def, scope),
|
||||||
&Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val),
|
&Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val, scope),
|
||||||
&Value::Selector(ref selector_list_node) => {
|
&Value::Selector(ref selector_list_node) => {
|
||||||
self.lookup_selector(&selector_list_node.sel)
|
self.lookup_selector(&selector_list_node.sel, scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +251,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
pos: Position::new(0, 0, 0),
|
pos: Position::new(0, 0, 0),
|
||||||
val: name.to_string(),
|
val: name.to_string(),
|
||||||
};
|
};
|
||||||
self.lookup_sym(&key)
|
self.scope.lookup_sym(&key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts the builder in validation mode.
|
/// Puts the builder in validation mode.
|
||||||
@ -358,7 +330,11 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn detect_import_cycle(&self, path: &str) -> bool {
|
fn detect_import_cycle(&self, path: &str) -> bool {
|
||||||
self.import_stack.iter().find(|p| *p == path).is_some()
|
self.scope
|
||||||
|
.import_stack
|
||||||
|
.iter()
|
||||||
|
.find(|p| *p == path)
|
||||||
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_import(&mut self, def: &ImportDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_import(&mut self, def: &ImportDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
@ -398,7 +374,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
format!(
|
format!(
|
||||||
"Import Cycle Detected!!!! {} is already in import stack: {:?}",
|
"Import Cycle Detected!!!! {} is already in import stack: {:?}",
|
||||||
normalized.to_string_lossy(),
|
normalized.to_string_lossy(),
|
||||||
self.import_stack,
|
self.scope.import_stack,
|
||||||
),
|
),
|
||||||
error::ErrorType::Unsupported,
|
error::ErrorType::Unsupported,
|
||||||
sym.pos.clone(),
|
sym.pos.clone(),
|
||||||
@ -417,21 +393,22 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let key = sym.into();
|
let key = sym.into();
|
||||||
if self.build_output.contains_key(&key) {
|
if self.scope.build_output.contains_key(&key) {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!("Binding for import name {} already exists", sym.fragment),
|
format!("Binding for import name {} already exists", sym.fragment),
|
||||||
error::ErrorType::DuplicateBinding,
|
error::ErrorType::DuplicateBinding,
|
||||||
def.path.pos.clone(),
|
def.path.pos.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
self.build_output.insert(key, result.clone());
|
self.scope.build_output.insert(key, result.clone());
|
||||||
let mut mut_assets_cache = self.assets.borrow_mut();
|
let mut mut_assets_cache = self.assets.borrow_mut();
|
||||||
mut_assets_cache.stash(normalized.clone(), result.clone())?;
|
mut_assets_cache.stash(normalized.clone(), result.clone())?;
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_let(&mut self, def: &LetDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_let(&mut self, def: &LetDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let val = self.eval_expr(&def.value)?;
|
let child_scope = self.scope.clone();
|
||||||
|
let val = self.eval_expr(&def.value, &child_scope)?;
|
||||||
let name = &def.name;
|
let name = &def.name;
|
||||||
if Self::check_reserved_word(&name.fragment) {
|
if Self::check_reserved_word(&name.fragment) {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
@ -440,7 +417,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
name.pos.clone(),
|
name.pos.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
match self.build_output.entry(name.into()) {
|
match self.scope.build_output.entry(name.into()) {
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
@ -461,16 +438,17 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eval_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
let child_scope = self.scope.clone();
|
||||||
match stmt {
|
match stmt {
|
||||||
&Statement::Assert(ref expr) => self.build_assert(&expr),
|
&Statement::Assert(ref expr) => self.build_assert(&expr),
|
||||||
&Statement::Let(ref def) => self.eval_let(def),
|
&Statement::Let(ref def) => self.eval_let(def),
|
||||||
&Statement::Import(ref def) => self.eval_import(def),
|
&Statement::Import(ref def) => self.eval_import(def),
|
||||||
&Statement::Expression(ref expr) => self.eval_expr(expr),
|
&Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope),
|
||||||
// Only one output can be used per file. Right now we enforce this by
|
// Only one output can be used per file. Right now we enforce this by
|
||||||
// having a single builder per file.
|
// having a single builder per file.
|
||||||
&Statement::Output(ref typ, ref expr) => {
|
&Statement::Output(ref typ, ref expr) => {
|
||||||
if let None = self.out_lock {
|
if let None = self.out_lock {
|
||||||
let val = self.eval_expr(expr)?;
|
let val = self.eval_expr(expr, &child_scope)?;
|
||||||
self.out_lock = Some((typ.fragment.to_string(), val.clone()));
|
self.out_lock = Some((typ.fragment.to_string(), val.clone()));
|
||||||
Ok(val)
|
Ok(val)
|
||||||
} else {
|
} else {
|
||||||
@ -484,19 +462,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_sym(&self, sym: &PositionedItem<String>) -> Option<Rc<Val>> {
|
|
||||||
if &sym.val == "env" {
|
|
||||||
return Some(self.env.clone());
|
|
||||||
}
|
|
||||||
if &sym.val == "self" {
|
|
||||||
return self.peek_val();
|
|
||||||
}
|
|
||||||
if self.build_output.contains_key(sym) {
|
|
||||||
return Some(self.build_output[sym].clone());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_in_fieldlist(
|
fn find_in_fieldlist(
|
||||||
target: &str,
|
target: &str,
|
||||||
fs: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
fs: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
||||||
@ -580,8 +545,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_selector(&mut self, sl: &SelectorList) -> Result<Rc<Val>, Box<Error>> {
|
fn lookup_selector(&mut self, sl: &SelectorList, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let first = self.eval_expr(&sl.head)?;
|
let first = self.eval_expr(&sl.head, scope)?;
|
||||||
// First we ensure that the result is a tuple or a list.
|
// First we ensure that the result is a tuple or a list.
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
match first.as_ref() {
|
match first.as_ref() {
|
||||||
@ -892,10 +857,10 @@ impl<'a> FileBuilder<'a> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_binary(&mut self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_binary(&mut self, def: &BinaryOpDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let kind = &def.kind;
|
let kind = &def.kind;
|
||||||
let left = self.eval_expr(&def.left)?;
|
let left = self.eval_expr(&def.left, scope)?;
|
||||||
let right = self.eval_expr(&def.right)?;
|
let right = self.eval_expr(&def.right, scope)?;
|
||||||
match kind {
|
match kind {
|
||||||
&BinaryExprType::Add => self.add_vals(&def.pos, left, right),
|
&BinaryExprType::Add => self.add_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
|
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
|
||||||
@ -904,10 +869,10 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_compare(&mut self, def: &ComparisonDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_compare(&mut self, def: &ComparisonDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let kind = &def.kind;
|
let kind = &def.kind;
|
||||||
let left = self.eval_expr(&def.left)?;
|
let left = self.eval_expr(&def.left, scope)?;
|
||||||
let right = self.eval_expr(&def.right)?;
|
let right = self.eval_expr(&def.right, scope)?;
|
||||||
match kind {
|
match kind {
|
||||||
&CompareType::Equal => self.do_deep_equal(&def.pos, left, right),
|
&CompareType::Equal => self.do_deep_equal(&def.pos, left, right),
|
||||||
&CompareType::GT => self.do_gt(&def.pos, left, right),
|
&CompareType::GT => self.do_gt(&def.pos, left, right),
|
||||||
@ -918,34 +883,9 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_val(&mut self, tuple: Rc<Val>) {
|
|
||||||
if let Some(ref mut v) = self.stack {
|
|
||||||
v.push(tuple);
|
|
||||||
} else {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
v.push(tuple);
|
|
||||||
self.stack = Some(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_val(&mut self) -> Option<Rc<Val>> {
|
|
||||||
if let Some(ref mut v) = self.stack {
|
|
||||||
v.pop()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek_val(&self) -> Option<Rc<Val>> {
|
|
||||||
if let Some(ref v) = self.stack {
|
|
||||||
v.first().map(|v| v.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_outputs_as_val(&mut self) -> Rc<Val> {
|
fn get_outputs_as_val(&mut self) -> Rc<Val> {
|
||||||
let fields: Vec<(PositionedItem<String>, Rc<Val>)> = self.build_output.drain().collect();
|
let fields: Vec<(PositionedItem<String>, Rc<Val>)> =
|
||||||
|
self.scope.build_output.drain().collect();
|
||||||
Rc::new(Val::Tuple(fields))
|
Rc::new(Val::Tuple(fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -953,6 +893,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
src_fields: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
src_fields: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
||||||
overrides: &Vec<(Token, Expression)>,
|
overrides: &Vec<(Token, Expression)>,
|
||||||
|
scope: &Scope,
|
||||||
) -> Result<Rc<Val>, Box<Error>> {
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let mut m = HashMap::<PositionedItem<String>, (i32, Rc<Val>)>::new();
|
let mut m = HashMap::<PositionedItem<String>, (i32, Rc<Val>)>::new();
|
||||||
// loop through fields and build up a hashmap
|
// loop through fields and build up a hashmap
|
||||||
@ -962,7 +903,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
v.insert((count, val.clone()));
|
v.insert((count, val.clone()));
|
||||||
count += 1;
|
count += 1;
|
||||||
} else {
|
} else {
|
||||||
self.pop_val();
|
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"Duplicate \
|
"Duplicate \
|
||||||
@ -976,7 +916,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &(ref key, ref val) in overrides.iter() {
|
for &(ref key, ref val) in overrides.iter() {
|
||||||
let expr_result = self.eval_expr(val)?;
|
let expr_result = self.eval_expr(val, scope)?;
|
||||||
match m.entry(key.into()) {
|
match m.entry(key.into()) {
|
||||||
// brand new field here.
|
// brand new field here.
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
@ -993,7 +933,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
{
|
{
|
||||||
v.insert((src_val.0, expr_result));
|
v.insert((src_val.0, expr_result));
|
||||||
} else {
|
} else {
|
||||||
self.pop_val();
|
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"Expected type {} for field {} but got {}",
|
"Expected type {} for field {} but got {}",
|
||||||
@ -1008,7 +947,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.pop_val();
|
|
||||||
let mut new_fields: Vec<(PositionedItem<String>, (i32, Rc<Val>))> = m.drain().collect();
|
let mut new_fields: Vec<(PositionedItem<String>, (i32, Rc<Val>))> = m.drain().collect();
|
||||||
// We want to maintain our order for the fields to make comparing tuples
|
// We want to maintain our order for the fields to make comparing tuples
|
||||||
// easier in later code. So we sort by the field order before constructing a new tuple.
|
// easier in later code. So we sort by the field order before constructing a new tuple.
|
||||||
@ -1029,11 +967,12 @@ impl<'a> FileBuilder<'a> {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_copy(&mut self, def: &CopyDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_copy(&mut self, def: &CopyDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let v = self.lookup_selector(&def.selector.sel)?;
|
let v = self.lookup_selector(&def.selector.sel, scope)?;
|
||||||
if let &Val::Tuple(ref src_fields) = v.as_ref() {
|
if let &Val::Tuple(ref src_fields) = v.as_ref() {
|
||||||
self.push_val(v.clone());
|
let mut child_scope = scope.spawn_child();
|
||||||
return self.copy_from_base(&src_fields, &def.fields);
|
child_scope.set_curr_val(v.clone());
|
||||||
|
return self.copy_from_base(&src_fields, &def.fields, &child_scope);
|
||||||
}
|
}
|
||||||
if let &Val::Module(ref mod_def) = v.as_ref() {
|
if let &Val::Module(ref mod_def) = v.as_ref() {
|
||||||
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
||||||
@ -1045,12 +984,13 @@ impl<'a> FileBuilder<'a> {
|
|||||||
// argset.
|
// argset.
|
||||||
// Push our base tuple on the stack so the copy can use
|
// Push our base tuple on the stack so the copy can use
|
||||||
// self to reference it.
|
// self to reference it.
|
||||||
b.push_val(maybe_tpl.clone());
|
let mut child_scope = scope.spawn_child();
|
||||||
let mod_args = self.copy_from_base(src_fields, &def.fields)?;
|
child_scope.set_curr_val(maybe_tpl.clone());
|
||||||
|
let mod_args = self.copy_from_base(src_fields, &def.fields, &child_scope)?;
|
||||||
// put our copied parameters tuple in our builder under the mod key.
|
// put our copied parameters tuple in our builder under the mod key.
|
||||||
let mod_key =
|
let mod_key =
|
||||||
PositionedItem::new_with_pos(String::from("mod"), Position::new(0, 0, 0));
|
PositionedItem::new_with_pos(String::from("mod"), Position::new(0, 0, 0));
|
||||||
match b.build_output.entry(mod_key) {
|
match b.scope.build_output.entry(mod_key) {
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
@ -1090,27 +1030,27 @@ impl<'a> FileBuilder<'a> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_format(&mut self, def: &FormatDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_format(&mut self, def: &FormatDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let tmpl = &def.template;
|
let tmpl = &def.template;
|
||||||
let args = &def.args;
|
let args = &def.args;
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
for v in args.iter() {
|
for v in args.iter() {
|
||||||
let rcv = self.eval_expr(v)?;
|
let rcv = self.eval_expr(v, scope)?;
|
||||||
vals.push(rcv.deref().clone());
|
vals.push(rcv.deref().clone());
|
||||||
}
|
}
|
||||||
let formatter = format::Formatter::new(tmpl.clone(), vals);
|
let formatter = format::Formatter::new(tmpl.clone(), vals);
|
||||||
Ok(Rc::new(Val::Str(formatter.render(&def.pos)?)))
|
Ok(Rc::new(Val::Str(formatter.render(&def.pos)?)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_call(&mut self, def: &CallDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_call(&mut self, def: &CallDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let sel = &def.macroref;
|
let sel = &def.macroref;
|
||||||
let args = &def.arglist;
|
let args = &def.arglist;
|
||||||
let v = self.lookup_selector(&sel.sel)?;
|
let v = self.lookup_selector(&sel.sel, scope)?;
|
||||||
if let &Val::Macro(ref m) = v.deref() {
|
if let &Val::Macro(ref m) = v.deref() {
|
||||||
// Congratulations this is actually a macro.
|
// Congratulations this is actually a macro.
|
||||||
let mut argvals: Vec<Rc<Val>> = Vec::new();
|
let mut argvals: Vec<Rc<Val>> = Vec::new();
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
argvals.push(self.eval_expr(arg)?);
|
argvals.push(self.eval_expr(arg, scope)?);
|
||||||
}
|
}
|
||||||
let fields = m.eval(self.file.clone(), self, argvals)?;
|
let fields = m.eval(self.file.clone(), self, argvals)?;
|
||||||
return Ok(Rc::new(Val::Tuple(fields)));
|
return Ok(Rc::new(Val::Tuple(fields)));
|
||||||
@ -1148,46 +1088,46 @@ impl<'a> FileBuilder<'a> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_module_def(&mut self, def: &ModuleDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_module_def(&mut self, def: &ModuleDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let root = self.file_dir();
|
let root = self.file_dir();
|
||||||
// Always work on a copy. The original should not be modified.
|
// Always work on a copy. The original should not be modified.
|
||||||
let mut def = def.clone();
|
let mut def = def.clone();
|
||||||
// First we rewrite the imports to be absolute paths.
|
// First we rewrite the imports to be absolute paths.
|
||||||
def.imports_to_absolute(root);
|
def.imports_to_absolute(root);
|
||||||
// Then we create our tuple default.
|
// Then we create our tuple default.
|
||||||
def.arg_tuple = Some(self.eval_tuple(&def.arg_set)?);
|
def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?);
|
||||||
// Then we construct a new Val::Module
|
// Then we construct a new Val::Module
|
||||||
Ok(Rc::new(Val::Module(def)))
|
Ok(Rc::new(Val::Module(def)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_select(&mut self, def: &SelectDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_select(&mut self, def: &SelectDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let target = &def.val;
|
let target = &def.val;
|
||||||
let def_expr = &def.default;
|
let def_expr = &def.default;
|
||||||
let fields = &def.tuple;
|
let fields = &def.tuple;
|
||||||
// First resolve the target expression.
|
// First resolve the target expression.
|
||||||
let v = self.eval_expr(target)?;
|
let v = self.eval_expr(target, scope)?;
|
||||||
// Second ensure that the expression resolves to a string.
|
// Second ensure that the expression resolves to a string.
|
||||||
if let &Val::Str(ref name) = v.deref() {
|
if let &Val::Str(ref name) = v.deref() {
|
||||||
// Third find the field with that name in the tuple.
|
// Third find the field with that name in the tuple.
|
||||||
for &(ref fname, ref val_expr) in fields.iter() {
|
for &(ref fname, ref val_expr) in fields.iter() {
|
||||||
if &fname.fragment == name {
|
if &fname.fragment == name {
|
||||||
// Fourth return the result of evaluating that field.
|
// Fourth return the result of evaluating that field.
|
||||||
return self.eval_expr(val_expr);
|
return self.eval_expr(val_expr, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise return the default.
|
// Otherwise return the default.
|
||||||
return self.eval_expr(def_expr);
|
return self.eval_expr(def_expr, scope);
|
||||||
} else if let &Val::Boolean(b) = v.deref() {
|
} else if let &Val::Boolean(b) = v.deref() {
|
||||||
for &(ref fname, ref val_expr) in fields.iter() {
|
for &(ref fname, ref val_expr) in fields.iter() {
|
||||||
if &fname.fragment == "true" && b {
|
if &fname.fragment == "true" && b {
|
||||||
// Fourth return the result of evaluating that field.
|
// Fourth return the result of evaluating that field.
|
||||||
return self.eval_expr(val_expr);
|
return self.eval_expr(val_expr, scope);
|
||||||
} else if &fname.fragment == "false" && !b {
|
} else if &fname.fragment == "false" && !b {
|
||||||
return self.eval_expr(val_expr);
|
return self.eval_expr(val_expr, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise return the default.
|
// Otherwise return the default.
|
||||||
return self.eval_expr(def_expr);
|
return self.eval_expr(def_expr, scope);
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
@ -1201,8 +1141,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_list_op(&mut self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_list_op(&mut self, def: &ListOpDef, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let maybe_list = self.eval_expr(&def.target)?;
|
let maybe_list = self.eval_expr(&def.target, scope)?;
|
||||||
let l = match maybe_list.as_ref() {
|
let l = match maybe_list.as_ref() {
|
||||||
&Val::List(ref elems) => elems,
|
&Val::List(ref elems) => elems,
|
||||||
other => {
|
other => {
|
||||||
@ -1214,7 +1154,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mac = &def.mac;
|
let mac = &def.mac;
|
||||||
if let &Val::Macro(ref macdef) = self.lookup_selector(&mac.sel)?.as_ref() {
|
if let &Val::Macro(ref macdef) = self.lookup_selector(&mac.sel, scope)?.as_ref() {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for item in l.iter() {
|
for item in l.iter() {
|
||||||
let argvals = vec![item.clone()];
|
let argvals = vec![item.clone()];
|
||||||
@ -1303,19 +1243,19 @@ impl<'a> FileBuilder<'a> {
|
|||||||
|
|
||||||
// Evals a single Expression in the context of a running Builder.
|
// Evals a single Expression in the context of a running Builder.
|
||||||
// It does not mutate the builders collected state at all.
|
// It does not mutate the builders collected state at all.
|
||||||
pub fn eval_expr(&mut self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
pub fn eval_expr(&mut self, expr: &Expression, scope: &Scope) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match expr {
|
match expr {
|
||||||
&Expression::Simple(ref val) => self.eval_value(val),
|
&Expression::Simple(ref val) => self.eval_value(val, scope),
|
||||||
&Expression::Binary(ref def) => self.eval_binary(def),
|
&Expression::Binary(ref def) => self.eval_binary(def, scope),
|
||||||
&Expression::Compare(ref def) => self.eval_compare(def),
|
&Expression::Compare(ref def) => self.eval_compare(def, scope),
|
||||||
&Expression::Copy(ref def) => self.eval_copy(def),
|
&Expression::Copy(ref def) => self.eval_copy(def, scope),
|
||||||
&Expression::Grouped(ref expr) => self.eval_expr(expr),
|
&Expression::Grouped(ref expr) => self.eval_expr(expr, scope),
|
||||||
&Expression::Format(ref def) => self.eval_format(def),
|
&Expression::Format(ref def) => self.eval_format(def, scope),
|
||||||
&Expression::Call(ref def) => self.eval_call(def),
|
&Expression::Call(ref def) => self.eval_call(def, scope),
|
||||||
&Expression::Macro(ref def) => self.eval_macro_def(def),
|
&Expression::Macro(ref def) => self.eval_macro_def(def),
|
||||||
&Expression::Module(ref def) => self.eval_module_def(def),
|
&Expression::Module(ref def) => self.eval_module_def(def, scope),
|
||||||
&Expression::Select(ref def) => self.eval_select(def),
|
&Expression::Select(ref def) => self.eval_select(def, scope),
|
||||||
&Expression::ListOp(ref def) => self.eval_list_op(def),
|
&Expression::ListOp(ref def) => self.eval_list_op(def, scope),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
src/build/scope.rs
Normal file
100
src/build/scope.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use std::clone::Clone;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::Into;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::ast::PositionedItem;
|
||||||
|
use crate::build::ir::Val;
|
||||||
|
|
||||||
|
/// Defines a set of values in a parsed file.
|
||||||
|
pub type ValueMap = HashMap<PositionedItem<String>, Rc<Val>>;
|
||||||
|
|
||||||
|
/// Defines a scope for execution in ucg.
|
||||||
|
///
|
||||||
|
/// Scopes in ucg are defined by the currently executing file and
|
||||||
|
/// the complex data types in that file. (Tuple, List, Modules, and the
|
||||||
|
/// left operands for dot selectors).
|
||||||
|
///
|
||||||
|
/// UCG Scopes do not descend up into their parent scopes so we do not maintain a stack
|
||||||
|
/// for those.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Scope {
|
||||||
|
pub import_stack: Vec<String>,
|
||||||
|
pub env: Rc<Val>,
|
||||||
|
pub curr_val: Option<Rc<Val>>,
|
||||||
|
pub build_output: ValueMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
// Construct a new scope with environment variables.
|
||||||
|
pub fn new(env: Rc<Val>) -> Self {
|
||||||
|
Self {
|
||||||
|
import_stack: Vec::new(),
|
||||||
|
env: env,
|
||||||
|
// CurrVal represents the currently processing value.
|
||||||
|
// (eg: Tuple, List. left side of a dot selection.)
|
||||||
|
curr_val: None,
|
||||||
|
build_output: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawn a child scope based on the current scope but without the current
|
||||||
|
/// val set.
|
||||||
|
pub fn spawn_child(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
import_stack: self.import_stack.clone(),
|
||||||
|
env: self.env.clone(),
|
||||||
|
// Children start with no current val
|
||||||
|
curr_val: None,
|
||||||
|
build_output: self.build_output.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_clean(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
import_stack: self.import_stack.clone(),
|
||||||
|
env: self.env.clone(),
|
||||||
|
// Children start with no current val
|
||||||
|
curr_val: None,
|
||||||
|
build_output: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an import onto the import stack.
|
||||||
|
pub fn push_import<S: Into<String>>(&mut self, path: S) {
|
||||||
|
self.import_stack.push(path.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend_import_stack(&mut self, imports: &Vec<String>) {
|
||||||
|
let mut new_stack = self.import_stack.clone();
|
||||||
|
new_stack.append(imports.clone().as_mut());
|
||||||
|
self.import_stack = new_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the current value for our execution context.
|
||||||
|
pub fn set_curr_val(&mut self, val: Rc<Val>) {
|
||||||
|
self.curr_val = Some(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup a symbol in the current execution context.
|
||||||
|
///
|
||||||
|
/// The lookup rules are simple.
|
||||||
|
///
|
||||||
|
/// * `env` is always an environment variable lookup.
|
||||||
|
/// * `self` is always the current value. This symbol is only
|
||||||
|
/// valid when the current value is a tuple.
|
||||||
|
/// * everything else is looked up in the currently accumulated build output
|
||||||
|
/// for this execution context.
|
||||||
|
pub fn lookup_sym(&self, sym: &PositionedItem<String>) -> Option<Rc<Val>> {
|
||||||
|
if &sym.val == "env" {
|
||||||
|
return Some(self.env.clone());
|
||||||
|
}
|
||||||
|
if &sym.val == "self" {
|
||||||
|
return self.curr_val.clone();
|
||||||
|
}
|
||||||
|
if self.build_output.contains_key(sym) {
|
||||||
|
return Some(self.build_output[sym].clone());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,10 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder) {
|
fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder) {
|
||||||
for tpl in cases.drain(0..) {
|
for tpl in cases.drain(0..) {
|
||||||
assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1));
|
assert_eq!(
|
||||||
|
b.eval_expr(&tpl.0, &b.scope.spawn_child()).unwrap(),
|
||||||
|
Rc::new(tpl.1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,14 +136,15 @@ fn test_eval_simple_lookup_error() {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
||||||
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("var1".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("var1".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Int(1)));
|
.or_insert(Rc::new(Val::Int(1)));
|
||||||
let expr = Expression::Simple(Value::Symbol(value_node!(
|
let expr = Expression::Simple(Value::Symbol(value_node!(
|
||||||
"var".to_string(),
|
"var".to_string(),
|
||||||
Position::new(1, 1, 1)
|
Position::new(1, 1, 1)
|
||||||
)));
|
)));
|
||||||
assert!(b.eval_expr(&expr).is_err());
|
assert!(b.eval_expr(&expr, &b.scope.spawn_child()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include nested for each.
|
// Include nested for each.
|
||||||
@ -172,7 +176,8 @@ fn test_expr_copy_not_a_tuple() {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
||||||
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Int(1)));
|
.or_insert(Rc::new(Val::Int(1)));
|
||||||
test_expr_to_val(
|
test_expr_to_val(
|
||||||
@ -197,7 +202,8 @@ fn test_expr_copy_field_type_error() {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
||||||
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Tuple(vec![(
|
.or_insert(Rc::new(Val::Tuple(vec![(
|
||||||
value_node!("fld1".to_string(), Position::new(1, 0, 0)),
|
value_node!("fld1".to_string(), Position::new(1, 0, 0)),
|
||||||
@ -234,10 +240,12 @@ fn test_macro_hermetic() {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
||||||
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("arg1".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("arg1".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Str("bar".to_string())));
|
.or_insert(Rc::new(Val::Str("bar".to_string())));
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("tstmac".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("tstmac".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Macro(MacroDef {
|
.or_insert(Rc::new(Val::Macro(MacroDef {
|
||||||
argdefs: vec![value_node!("arg2".to_string(), Position::new(1, 0, 0))],
|
argdefs: vec![value_node!("arg2".to_string(), Position::new(1, 0, 0))],
|
||||||
@ -278,7 +286,8 @@ fn test_select_expr_not_a_string() {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
||||||
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
|
||||||
b.build_output
|
b.scope
|
||||||
|
.build_output
|
||||||
.entry(value_node!("foo".to_string(), Position::new(1, 0, 0)))
|
.entry(value_node!("foo".to_string(), Position::new(1, 0, 0)))
|
||||||
.or_insert(Rc::new(Val::Int(4)));
|
.or_insert(Rc::new(Val::Int(4)));
|
||||||
test_expr_to_val(
|
test_expr_to_val(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user