2017-07-11 20:29:54 -05:00
|
|
|
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
|
|
//
|
|
|
|
// 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.
|
2018-02-07 19:49:13 -06:00
|
|
|
|
|
|
|
//! The build stage of the ucg compiler.
|
2018-08-13 23:11:35 -05:00
|
|
|
use std::cell::RefCell;
|
2018-05-14 21:34:38 -05:00
|
|
|
use std::collections::hash_map::Entry;
|
2018-12-28 19:02:37 -06:00
|
|
|
use std::collections::HashMap;
|
2018-03-11 20:39:50 -05:00
|
|
|
use std::env;
|
2017-06-08 22:15:48 -05:00
|
|
|
use std::error::Error;
|
2018-05-14 21:34:38 -05:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Read;
|
2017-07-19 18:42:31 -05:00
|
|
|
use std::ops::Deref;
|
2018-05-28 13:18:50 -05:00
|
|
|
use std::path::PathBuf;
|
2017-07-19 18:42:31 -05:00
|
|
|
use std::rc::Rc;
|
2018-05-28 13:18:50 -05:00
|
|
|
use std::string::ToString;
|
2017-06-08 22:15:48 -05:00
|
|
|
|
2019-01-07 19:27:29 -06:00
|
|
|
use regex;
|
2018-11-06 19:40:56 -06:00
|
|
|
use simple_error;
|
2019-01-18 19:58:57 -06:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2018-11-06 19:40:56 -06:00
|
|
|
|
2018-12-06 12:23:52 -06:00
|
|
|
use crate::ast::*;
|
2019-02-06 20:35:33 -06:00
|
|
|
use crate::build::format::{ExpressionFormatter, FormatRenderer, SimpleFormatter};
|
2018-12-28 21:56:11 -06:00
|
|
|
use crate::build::scope::{find_in_fieldlist, Scope, ValueMap};
|
2019-01-05 14:27:49 -06:00
|
|
|
use crate::convert::ImporterRegistry;
|
2018-12-06 12:23:52 -06:00
|
|
|
use crate::error;
|
|
|
|
use crate::iter::OffsetStrIter;
|
|
|
|
use crate::parse::parse;
|
2017-08-12 14:48:28 -05:00
|
|
|
|
2018-08-13 23:11:35 -05:00
|
|
|
pub mod assets;
|
2019-02-06 20:35:33 -06:00
|
|
|
pub mod format;
|
2018-08-24 19:36:36 -05:00
|
|
|
pub mod ir;
|
2018-12-31 17:13:58 -06:00
|
|
|
pub mod scope;
|
2019-02-06 20:35:33 -06:00
|
|
|
|
2019-01-13 16:56:09 -06:00
|
|
|
mod stdlib;
|
2018-08-24 19:36:36 -05:00
|
|
|
|
|
|
|
pub use self::ir::Val;
|
2018-08-13 23:11:35 -05:00
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
enum ProcessingOpType {
|
|
|
|
Map,
|
|
|
|
Filter,
|
|
|
|
}
|
|
|
|
|
2019-01-24 20:04:40 -06:00
|
|
|
impl FuncDef {
|
|
|
|
/// Expands a ucg function using the given arguments into a new Tuple.
|
2018-02-12 22:48:59 -06:00
|
|
|
pub fn eval(
|
|
|
|
&self,
|
2018-12-31 10:10:19 -06:00
|
|
|
parent_builder: &FileBuilder,
|
2018-02-12 22:48:59 -06:00
|
|
|
mut args: Vec<Rc<Val>>,
|
2019-01-16 19:07:03 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-08-12 14:48:28 -05:00
|
|
|
// Error conditions. If the args don't match the length and types of the argdefs then this is
|
2019-01-24 20:04:40 -06:00
|
|
|
// func call error.
|
2017-08-12 14:48:28 -05:00
|
|
|
if args.len() > self.argdefs.len() {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Func called with too many args",
|
2018-02-12 22:48:59 -06:00
|
|
|
error::ErrorType::BadArgLen,
|
|
|
|
self.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
// If the args don't match the types required by the expressions then that is a TypeFail.
|
2019-01-24 20:04:40 -06:00
|
|
|
// If the expressions reference Symbols not defined in the FuncDef that is also an error.
|
2019-01-14 18:23:39 -06:00
|
|
|
let mut build_output = HashMap::<PositionedItem<String>, Rc<Val>>::new();
|
2017-08-12 14:48:28 -05:00
|
|
|
for (i, arg) in args.drain(0..).enumerate() {
|
2019-01-14 18:23:39 -06:00
|
|
|
build_output
|
|
|
|
.entry(self.argdefs[i].clone())
|
|
|
|
.or_insert(arg.clone());
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
2019-01-23 20:56:59 -06:00
|
|
|
let mut b = parent_builder.clone_builder();
|
2019-01-14 18:23:39 -06:00
|
|
|
if let Some(ref scope) = self.scope {
|
|
|
|
b.scope = scope.spawn_child();
|
|
|
|
}
|
|
|
|
// We clobber anything that used to be in the scope with the arguments.
|
|
|
|
b.merge_build_output(build_output, true);
|
2019-01-16 19:07:03 -06:00
|
|
|
Ok(b.eval_expr(self.fields.as_ref(), &b.scope.spawn_child())?)
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
}
|
2017-08-08 21:02:54 -05:00
|
|
|
|
2018-02-07 19:49:13 -06:00
|
|
|
/// The result of a build.
|
2019-01-05 13:27:51 -06:00
|
|
|
type BuildResult = Result<(), Box<dyn Error>>;
|
2017-06-08 22:15:48 -05:00
|
|
|
|
2018-07-14 22:56:47 -05:00
|
|
|
/// AssertCollector collects the results of assertions in the UCG AST.
|
2018-06-04 21:45:44 -05:00
|
|
|
pub struct AssertCollector {
|
2019-01-13 14:20:08 -06:00
|
|
|
pub counter: i32,
|
2018-06-04 21:45:44 -05:00
|
|
|
pub success: bool,
|
|
|
|
pub summary: String,
|
|
|
|
pub failures: String,
|
|
|
|
}
|
|
|
|
|
2018-11-27 15:03:27 -06:00
|
|
|
/// Builder handles building ucg code for a single file.
|
2018-12-31 10:10:19 -06:00
|
|
|
pub struct FileBuilder<'a> {
|
2019-01-23 20:56:59 -06:00
|
|
|
working_dir: PathBuf,
|
2019-01-13 20:33:38 -06:00
|
|
|
std: Rc<HashMap<String, &'static str>>,
|
2018-12-13 19:03:22 -06:00
|
|
|
import_path: &'a Vec<PathBuf>,
|
2018-06-04 21:45:44 -05:00
|
|
|
validate_mode: bool,
|
2018-08-17 12:59:26 -05:00
|
|
|
pub assert_collector: AssertCollector,
|
2018-12-31 17:13:58 -06:00
|
|
|
scope: Scope,
|
2019-01-05 14:27:49 -06:00
|
|
|
import_registry: ImporterRegistry,
|
2018-08-13 23:11:35 -05:00
|
|
|
// NOTE(jwall): We use interior mutability here because we need
|
|
|
|
// our asset cache to be shared by multiple different sub-builders.
|
|
|
|
// We use Rc to handle the reference counting for us and we use
|
|
|
|
// RefCell to give us interior mutability. This sacrifices our
|
|
|
|
// compile time memory safety for runtime checks. However it's
|
|
|
|
// acceptable in this case since I can't figure out a better way to
|
|
|
|
// handle it.
|
2018-11-27 15:03:27 -06:00
|
|
|
// The assets are other parsed files from import statements. They
|
|
|
|
// are keyed by the canonicalized import path. This acts as a cache
|
|
|
|
// so multiple imports of the same file don't have to be parsed
|
|
|
|
// multiple times.
|
2018-08-13 23:11:35 -05:00
|
|
|
assets: Rc<RefCell<assets::Cache>>,
|
2018-11-23 12:50:47 -06:00
|
|
|
pub is_module: bool,
|
2017-11-15 22:41:55 -06:00
|
|
|
pub last: Option<Rc<Val>>,
|
2018-08-15 18:22:05 -05:00
|
|
|
pub out_lock: Option<(String, Rc<Val>)>,
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! eval_binary_expr {
|
2018-05-14 21:34:38 -05:00
|
|
|
($case:pat, $pos:ident, $rside:ident, $result:expr, $msg:expr) => {
|
2017-06-10 11:09:24 -05:00
|
|
|
match $rside.as_ref() {
|
|
|
|
$case => {
|
2017-12-09 10:02:45 -06:00
|
|
|
return Ok(Rc::new($result));
|
2018-04-16 20:04:17 -05:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
val => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected {} but got ({})", $msg, val),
|
2018-04-16 20:04:17 -05:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
$pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
2018-04-16 20:04:17 -05:00
|
|
|
};
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
|
2019-01-13 16:56:09 -06:00
|
|
|
// TODO(jwall): Use the builder patter here. Just like AstWalker.
|
2018-12-31 10:10:19 -06:00
|
|
|
impl<'a> FileBuilder<'a> {
|
2018-11-26 21:36:50 -06:00
|
|
|
/// Constructs a new Builder.
|
2018-12-13 19:03:22 -06:00
|
|
|
pub fn new<P: Into<PathBuf>>(
|
2019-01-23 20:56:59 -06:00
|
|
|
working_dir: P,
|
2018-12-13 19:03:22 -06:00
|
|
|
import_paths: &'a Vec<PathBuf>,
|
|
|
|
cache: Rc<RefCell<assets::Cache>>,
|
|
|
|
) -> Self {
|
2018-12-31 17:13:58 -06:00
|
|
|
let env_vars: Vec<(String, String)> = env::vars().collect();
|
|
|
|
let scope = scope::Scope::new(Rc::new(Val::Env(env_vars)));
|
2019-01-23 20:56:59 -06:00
|
|
|
Self::new_with_scope(working_dir, import_paths, cache, scope)
|
2018-11-26 21:36:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructs a new Builder with a provided scope.
|
|
|
|
pub fn new_with_scope<P: Into<PathBuf>>(
|
2019-01-23 20:56:59 -06:00
|
|
|
working_dir: P,
|
2018-12-13 19:03:22 -06:00
|
|
|
import_paths: &'a Vec<PathBuf>,
|
2018-11-26 21:36:50 -06:00
|
|
|
cache: Rc<RefCell<assets::Cache>>,
|
2018-12-31 17:13:58 -06:00
|
|
|
scope: Scope,
|
2018-11-26 21:36:50 -06:00
|
|
|
) -> Self {
|
2018-12-31 10:10:19 -06:00
|
|
|
FileBuilder {
|
2018-11-28 21:12:09 -06:00
|
|
|
// Our import stack is initialized with ourself.
|
2019-01-23 20:56:59 -06:00
|
|
|
working_dir: working_dir.into(),
|
|
|
|
std: Rc::new(stdlib::get_libs()),
|
2018-12-13 19:03:22 -06:00
|
|
|
import_path: import_paths,
|
2018-11-26 21:36:50 -06:00
|
|
|
validate_mode: false,
|
|
|
|
assert_collector: AssertCollector {
|
2019-01-13 14:20:08 -06:00
|
|
|
counter: 0,
|
2018-11-26 21:36:50 -06:00
|
|
|
success: true,
|
|
|
|
summary: String::new(),
|
|
|
|
failures: String::new(),
|
|
|
|
},
|
2018-12-31 17:13:58 -06:00
|
|
|
scope: scope,
|
2019-01-05 14:27:49 -06:00
|
|
|
import_registry: ImporterRegistry::make_registry(),
|
2018-11-26 21:36:50 -06:00
|
|
|
assets: cache,
|
|
|
|
out_lock: None,
|
|
|
|
is_module: false,
|
|
|
|
last: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 20:56:59 -06:00
|
|
|
pub fn clone_builder(&self) -> Self {
|
2018-12-31 10:10:19 -06:00
|
|
|
FileBuilder {
|
2019-01-23 20:56:59 -06:00
|
|
|
working_dir: self.working_dir.clone(),
|
2019-01-13 20:33:38 -06:00
|
|
|
std: self.std.clone(),
|
2018-12-13 19:03:22 -06:00
|
|
|
import_path: self.import_path,
|
2018-12-13 18:29:41 -06:00
|
|
|
validate_mode: false,
|
|
|
|
assert_collector: AssertCollector {
|
2019-01-13 14:20:08 -06:00
|
|
|
counter: 0,
|
2018-12-13 18:29:41 -06:00
|
|
|
success: true,
|
|
|
|
summary: String::new(),
|
|
|
|
failures: String::new(),
|
|
|
|
},
|
|
|
|
assets: self.assets.clone(),
|
2019-01-05 14:27:49 -06:00
|
|
|
// This is admittedly a little wasteful but we can live with it for now.
|
|
|
|
import_registry: ImporterRegistry::make_registry(),
|
2018-12-31 17:13:58 -06:00
|
|
|
scope: self.scope.spawn_clean(),
|
2018-12-13 18:29:41 -06:00
|
|
|
out_lock: None,
|
|
|
|
is_module: false,
|
|
|
|
last: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO(jwall): With builder pattern
|
2018-12-31 17:13:58 -06:00
|
|
|
pub fn set_build_output(&mut self, scope: ValueMap) {
|
|
|
|
self.scope.build_output = scope;
|
2018-12-13 18:29:41 -06:00
|
|
|
}
|
|
|
|
|
2019-01-23 20:56:59 -06:00
|
|
|
/// Builds a ucg file at the named path.
|
|
|
|
pub fn build<P: Into<PathBuf>>(&mut self, file: P) -> BuildResult {
|
|
|
|
let file = file.into();
|
|
|
|
self.working_dir = file.parent().unwrap().to_path_buf();
|
|
|
|
let mut f = File::open(&file)?;
|
|
|
|
let mut s = String::new();
|
|
|
|
f.read_to_string(&mut s)?;
|
|
|
|
let input = OffsetStrIter::new(&s).with_src_file(file.clone());
|
|
|
|
let eval_result = self.eval_input(input);
|
|
|
|
match eval_result {
|
|
|
|
Ok(v) => {
|
|
|
|
self.last = Some(v);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
let err = simple_error::SimpleError::new(
|
|
|
|
format!(
|
|
|
|
"Error building file: {}\n{}",
|
|
|
|
file.to_string_lossy(),
|
|
|
|
e.as_ref()
|
|
|
|
)
|
|
|
|
.as_ref(),
|
|
|
|
);
|
|
|
|
Err(Box::new(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 18:23:39 -06:00
|
|
|
pub fn merge_build_output(&mut self, scope: ValueMap, clobber: bool) {
|
|
|
|
for (name, value) in scope.iter() {
|
|
|
|
if !clobber && !self.scope.build_output.contains_key(name) {
|
|
|
|
self.scope.build_output.insert(name.clone(), value.clone());
|
|
|
|
} else {
|
|
|
|
self.scope.build_output.insert(name.clone(), value.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:36:50 -06:00
|
|
|
pub fn set_strict(&mut self, to: bool) {
|
2019-01-31 19:02:50 -06:00
|
|
|
self.scope.strict = to;
|
2018-11-26 21:36:50 -06:00
|
|
|
}
|
|
|
|
|
2018-12-31 17:13:58 -06:00
|
|
|
fn eval_tuple(
|
2019-01-06 20:56:08 -06:00
|
|
|
&self,
|
2018-12-31 17:13:58 -06:00
|
|
|
fields: &Vec<(Token, Expression)>,
|
|
|
|
scope: &Scope,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-02-19 16:36:19 -06:00
|
|
|
let mut new_fields = Vec::<(String, Rc<Val>)>::new();
|
2017-12-03 18:24:26 -06:00
|
|
|
for &(ref name, ref expr) in fields.iter() {
|
2018-12-31 17:13:58 -06:00
|
|
|
let val = self.eval_expr(expr, scope)?;
|
2019-02-19 16:36:19 -06:00
|
|
|
new_fields.push((name.fragment.clone(), val));
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Ok(Rc::new(Val::Tuple(new_fields)))
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_list(&self, def: &ListDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
let mut vals = Vec::new();
|
|
|
|
for expr in def.elems.iter() {
|
2018-12-31 17:13:58 -06:00
|
|
|
vals.push(self.eval_expr(expr, scope)?);
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Ok(Rc::new(Val::List(vals)))
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_value(&self, v: &Value, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-06-10 11:09:24 -05:00
|
|
|
match v {
|
2018-03-12 20:29:31 -05:00
|
|
|
&Value::Empty(_) => Ok(Rc::new(Val::Empty)),
|
2018-03-22 20:09:38 -05:00
|
|
|
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
|
2017-11-05 15:26:52 -06:00
|
|
|
&Value::Int(ref i) => Ok(Rc::new(Val::Int(i.val))),
|
|
|
|
&Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))),
|
2018-06-10 14:13:08 -05:00
|
|
|
&Value::Str(ref s) => Ok(Rc::new(Val::Str(s.val.to_string()))),
|
2019-02-20 20:28:05 -06:00
|
|
|
&Value::Symbol(ref s) => {
|
|
|
|
scope
|
|
|
|
.lookup_sym(&(s.into()), true)
|
|
|
|
.ok_or(Box::new(error::BuildError::with_pos(
|
|
|
|
format!("Unable to find binding {}", s.val,),
|
|
|
|
error::ErrorType::NoSuchSymbol,
|
|
|
|
v.pos().clone(),
|
|
|
|
)))
|
|
|
|
}
|
2018-12-31 17:13:58 -06:00
|
|
|
&Value::List(ref def) => self.eval_list(def, scope),
|
|
|
|
&Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val, scope),
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:49:13 -06:00
|
|
|
/// Returns a Val by name from previously built UCG.
|
2017-11-15 22:41:55 -06:00
|
|
|
pub fn get_out_by_name(&self, name: &str) -> Option<Rc<Val>> {
|
2018-11-05 21:34:12 -06:00
|
|
|
let key = PositionedItem {
|
|
|
|
pos: Position::new(0, 0, 0),
|
2017-11-15 22:41:55 -06:00
|
|
|
val: name.to_string(),
|
|
|
|
};
|
2019-01-06 14:25:17 -06:00
|
|
|
self.scope.lookup_sym(&key, true)
|
2017-11-15 22:41:55 -06:00
|
|
|
}
|
|
|
|
|
2018-06-04 21:45:44 -05:00
|
|
|
/// Puts the builder in validation mode.
|
|
|
|
///
|
|
|
|
/// Among other things this means that assertions will be evaluated and their results
|
|
|
|
/// will be saved in a report for later output.
|
|
|
|
pub fn enable_validate_mode(&mut self) {
|
|
|
|
self.validate_mode = true;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:49:13 -06:00
|
|
|
/// Builds a list of parsed UCG Statements.
|
2018-11-28 20:15:04 -06:00
|
|
|
pub fn eval_stmts(&mut self, ast: &Vec<Statement>) -> BuildResult {
|
2017-11-05 15:26:52 -06:00
|
|
|
for stmt in ast.iter() {
|
2018-12-06 12:46:47 -06:00
|
|
|
self.eval_stmt(stmt)?;
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-01-05 13:27:51 -06:00
|
|
|
fn eval_input(&mut self, input: OffsetStrIter) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-11-05 21:34:12 -06:00
|
|
|
match parse(input.clone()) {
|
2018-02-02 15:27:33 -06:00
|
|
|
Ok(stmts) => {
|
2018-06-10 13:51:19 -05:00
|
|
|
//panic!("Successfully parsed {}", input);
|
2018-04-16 20:04:17 -05:00
|
|
|
let mut out: Option<Rc<Val>> = None;
|
2017-11-05 15:26:52 -06:00
|
|
|
for stmt in stmts.iter() {
|
2018-12-06 12:46:47 -06:00
|
|
|
out = Some(self.eval_stmt(stmt)?);
|
2018-04-16 20:04:17 -05:00
|
|
|
}
|
|
|
|
match out {
|
|
|
|
None => return Ok(Rc::new(Val::Empty)),
|
|
|
|
Some(val) => Ok(val),
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2019-02-20 20:39:28 -06:00
|
|
|
// TODO(jwall): We can probably use actual errors now?
|
|
|
|
Err(err) => {
|
|
|
|
let cause = Box::new(simple_error::SimpleError::new(err));
|
|
|
|
Err(error::BuildError::with_pos(
|
|
|
|
"Unable to parse input",
|
|
|
|
error::ErrorType::ParseError,
|
|
|
|
(&input).into(),
|
|
|
|
)
|
|
|
|
.wrap_cause(cause)
|
|
|
|
.to_boxed())
|
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO Non file builder specific.
|
2018-08-30 19:20:17 -05:00
|
|
|
/// Evaluate an input string as UCG.
|
2019-01-05 13:27:51 -06:00
|
|
|
pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-11-06 19:40:56 -06:00
|
|
|
self.eval_input(OffsetStrIter::new(input))
|
2018-08-30 19:20:17 -05:00
|
|
|
}
|
|
|
|
|
2018-11-26 20:22:37 -06:00
|
|
|
fn check_reserved_word(name: &str) -> bool {
|
|
|
|
match name {
|
2019-01-24 20:04:40 -06:00
|
|
|
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "func"
|
|
|
|
| "module" | "env" | "map" | "filter" | "NULL" | "out" | "in" | "is" | "not" => true,
|
2018-11-26 20:22:37 -06:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-28 21:12:09 -06:00
|
|
|
fn detect_import_cycle(&self, path: &str) -> bool {
|
2018-12-31 17:13:58 -06:00
|
|
|
self.scope
|
|
|
|
.import_stack
|
|
|
|
.iter()
|
|
|
|
.find(|p| *p == path)
|
|
|
|
.is_some()
|
2018-11-28 21:12:09 -06:00
|
|
|
}
|
|
|
|
|
2019-01-04 10:01:49 -06:00
|
|
|
fn find_file<P: Into<PathBuf>>(
|
2019-01-05 14:27:49 -06:00
|
|
|
&self,
|
2019-01-04 10:01:49 -06:00
|
|
|
path: P,
|
|
|
|
use_import_path: bool,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<PathBuf, Box<dyn Error>> {
|
2018-12-13 18:29:41 -06:00
|
|
|
// Try a relative path first.
|
2019-01-04 10:01:49 -06:00
|
|
|
let path = path.into();
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO(jwall): Change this to take a root directory.
|
|
|
|
let mut normalized = self.working_dir.clone();
|
2019-01-04 10:01:49 -06:00
|
|
|
if path.is_relative() {
|
|
|
|
normalized.push(&path);
|
2018-12-13 18:29:41 -06:00
|
|
|
// First see if the normalized file exists or not.
|
2019-01-04 10:01:49 -06:00
|
|
|
if !normalized.exists() && use_import_path {
|
2018-12-13 18:29:41 -06:00
|
|
|
// If it does not then look for it in the list of import_paths
|
|
|
|
for mut p in self.import_path.iter().cloned() {
|
2019-01-04 10:01:49 -06:00
|
|
|
p.push(&path);
|
2018-12-13 18:29:41 -06:00
|
|
|
if p.exists() {
|
|
|
|
normalized = p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-23 12:50:47 -06:00
|
|
|
} else {
|
2019-01-04 10:01:49 -06:00
|
|
|
normalized = path;
|
2018-11-23 12:50:47 -06:00
|
|
|
}
|
2019-01-04 10:01:49 -06:00
|
|
|
Ok(normalized.canonicalize()?)
|
|
|
|
}
|
|
|
|
|
2019-01-13 09:37:44 -06:00
|
|
|
fn eval_import(&self, def: &ImportDef) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-13 20:33:38 -06:00
|
|
|
// Look for a std file first.
|
|
|
|
if def.path.fragment.starts_with("std/") {
|
|
|
|
if self.std.contains_key(&def.path.fragment) {
|
|
|
|
// Okay then this is a stdlib and it's special.
|
|
|
|
// Introduce a scope so the above borrow is dropped before we modify
|
|
|
|
// the cache below.
|
|
|
|
// Only parse the file once on import.
|
|
|
|
let path = PathBuf::from(&def.path.fragment);
|
|
|
|
let maybe_asset = self.assets.borrow().get(&path)?;
|
|
|
|
let result = match maybe_asset {
|
|
|
|
Some(v) => v.clone(),
|
|
|
|
None => {
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO(jwall): This does not need to be a FileBuilder specifically
|
|
|
|
let mut b = self.clone_builder();
|
2019-01-13 20:33:38 -06:00
|
|
|
b.eval_string(self.std.get(&def.path.fragment).unwrap())?;
|
|
|
|
b.get_outputs_as_val()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut mut_assets_cache = self.assets.borrow_mut();
|
|
|
|
mut_assets_cache.stash(path, result.clone())?;
|
|
|
|
return Ok(result);
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-13 20:33:38 -06:00
|
|
|
format!("No such import {} in the std library.", def.path.fragment),
|
|
|
|
error::ErrorType::Unsupported,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-13 20:33:38 -06:00
|
|
|
}
|
|
|
|
}
|
2019-01-04 10:01:49 -06:00
|
|
|
// Try a relative path first.
|
|
|
|
let normalized = self.find_file(&def.path.fragment, true)?;
|
2018-11-28 21:12:09 -06:00
|
|
|
if self.detect_import_cycle(normalized.to_string_lossy().as_ref()) {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-11-28 21:12:09 -06:00
|
|
|
format!(
|
|
|
|
"Import Cycle Detected!!!! {} is already in import stack: {:?}",
|
|
|
|
normalized.to_string_lossy(),
|
2018-12-31 17:13:58 -06:00
|
|
|
self.scope.import_stack,
|
2018-11-28 21:12:09 -06:00
|
|
|
),
|
|
|
|
error::ErrorType::Unsupported,
|
2019-01-13 09:37:44 -06:00
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-11-28 21:12:09 -06:00
|
|
|
}
|
2018-11-23 12:50:47 -06:00
|
|
|
// Introduce a scope so the above borrow is dropped before we modify
|
|
|
|
// the cache below.
|
2018-08-13 23:11:35 -05:00
|
|
|
// Only parse the file once on import.
|
2018-12-06 12:46:47 -06:00
|
|
|
let maybe_asset = self.assets.borrow().get(&normalized)?;
|
2018-11-23 12:50:47 -06:00
|
|
|
let result = match maybe_asset {
|
2018-08-14 16:10:25 -05:00
|
|
|
Some(v) => v.clone(),
|
|
|
|
None => {
|
2019-01-23 20:56:59 -06:00
|
|
|
let mut b = self.clone_builder();
|
|
|
|
b.build(&normalized)?;
|
2018-11-23 12:50:47 -06:00
|
|
|
b.get_outputs_as_val()
|
2018-08-14 16:10:25 -05:00
|
|
|
}
|
|
|
|
};
|
2018-11-23 12:50:47 -06:00
|
|
|
let mut mut_assets_cache = self.assets.borrow_mut();
|
2018-12-06 12:46:47 -06:00
|
|
|
mut_assets_cache.stash(normalized.clone(), result.clone())?;
|
2018-08-13 23:11:35 -05:00
|
|
|
return Ok(result);
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-05 13:27:51 -06:00
|
|
|
fn eval_let(&mut self, def: &LetDef) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-12-31 17:13:58 -06:00
|
|
|
let child_scope = self.scope.clone();
|
|
|
|
let val = self.eval_expr(&def.value, &child_scope)?;
|
2017-12-03 18:24:26 -06:00
|
|
|
let name = &def.name;
|
2018-11-26 20:22:37 -06:00
|
|
|
if Self::check_reserved_word(&name.fragment) {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-11-26 20:22:37 -06:00
|
|
|
format!("Let {} binding collides with reserved word", name.fragment),
|
|
|
|
error::ErrorType::ReservedWordError,
|
|
|
|
name.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-11-26 20:22:37 -06:00
|
|
|
}
|
2018-12-31 17:13:58 -06:00
|
|
|
match self.scope.build_output.entry(name.into()) {
|
2017-12-03 18:24:26 -06:00
|
|
|
Entry::Occupied(e) => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!(
|
2018-08-13 23:11:35 -05:00
|
|
|
"Binding \
|
2018-02-12 22:48:59 -06:00
|
|
|
for {:?} already \
|
2018-11-28 20:25:03 -06:00
|
|
|
exists",
|
2018-05-29 20:49:33 -05:00
|
|
|
e.key(),
|
2018-02-12 22:48:59 -06:00
|
|
|
),
|
|
|
|
error::ErrorType::DuplicateBinding,
|
|
|
|
def.name.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Entry::Vacant(e) => {
|
2018-04-16 20:04:17 -05:00
|
|
|
e.insert(val.clone());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
2018-04-16 20:04:17 -05:00
|
|
|
Ok(val)
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-05 13:27:51 -06:00
|
|
|
fn eval_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-12-31 17:13:58 -06:00
|
|
|
let child_scope = self.scope.clone();
|
2017-08-12 14:48:28 -05:00
|
|
|
match stmt {
|
2019-02-06 20:44:15 -06:00
|
|
|
&Statement::Assert(ref expr) => self.eval_assert(&expr, &child_scope),
|
2018-11-28 20:15:04 -06:00
|
|
|
&Statement::Let(ref def) => self.eval_let(def),
|
2018-12-31 17:13:58 -06:00
|
|
|
&Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope),
|
2018-08-15 18:22:05 -05:00
|
|
|
// Only one output can be used per file. Right now we enforce this by
|
|
|
|
// having a single builder per file.
|
2019-02-06 21:06:05 -06:00
|
|
|
&Statement::Output(ref pos, ref typ, ref expr) => {
|
2018-08-13 20:37:58 -05:00
|
|
|
if let None = self.out_lock {
|
2018-12-31 17:13:58 -06:00
|
|
|
let val = self.eval_expr(expr, &child_scope)?;
|
2018-08-13 20:37:58 -05:00
|
|
|
self.out_lock = Some((typ.fragment.to_string(), val.clone()));
|
|
|
|
Ok(val)
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2018-08-13 20:37:58 -05:00
|
|
|
format!("You can only have one output per file."),
|
2019-02-06 21:06:05 -06:00
|
|
|
error::ErrorType::Unsupported,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2018-08-13 20:37:58 -05:00
|
|
|
}
|
|
|
|
}
|
2018-04-16 20:04:17 -05:00
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
|
2018-02-12 22:48:59 -06:00
|
|
|
fn add_vals(
|
|
|
|
&self,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos: &Position,
|
|
|
|
rpos: &Position,
|
2018-02-12 22:48:59 -06:00
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
match *left {
|
|
|
|
Val::Int(i) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i + ii), "Integer")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Val::Float(f) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f + ff), "Float")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
2018-06-10 14:13:08 -05:00
|
|
|
Val::Str(ref s) => match right.as_ref() {
|
|
|
|
&Val::Str(ref ss) => {
|
2019-01-18 18:44:29 -06:00
|
|
|
return Ok(Rc::new(Val::Str([s.to_string(), ss.clone()].concat())));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2018-02-12 22:48:59 -06:00
|
|
|
val => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!(
|
|
|
|
"Expected \
|
|
|
|
String \
|
|
|
|
but got \
|
|
|
|
{:?}",
|
|
|
|
val
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
2019-02-02 16:43:29 -06:00
|
|
|
rpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-07-19 18:42:31 -05:00
|
|
|
}
|
2018-02-12 22:48:59 -06:00
|
|
|
},
|
|
|
|
Val::List(ref l) => match right.as_ref() {
|
|
|
|
&Val::List(ref r) => {
|
|
|
|
let mut new_vec = Vec::new();
|
|
|
|
new_vec.extend(l.iter().cloned());
|
|
|
|
new_vec.extend(r.iter().cloned());
|
|
|
|
return Ok(Rc::new(Val::List(new_vec)));
|
|
|
|
}
|
|
|
|
val => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!(
|
|
|
|
"Expected \
|
|
|
|
List \
|
|
|
|
but got \
|
|
|
|
{:?}",
|
|
|
|
val
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
2019-02-02 16:43:29 -06:00
|
|
|
rpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-02-12 22:48:59 -06:00
|
|
|
}
|
|
|
|
},
|
2017-12-03 18:24:26 -06:00
|
|
|
ref expr => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!("{} does not support the '+' operation", expr.type_name()),
|
|
|
|
error::ErrorType::Unsupported,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 22:48:59 -06:00
|
|
|
fn subtract_vals(
|
|
|
|
&self,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos: &Position,
|
|
|
|
rpos: &Position,
|
2018-02-12 22:48:59 -06:00
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
match *left {
|
|
|
|
Val::Int(i) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i - ii), "Integer")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Val::Float(f) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f - ff), "Float")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
ref expr => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!("{} does not support the '-' operation", expr.type_name()),
|
|
|
|
error::ErrorType::Unsupported,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 22:48:59 -06:00
|
|
|
fn multiply_vals(
|
|
|
|
&self,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos: &Position,
|
|
|
|
rpos: &Position,
|
2018-02-12 22:48:59 -06:00
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
match *left {
|
|
|
|
Val::Int(i) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i * ii), "Integer")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Val::Float(f) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f * ff), "Float")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
ref expr => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!("{} does not support the '*' operation", expr.type_name()),
|
|
|
|
error::ErrorType::Unsupported,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 21:09:42 -06:00
|
|
|
fn mod_vals(
|
|
|
|
&self,
|
|
|
|
lpos: &Position,
|
|
|
|
rpos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
match *left {
|
|
|
|
Val::Int(i) => {
|
|
|
|
eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i % ii), "Integer")
|
|
|
|
}
|
|
|
|
Val::Float(f) => {
|
|
|
|
eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f % ff), "Float")
|
|
|
|
}
|
|
|
|
ref expr => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-18 21:09:42 -06:00
|
|
|
format!(
|
|
|
|
"{} does not support the 'modulus' operation",
|
|
|
|
expr.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::Unsupported,
|
|
|
|
lpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-02-18 21:09:42 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 22:48:59 -06:00
|
|
|
fn divide_vals(
|
|
|
|
&self,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos: &Position,
|
|
|
|
rpos: &Position,
|
2018-02-12 22:48:59 -06:00
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
match *left {
|
|
|
|
Val::Int(i) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii), rpos, right, Val::Int(i / ii), "Integer")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
Val::Float(f) => {
|
2019-02-02 16:43:29 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff), rpos, right, Val::Float(f / ff), "Float")
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
ref expr => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!("{} does not support the '*' operation", expr.type_name()),
|
|
|
|
error::ErrorType::Unsupported,
|
2019-02-02 16:43:29 -06:00
|
|
|
lpos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-24 08:58:16 -05:00
|
|
|
fn do_deep_equal(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-02-19 17:16:36 -06:00
|
|
|
match left.equal(right.as_ref()) {
|
|
|
|
Ok(b) => Ok(Rc::new(Val::Boolean(b))),
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(e) => Err(error::BuildError::with_pos(e.msg, e.err_type, pos.clone()).to_boxed()),
|
2019-02-19 17:16:36 -06:00
|
|
|
}
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_not_deep_equal(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-02-19 17:16:36 -06:00
|
|
|
match left.equal(right.as_ref()) {
|
|
|
|
Ok(b) => Ok(Rc::new(Val::Boolean(!b))),
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(e) => Err(error::BuildError::with_pos(e.msg, e.err_type, pos.clone()).to_boxed()),
|
2019-02-19 17:16:36 -06:00
|
|
|
}
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
2019-01-05 13:27:51 -06:00
|
|
|
fn do_gt(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-03-24 08:58:16 -05:00
|
|
|
// first ensure that left and right are numeric vals of the same type.
|
|
|
|
if let &Val::Int(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Int(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l > r)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let &Val::Float(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Float(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l > r)));
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected {} but got ({})", left.type_name(), right,),
|
2018-03-24 08:58:16 -05:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
2019-01-05 13:27:51 -06:00
|
|
|
fn do_lt(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-03-24 08:58:16 -05:00
|
|
|
// first ensure that left and right are numeric vals of the same type.
|
|
|
|
if let &Val::Int(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Int(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l < r)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let &Val::Float(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Float(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l < r)));
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected {} but got ({})", left.type_name(), right,),
|
2018-03-24 08:58:16 -05:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_ltequal(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-03-24 08:58:16 -05:00
|
|
|
if let &Val::Int(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Int(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l <= r)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let &Val::Float(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Float(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l <= r)));
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected {} but got ({})", left.type_name(), right),
|
2018-03-24 08:58:16 -05:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_gtequal(
|
|
|
|
&self,
|
|
|
|
pos: &Position,
|
|
|
|
left: Rc<Val>,
|
|
|
|
right: Rc<Val>,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-03-24 08:58:16 -05:00
|
|
|
if let &Val::Int(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Int(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l >= r)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let &Val::Float(ref l) = left.as_ref() {
|
|
|
|
if let &Val::Float(ref r) = right.as_ref() {
|
|
|
|
return Ok(Rc::new(Val::Boolean(l >= r)));
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected {} but got ({})", left.type_name(), right,),
|
2018-03-24 08:58:16 -05:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2018-03-24 08:58:16 -05:00
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn do_dot_lookup(&self, right: &Expression, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-06 14:25:17 -06:00
|
|
|
let pos = right.pos().clone();
|
2019-01-20 16:22:32 -06:00
|
|
|
let scope = scope.clone().use_curr_val();
|
2018-12-28 21:56:11 -06:00
|
|
|
match right {
|
2019-01-20 16:22:32 -06:00
|
|
|
Expression::Copy(_) => return self.eval_expr(right, &scope),
|
|
|
|
Expression::Call(_) => return self.eval_expr(right, &scope),
|
2018-12-28 21:56:11 -06:00
|
|
|
Expression::Simple(Value::Symbol(ref s)) => {
|
2019-01-06 14:25:17 -06:00
|
|
|
scope
|
|
|
|
.lookup_sym(s, true)
|
2019-02-20 20:28:05 -06:00
|
|
|
.ok_or(Box::new(error::BuildError::with_pos(
|
2019-01-06 14:25:17 -06:00
|
|
|
format!("Unable to find binding {}", s.val,),
|
|
|
|
error::ErrorType::NoSuchSymbol,
|
|
|
|
pos,
|
|
|
|
)))
|
2018-12-28 21:56:11 -06:00
|
|
|
}
|
|
|
|
Expression::Simple(Value::Str(ref s)) => {
|
2019-01-06 14:25:17 -06:00
|
|
|
scope
|
|
|
|
.lookup_sym(s, false)
|
2019-02-20 20:28:05 -06:00
|
|
|
.ok_or(Box::new(error::BuildError::with_pos(
|
2019-01-06 14:25:17 -06:00
|
|
|
format!("Unable to find binding {}", s.val,),
|
|
|
|
error::ErrorType::NoSuchSymbol,
|
|
|
|
pos,
|
|
|
|
)))
|
2018-12-28 21:56:11 -06:00
|
|
|
}
|
|
|
|
Expression::Simple(Value::Int(ref i)) => {
|
|
|
|
scope.lookup_idx(right.pos(), &Val::Int(i.val))
|
|
|
|
}
|
2019-01-10 18:38:14 -06:00
|
|
|
_ => {
|
2019-01-20 16:22:32 -06:00
|
|
|
let val = self.eval_expr(right, &scope)?;
|
2019-01-10 18:38:14 -06:00
|
|
|
match val.as_ref() {
|
|
|
|
Val::Int(i) => scope.lookup_idx(right.pos(), &Val::Int(*i)),
|
|
|
|
Val::Str(ref s) => scope
|
|
|
|
.lookup_sym(&PositionedItem::new(s.clone(), pos.clone()), false)
|
2019-02-20 20:28:05 -06:00
|
|
|
.ok_or(Box::new(error::BuildError::with_pos(
|
2019-01-10 18:38:14 -06:00
|
|
|
format!("Unable to find binding {}", s,),
|
|
|
|
error::ErrorType::NoSuchSymbol,
|
|
|
|
pos,
|
|
|
|
))),
|
2019-02-20 20:45:33 -06:00
|
|
|
_ => Err(error::BuildError::with_pos(
|
2019-01-10 18:38:14 -06:00
|
|
|
format!("Invalid selector lookup {}", val.type_name(),),
|
|
|
|
error::ErrorType::NoSuchSymbol,
|
|
|
|
pos,
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed()),
|
2019-01-10 18:38:14 -06:00
|
|
|
}
|
|
|
|
}
|
2018-12-28 19:02:37 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-17 19:20:01 -06:00
|
|
|
fn do_bool_operator(
|
|
|
|
&self,
|
|
|
|
kind: &BinaryExprType,
|
|
|
|
left: &Expression,
|
|
|
|
right: &Expression,
|
|
|
|
scope: &Scope,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let left_pos = left.pos();
|
|
|
|
let left = self.eval_expr(left, scope)?;
|
|
|
|
if let Val::Boolean(b) = left.as_ref() {
|
|
|
|
let right_pos = right.pos();
|
|
|
|
let b = *b;
|
|
|
|
if kind == &BinaryExprType::AND {
|
|
|
|
if !b {
|
|
|
|
// short circuit
|
|
|
|
return Ok(Rc::new(Val::Boolean(b)));
|
|
|
|
}
|
|
|
|
let right = self.eval_expr(right, scope)?;
|
|
|
|
if right.is_bool() {
|
|
|
|
return Ok(right);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if b {
|
|
|
|
// short circuit
|
|
|
|
return Ok(Rc::new(Val::Boolean(b)));
|
|
|
|
}
|
|
|
|
let right = self.eval_expr(right, scope)?;
|
|
|
|
if right.is_bool() {
|
|
|
|
return Ok(right);
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-17 19:20:01 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected boolean value for operator but got ({})",
|
2019-01-17 19:20:01 -06:00
|
|
|
left.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
right_pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-17 19:20:01 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-17 19:20:01 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected boolean value for operator but got ({})",
|
2019-01-17 19:20:01 -06:00
|
|
|
left.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
left_pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-17 19:20:01 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 11:42:11 -06:00
|
|
|
fn do_element_check(
|
2019-01-06 20:56:08 -06:00
|
|
|
&self,
|
2019-01-03 11:42:11 -06:00
|
|
|
left: &Expression,
|
|
|
|
right: &Expression,
|
|
|
|
scope: &Scope,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-03 11:42:11 -06:00
|
|
|
// First we evaluate our right hand side so we have a something to search
|
|
|
|
// inside for our left hand expression.
|
2019-02-03 09:58:38 -06:00
|
|
|
let right_pos = right.pos();
|
2019-01-03 11:42:11 -06:00
|
|
|
let right = self.eval_expr(right, scope)?;
|
|
|
|
// presence checks are only valid for tuples and lists.
|
|
|
|
if !(right.is_tuple() || right.is_list()) {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-03 11:42:11 -06:00
|
|
|
format!(
|
|
|
|
"Invalid righthand type for in operator {}",
|
|
|
|
right.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
2019-02-03 09:58:38 -06:00
|
|
|
right_pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-03 11:42:11 -06:00
|
|
|
}
|
|
|
|
if let &Val::List(ref els) = right.as_ref() {
|
|
|
|
let left = self.eval_expr(left, scope)?;
|
|
|
|
for val in els.iter() {
|
2019-02-03 09:58:38 -06:00
|
|
|
if let Ok(b) = self.do_deep_equal(right_pos, left.clone(), val.clone()) {
|
2019-01-03 11:42:11 -06:00
|
|
|
if let &Val::Boolean(b) = b.as_ref() {
|
|
|
|
if b {
|
|
|
|
// We found a match
|
|
|
|
return Ok(Rc::new(Val::Boolean(true)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We didn't find a match anywhere so return false.
|
|
|
|
return Ok(Rc::new(Val::Boolean(false)));
|
|
|
|
} else {
|
|
|
|
// Handle our tuple case since this isn't a list.
|
2019-01-28 21:22:23 -06:00
|
|
|
let child_scope = scope.spawn_child().set_curr_val(right.clone());
|
2019-01-03 11:42:11 -06:00
|
|
|
// Search for the field in our tuple or list.
|
|
|
|
let maybe_val = self.do_dot_lookup(left, &child_scope);
|
|
|
|
// Return the result of the search.
|
|
|
|
return Ok(Rc::new(Val::Boolean(maybe_val.is_ok())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-07 19:27:29 -06:00
|
|
|
fn eval_re_match(
|
|
|
|
&self,
|
|
|
|
left: Rc<Val>,
|
|
|
|
left_pos: &Position,
|
|
|
|
right: Rc<Val>,
|
|
|
|
right_pos: &Position,
|
|
|
|
negate: bool,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let re = if let Val::Str(ref s) = right.as_ref() {
|
|
|
|
regex::Regex::new(s.as_ref())?
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected string for regex but got ({})", right.type_name()),
|
2019-01-07 19:27:29 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
right_pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-07 19:27:29 -06:00
|
|
|
};
|
|
|
|
let tgt = if let Val::Str(ref s) = left.as_ref() {
|
|
|
|
s.as_ref()
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected string but got ({})", left.type_name()),
|
2019-01-07 19:27:29 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
left_pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-07 19:27:29 -06:00
|
|
|
};
|
|
|
|
return if negate {
|
|
|
|
Ok(Rc::new(Val::Boolean(!re.is_match(tgt))))
|
|
|
|
} else {
|
|
|
|
Ok(Rc::new(Val::Boolean(re.is_match(tgt))))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_binary(&self, def: &BinaryOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
let kind = &def.kind;
|
2019-01-03 11:42:11 -06:00
|
|
|
if let &BinaryExprType::IN = kind {
|
2019-01-15 18:51:35 -06:00
|
|
|
// TODO Should we support this operation on strings too?
|
2019-01-03 11:42:11 -06:00
|
|
|
return self.do_element_check(&def.left, &def.right, scope);
|
2019-01-19 10:34:58 -06:00
|
|
|
}
|
|
|
|
if let &BinaryExprType::IS = kind {
|
|
|
|
return self.eval_is_check(def, scope);
|
|
|
|
}
|
2019-01-17 19:20:01 -06:00
|
|
|
match kind {
|
|
|
|
// We special case the boolean operators because we want them to short circuit.
|
|
|
|
&BinaryExprType::AND | &BinaryExprType::OR => {
|
|
|
|
return self.do_bool_operator(kind, &def.left, &def.right, scope);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// noop
|
|
|
|
}
|
|
|
|
}
|
2018-12-31 17:13:58 -06:00
|
|
|
let left = self.eval_expr(&def.left, scope)?;
|
2019-01-28 21:22:23 -06:00
|
|
|
let child_scope = scope.spawn_child().set_curr_val(left.clone());
|
2018-12-28 21:56:11 -06:00
|
|
|
if let &BinaryExprType::DOT = kind {
|
|
|
|
return self.do_dot_lookup(&def.right, &child_scope);
|
|
|
|
};
|
|
|
|
// TODO(jwall): We need to handle call and copy expressions specially.
|
|
|
|
let right = match self.eval_expr(&def.right, scope) {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
};
|
2017-12-03 18:24:26 -06:00
|
|
|
match kind {
|
2018-12-28 19:02:37 -06:00
|
|
|
// Handle math and concatenation operators here
|
2019-02-02 16:43:29 -06:00
|
|
|
&BinaryExprType::Add => self.add_vals(&def.pos, def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::Sub => self.subtract_vals(&def.pos, def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::Mul => self.multiply_vals(&def.pos, def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::Div => self.divide_vals(&def.pos, def.right.pos(), left, right),
|
2019-02-18 21:09:42 -06:00
|
|
|
&BinaryExprType::Mod => self.mod_vals(&def.pos, def.right.pos(), left, right),
|
2018-12-28 19:02:37 -06:00
|
|
|
// Handle Comparison operators here
|
2019-02-19 17:16:36 -06:00
|
|
|
&BinaryExprType::Equal => self.do_deep_equal(&def.right.pos(), left, right),
|
2019-02-03 09:58:38 -06:00
|
|
|
&BinaryExprType::GT => self.do_gt(&def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::LT => self.do_lt(&def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::GTEqual => self.do_gtequal(&def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::LTEqual => self.do_ltequal(&def.right.pos(), left, right),
|
|
|
|
&BinaryExprType::NotEqual => self.do_not_deep_equal(&def.right.pos(), left, right),
|
2019-01-07 19:27:29 -06:00
|
|
|
&BinaryExprType::REMatch => {
|
|
|
|
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), false)
|
|
|
|
}
|
|
|
|
&BinaryExprType::NotREMatch => {
|
|
|
|
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true)
|
|
|
|
}
|
2019-01-17 19:20:01 -06:00
|
|
|
&BinaryExprType::IN
|
2019-01-19 10:34:58 -06:00
|
|
|
| &BinaryExprType::IS
|
2019-01-17 19:20:01 -06:00
|
|
|
| &BinaryExprType::DOT
|
|
|
|
| &BinaryExprType::AND
|
|
|
|
| &BinaryExprType::OR => panic!("Unreachable"),
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 12:50:47 -06:00
|
|
|
fn get_outputs_as_val(&mut self) -> Rc<Val> {
|
2019-02-19 16:36:19 -06:00
|
|
|
let fields: Vec<(String, Rc<Val>)> = self
|
|
|
|
.scope
|
|
|
|
.build_output
|
|
|
|
.drain()
|
|
|
|
.map(|v| (v.0.val, v.1))
|
|
|
|
.collect();
|
2018-11-23 12:50:47 -06:00
|
|
|
Rc::new(Val::Tuple(fields))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn copy_from_base(
|
2019-01-06 20:56:08 -06:00
|
|
|
&self,
|
2019-02-19 16:36:19 -06:00
|
|
|
src_fields: &Vec<(String, Rc<Val>)>,
|
2018-11-23 12:50:47 -06:00
|
|
|
overrides: &Vec<(Token, Expression)>,
|
2018-12-31 17:13:58 -06:00
|
|
|
scope: &Scope,
|
2019-01-05 13:27:51 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-02-19 16:36:19 -06:00
|
|
|
let mut m = HashMap::<String, (i32, Rc<Val>)>::new();
|
2018-11-23 12:50:47 -06:00
|
|
|
// loop through fields and build up a hashmap
|
|
|
|
let mut count = 0;
|
|
|
|
for &(ref key, ref val) in src_fields.iter() {
|
|
|
|
if let Entry::Vacant(v) = m.entry(key.clone()) {
|
|
|
|
v.insert((count, val.clone()));
|
|
|
|
count += 1;
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::new(
|
2018-11-23 12:50:47 -06:00
|
|
|
format!(
|
|
|
|
"Duplicate \
|
|
|
|
field: {} in \
|
|
|
|
tuple",
|
2019-02-19 16:36:19 -06:00
|
|
|
key
|
2018-11-23 12:50:47 -06:00
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-11-23 12:50:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for &(ref key, ref val) in overrides.iter() {
|
2018-12-31 17:13:58 -06:00
|
|
|
let expr_result = self.eval_expr(val, scope)?;
|
2019-02-19 16:36:19 -06:00
|
|
|
match m.entry(key.fragment.clone()) {
|
2018-11-23 12:50:47 -06:00
|
|
|
// brand new field here.
|
|
|
|
Entry::Vacant(v) => {
|
|
|
|
v.insert((count, expr_result));
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
Entry::Occupied(mut v) => {
|
|
|
|
// overriding field here.
|
|
|
|
// Ensure that the new type matches the old type.
|
|
|
|
let src_val = v.get().clone();
|
2018-11-25 13:26:47 -06:00
|
|
|
if src_val.1.type_equal(&expr_result)
|
|
|
|
|| src_val.1.is_empty()
|
|
|
|
|| expr_result.is_empty()
|
|
|
|
{
|
2018-11-23 12:50:47 -06:00
|
|
|
v.insert((src_val.0, expr_result));
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-11-23 12:50:47 -06:00
|
|
|
format!(
|
2019-02-08 19:01:16 -06:00
|
|
|
"Expected type {} for field {} but got ({})",
|
2018-11-23 12:50:47 -06:00
|
|
|
src_val.1.type_name(),
|
|
|
|
key.fragment,
|
|
|
|
expr_result.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
key.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-11-23 12:50:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2019-02-19 16:36:19 -06:00
|
|
|
let mut new_fields: Vec<(String, (i32, Rc<Val>))> = m.drain().collect();
|
2018-11-23 12:50:47 -06:00
|
|
|
// 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.
|
|
|
|
new_fields.sort_by(|a, b| {
|
|
|
|
let ta = a.1.clone();
|
|
|
|
let tb = b.1.clone();
|
|
|
|
ta.0.cmp(&tb.0)
|
|
|
|
});
|
|
|
|
return Ok(Rc::new(Val::Tuple(
|
|
|
|
new_fields
|
|
|
|
.iter()
|
|
|
|
.map(|a| {
|
|
|
|
let first = a.0.clone();
|
|
|
|
let t = a.1.clone();
|
|
|
|
(first, t.1)
|
2018-12-10 21:27:44 -06:00
|
|
|
})
|
|
|
|
.collect(),
|
2018-11-23 12:50:47 -06:00
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
2019-02-21 19:39:30 -06:00
|
|
|
fn eval_module_copy(
|
|
|
|
&self,
|
|
|
|
def: &CopyDef,
|
|
|
|
mod_def: &ModuleDef,
|
|
|
|
scope: &Scope,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
|
|
|
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
|
|
|
|
// 1. First we create a builder.
|
|
|
|
// TODO(jwall): This file should optionally come from the module def itself.
|
|
|
|
let mut b = self.clone_builder();
|
|
|
|
b.is_module = true;
|
|
|
|
// 2. We construct an argument tuple by copying from the defs
|
|
|
|
// argset.
|
|
|
|
// Push our base tuple on the stack so the copy can use
|
|
|
|
// self to reference it.
|
|
|
|
let child_scope = scope.spawn_child().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.
|
|
|
|
let mod_key = PositionedItem::new_with_pos(String::from("mod"), Position::new(0, 0, 0));
|
|
|
|
match b.scope.build_output.entry(mod_key) {
|
|
|
|
Entry::Occupied(e) => {
|
|
|
|
return Err(error::BuildError::with_pos(
|
|
|
|
format!(
|
|
|
|
"Binding \
|
|
|
|
for {:?} already \
|
|
|
|
exists in module",
|
|
|
|
e.key(),
|
|
|
|
),
|
|
|
|
error::ErrorType::DuplicateBinding,
|
|
|
|
mod_def.pos.clone(),
|
|
|
|
)
|
|
|
|
.to_boxed());
|
|
|
|
}
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
e.insert(mod_args.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 4. Evaluate all the statements using the builder.
|
|
|
|
b.eval_stmts(&mod_def.statements)?;
|
|
|
|
if let Some(ref expr) = mod_def.out_expr {
|
|
|
|
// 5. Produce the out expression in the context of the statements
|
|
|
|
// we evaluated previously.
|
|
|
|
return b.eval_expr(expr, &b.scope);
|
|
|
|
} else {
|
|
|
|
// 5. Take all of the bindings in the module and construct a new
|
|
|
|
// tuple using them.
|
|
|
|
return Ok(b.get_outputs_as_val());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(error::BuildError::with_pos(
|
|
|
|
format!(
|
|
|
|
"Weird value stored in our module parameters slot {:?}",
|
|
|
|
mod_def.arg_tuple
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.selector.pos().clone(),
|
|
|
|
)
|
|
|
|
.to_boxed());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_copy(&self, def: &CopyDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-12-28 19:02:37 -06:00
|
|
|
let v = self.eval_value(&def.selector, scope)?;
|
2018-11-16 09:53:19 -06:00
|
|
|
if let &Val::Tuple(ref src_fields) = v.as_ref() {
|
2019-01-28 21:22:23 -06:00
|
|
|
let child_scope = scope.spawn_child().set_curr_val(v.clone());
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.copy_from_base(&src_fields, &def.fields, &child_scope);
|
2018-11-23 12:50:47 -06:00
|
|
|
}
|
|
|
|
if let &Val::Module(ref mod_def) = v.as_ref() {
|
2019-02-21 19:39:30 -06:00
|
|
|
return self.eval_module_copy(def, mod_def, scope);
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-02-08 19:01:16 -06:00
|
|
|
format!("Expected Tuple or Module but got ({})", v),
|
2018-02-12 22:48:59 -06:00
|
|
|
error::ErrorType::TypeFail,
|
2018-12-28 19:02:37 -06:00
|
|
|
def.selector.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_format(&self, def: &FormatDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
let tmpl = &def.template;
|
2019-01-28 21:22:23 -06:00
|
|
|
return match &def.args {
|
|
|
|
FormatArgs::List(ref args) => {
|
|
|
|
let mut vals = Vec::new();
|
|
|
|
for v in args.iter() {
|
|
|
|
let rcv = self.eval_expr(v, scope)?;
|
|
|
|
vals.push(rcv.deref().clone());
|
|
|
|
}
|
|
|
|
let formatter = SimpleFormatter::new(tmpl.clone(), vals);
|
|
|
|
Ok(Rc::new(Val::Str(formatter.render(&def.pos)?)))
|
|
|
|
}
|
|
|
|
FormatArgs::Single(ref expr) => {
|
|
|
|
let val = self.eval_expr(expr, scope)?;
|
|
|
|
let mut builder = self.clone_builder();
|
|
|
|
builder.scope.build_output.insert(
|
|
|
|
PositionedItem::new("item".to_string(), expr.pos().clone()),
|
|
|
|
val,
|
|
|
|
);
|
|
|
|
let formatter = ExpressionFormatter::new(tmpl.clone(), builder);
|
|
|
|
Ok(Rc::new(Val::Str(formatter.render(&def.pos)?)))
|
|
|
|
}
|
|
|
|
};
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_call(&self, def: &CallDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
let args = &def.arglist;
|
2019-01-24 20:04:40 -06:00
|
|
|
let v = self.eval_value(&def.funcref, scope)?;
|
2019-02-08 20:57:13 -06:00
|
|
|
let call_pos = def.pos.clone();
|
2019-01-24 20:04:40 -06:00
|
|
|
if let &Val::Func(ref def) = v.deref() {
|
|
|
|
// Congratulations this is actually a function.
|
2017-12-03 18:24:26 -06:00
|
|
|
let mut argvals: Vec<Rc<Val>> = Vec::new();
|
|
|
|
for arg in args.iter() {
|
2018-12-31 17:13:58 -06:00
|
|
|
argvals.push(self.eval_expr(arg, scope)?);
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
2019-02-08 20:57:13 -06:00
|
|
|
return match def.eval(self, argvals) {
|
|
|
|
Ok(v) => Ok(v),
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(e) => Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Func evaluation failed\nCaused by:\n\t{}", e),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
call_pos,
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed()),
|
2019-02-08 20:57:13 -06:00
|
|
|
};
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
// We should pretty print the selectors here.
|
2019-01-24 20:04:40 -06:00
|
|
|
format!("{} is not a Function", v),
|
2018-02-12 22:48:59 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-24 20:04:40 -06:00
|
|
|
fn eval_func_def(&self, def: &mut FuncDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-14 18:23:39 -06:00
|
|
|
def.scope = Some(scope.spawn_child());
|
2019-01-24 20:04:40 -06:00
|
|
|
Ok(Rc::new(Val::Func(def.clone())))
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO(jwall): This stays with the FileBuilder specifically.
|
2018-11-25 13:02:49 -06:00
|
|
|
fn file_dir(&self) -> PathBuf {
|
2019-01-23 20:56:59 -06:00
|
|
|
return if self.working_dir.is_file() {
|
2018-11-23 12:50:47 -06:00
|
|
|
// Only use the dirname portion if the root is a file.
|
2019-01-23 20:56:59 -06:00
|
|
|
self.working_dir.parent().unwrap().to_path_buf()
|
2018-11-23 12:50:47 -06:00
|
|
|
} else {
|
2019-01-23 20:56:59 -06:00
|
|
|
// otherwise use clone of the root.
|
|
|
|
self.working_dir.clone()
|
2018-11-23 12:50:47 -06:00
|
|
|
};
|
2018-11-25 13:02:49 -06:00
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_module_def(&self, def: &ModuleDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-23 20:56:59 -06:00
|
|
|
// TODO(jwall): This should actually be passed in to here.
|
2018-11-25 13:02:49 -06:00
|
|
|
let root = self.file_dir();
|
|
|
|
// Always work on a copy. The original should not be modified.
|
|
|
|
let mut def = def.clone();
|
|
|
|
// First we rewrite the imports to be absolute paths.
|
2018-11-23 12:50:47 -06:00
|
|
|
def.imports_to_absolute(root);
|
|
|
|
// Then we create our tuple default.
|
2018-12-31 17:13:58 -06:00
|
|
|
def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?);
|
2018-11-23 12:50:47 -06:00
|
|
|
// Then we construct a new Val::Module
|
|
|
|
Ok(Rc::new(Val::Module(def)))
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_select(&self, def: &SelectDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
let target = &def.val;
|
|
|
|
let def_expr = &def.default;
|
|
|
|
let fields = &def.tuple;
|
|
|
|
// First resolve the target expression.
|
2018-12-31 17:13:58 -06:00
|
|
|
let v = self.eval_expr(target, scope)?;
|
2017-12-03 18:24:26 -06:00
|
|
|
// Second ensure that the expression resolves to a string.
|
2018-06-10 14:13:08 -05:00
|
|
|
if let &Val::Str(ref name) = v.deref() {
|
2017-12-03 18:24:26 -06:00
|
|
|
// Third find the field with that name in the tuple.
|
|
|
|
for &(ref fname, ref val_expr) in fields.iter() {
|
|
|
|
if &fname.fragment == name {
|
|
|
|
// Fourth return the result of evaluating that field.
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.eval_expr(val_expr, scope);
|
2017-07-19 18:42:31 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2018-04-16 20:04:17 -05:00
|
|
|
// Otherwise return the default.
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.eval_expr(def_expr, scope);
|
2018-11-26 23:03:11 -06:00
|
|
|
} else if let &Val::Boolean(b) = v.deref() {
|
|
|
|
for &(ref fname, ref val_expr) in fields.iter() {
|
|
|
|
if &fname.fragment == "true" && b {
|
|
|
|
// Fourth return the result of evaluating that field.
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.eval_expr(val_expr, scope);
|
2018-11-26 23:03:11 -06:00
|
|
|
} else if &fname.fragment == "false" && !b {
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.eval_expr(val_expr, scope);
|
2018-11-26 23:03:11 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise return the default.
|
2018-12-31 17:13:58 -06:00
|
|
|
return self.eval_expr(def_expr, scope);
|
2017-12-03 18:24:26 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2018-02-12 22:48:59 -06:00
|
|
|
format!(
|
|
|
|
"Expected String but got \
|
|
|
|
{} in Select expression",
|
|
|
|
v.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2017-12-03 18:24:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 15:45:00 -06:00
|
|
|
fn eval_functional_list_processing(
|
2019-01-06 16:17:00 -06:00
|
|
|
&self,
|
2019-01-06 15:45:00 -06:00
|
|
|
elems: &Vec<Rc<Val>>,
|
2019-01-24 20:04:40 -06:00
|
|
|
def: &FuncDef,
|
2019-01-06 20:56:08 -06:00
|
|
|
typ: ProcessingOpType,
|
2019-01-06 15:45:00 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let mut out = Vec::new();
|
|
|
|
for item in elems.iter() {
|
|
|
|
let argvals = vec![item.clone()];
|
2019-02-08 20:57:13 -06:00
|
|
|
let val = def.eval(self, argvals)?;
|
2019-01-16 19:07:03 -06:00
|
|
|
match typ {
|
|
|
|
ProcessingOpType::Map => {
|
|
|
|
out.push(val.clone());
|
|
|
|
}
|
|
|
|
ProcessingOpType::Filter => {
|
|
|
|
if let &Val::Empty = val.as_ref() {
|
|
|
|
// noop
|
|
|
|
continue;
|
|
|
|
} else if let &Val::Boolean(false) = val.as_ref() {
|
|
|
|
// noop
|
|
|
|
continue;
|
2019-01-06 15:45:00 -06:00
|
|
|
}
|
2019-01-16 19:07:03 -06:00
|
|
|
out.push(item.clone());
|
2019-01-06 15:45:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ok(Rc::new(Val::List(out)));
|
|
|
|
}
|
|
|
|
|
2019-01-06 16:17:00 -06:00
|
|
|
fn eval_functional_tuple_processing(
|
|
|
|
&self,
|
2019-02-19 16:36:19 -06:00
|
|
|
fs: &Vec<(String, Rc<Val>)>,
|
2019-01-24 20:04:40 -06:00
|
|
|
def: &FuncDef,
|
2019-01-06 20:56:08 -06:00
|
|
|
typ: ProcessingOpType,
|
2019-01-06 16:17:00 -06:00
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let mut out = Vec::new();
|
|
|
|
for &(ref name, ref val) in fs {
|
2019-02-19 16:36:19 -06:00
|
|
|
let argvals = vec![Rc::new(Val::Str(name.clone())), val.clone()];
|
2019-02-08 20:57:13 -06:00
|
|
|
let result = def.eval(self, argvals)?;
|
2019-01-16 19:07:03 -06:00
|
|
|
match typ {
|
|
|
|
ProcessingOpType::Map => {
|
|
|
|
if let &Val::List(ref fs) = result.as_ref() {
|
|
|
|
if fs.len() == 2 {
|
|
|
|
// index 0 should be a string for the new field name.
|
|
|
|
// index 1 should be the val.
|
|
|
|
let new_name = if let &Val::Str(ref s) = fs[0].as_ref() {
|
|
|
|
s.clone()
|
2019-01-06 16:17:00 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-06 16:17:00 -06:00
|
|
|
format!(
|
2019-01-16 19:07:03 -06:00
|
|
|
"map on tuple expects the first item out list to be a string but got size {}",
|
|
|
|
fs[0].type_name()
|
2019-01-06 16:17:00 -06:00
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
).to_boxed());
|
2019-01-16 19:07:03 -06:00
|
|
|
};
|
2019-02-19 16:36:19 -06:00
|
|
|
out.push((new_name, fs[1].clone()));
|
2019-01-06 16:17:00 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-06 16:17:00 -06:00
|
|
|
format!(
|
2019-01-16 19:07:03 -06:00
|
|
|
"map on a tuple field expects a list of size 2 as output but got size {}",
|
|
|
|
fs.len()
|
2019-01-06 16:17:00 -06:00
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
).to_boxed());
|
2019-01-06 16:17:00 -06:00
|
|
|
}
|
2019-01-16 19:07:03 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-16 19:07:03 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"map on a tuple field expects a list as output but got ({})",
|
2019-01-16 19:07:03 -06:00
|
|
|
result.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-06 16:17:00 -06:00
|
|
|
}
|
2019-01-16 19:07:03 -06:00
|
|
|
}
|
|
|
|
ProcessingOpType::Filter => {
|
|
|
|
if let &Val::Empty = result.as_ref() {
|
|
|
|
// noop
|
|
|
|
continue;
|
|
|
|
} else if let &Val::Boolean(false) = result.as_ref() {
|
|
|
|
// noop
|
|
|
|
continue;
|
2019-01-06 16:17:00 -06:00
|
|
|
}
|
2019-01-16 19:07:03 -06:00
|
|
|
out.push((name.clone(), val.clone()));
|
2019-01-06 16:17:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Rc::new(Val::Tuple(out)))
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_reduce_op(&self, def: &ReduceOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let maybe_target = self.eval_expr(&def.target, scope)?;
|
|
|
|
let mut acc = self.eval_expr(&def.acc, scope)?;
|
2019-01-30 20:23:35 -06:00
|
|
|
let maybe_mac = self.eval_expr(&def.func, scope)?;
|
2019-01-24 20:04:40 -06:00
|
|
|
let funcdef = match maybe_mac.as_ref() {
|
|
|
|
&Val::Func(ref funcdef) => funcdef,
|
2019-01-06 20:56:08 -06:00
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-24 20:04:40 -06:00
|
|
|
format!("Expected func but got {:?}", def.func),
|
2019-01-06 20:56:08 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-06 20:56:08 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
match maybe_target.as_ref() {
|
|
|
|
&Val::List(ref elems) => {
|
|
|
|
for item in elems.iter() {
|
|
|
|
let argvals = vec![acc.clone(), item.clone()];
|
2019-02-08 20:57:13 -06:00
|
|
|
let result = funcdef.eval(self, argvals)?;
|
2019-01-16 19:07:03 -06:00
|
|
|
acc = result;
|
2019-01-06 20:56:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
&Val::Tuple(ref fs) => {
|
|
|
|
for &(ref name, ref val) in fs.iter() {
|
2019-02-19 16:36:19 -06:00
|
|
|
let argvals = vec![acc.clone(), Rc::new(Val::Str(name.clone())), val.clone()];
|
2019-02-08 20:57:13 -06:00
|
|
|
let result = funcdef.eval(self, argvals)?;
|
2019-01-16 19:07:03 -06:00
|
|
|
acc = result;
|
2019-01-06 20:56:08 -06:00
|
|
|
}
|
|
|
|
}
|
2019-01-18 19:58:57 -06:00
|
|
|
&Val::Str(ref s) => {
|
|
|
|
for gc in s.graphemes(true) {
|
|
|
|
let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))];
|
2019-02-08 20:57:13 -06:00
|
|
|
let result = funcdef.eval(self, argvals)?;
|
2019-01-18 19:58:57 -06:00
|
|
|
acc = result;
|
|
|
|
}
|
|
|
|
}
|
2019-01-06 20:56:08 -06:00
|
|
|
other => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-06 20:56:08 -06:00
|
|
|
format!(
|
2019-01-18 19:58:57 -06:00
|
|
|
"Expected List Str, or Tuple as target but got {:?}",
|
2019-01-06 20:56:08 -06:00
|
|
|
other.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.target.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-06 20:56:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(acc)
|
|
|
|
}
|
|
|
|
|
2019-01-18 19:58:57 -06:00
|
|
|
fn eval_functional_string_processing(
|
|
|
|
&self,
|
|
|
|
s: &str,
|
2019-01-24 20:04:40 -06:00
|
|
|
def: &FuncDef,
|
2019-01-18 19:58:57 -06:00
|
|
|
typ: ProcessingOpType,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let mut result = String::new();
|
|
|
|
for gc in s.graphemes(true) {
|
|
|
|
let arg = Rc::new(Val::Str(gc.to_string()));
|
2019-02-08 20:57:13 -06:00
|
|
|
let out = def.eval(self, vec![arg])?;
|
2019-01-18 19:58:57 -06:00
|
|
|
match typ {
|
|
|
|
ProcessingOpType::Filter => {
|
|
|
|
match out.as_ref() {
|
|
|
|
Val::Boolean(b) => {
|
|
|
|
if *b {
|
|
|
|
result.push_str(gc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Val::Empty => {
|
|
|
|
// noop
|
|
|
|
}
|
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-18 19:58:57 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected boolean or NULL for filter return but got ({})",
|
2019-01-18 19:58:57 -06:00
|
|
|
out.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-18 19:58:57 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ProcessingOpType::Map => match out.as_ref() {
|
|
|
|
Val::Str(s) => {
|
|
|
|
result.push_str(&s);
|
|
|
|
}
|
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected string map return but got ({})", out.type_name()),
|
2019-01-18 19:58:57 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-18 19:58:57 -06:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Rc::new(Val::Str(result)))
|
|
|
|
}
|
|
|
|
|
2019-01-06 15:45:00 -06:00
|
|
|
fn eval_functional_processing(
|
2019-01-06 20:56:08 -06:00
|
|
|
&self,
|
|
|
|
def: &MapFilterOpDef,
|
|
|
|
typ: ProcessingOpType,
|
2019-01-06 15:45:00 -06:00
|
|
|
scope: &Scope,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-06 20:56:08 -06:00
|
|
|
let maybe_target = self.eval_expr(&def.target, scope)?;
|
2019-01-30 20:23:35 -06:00
|
|
|
let maybe_mac = self.eval_expr(&def.func, scope)?;
|
2019-01-06 15:45:00 -06:00
|
|
|
let macdef = match maybe_mac.as_ref() {
|
2019-01-24 20:04:40 -06:00
|
|
|
&Val::Func(ref macdef) => macdef,
|
2019-01-06 15:45:00 -06:00
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-24 20:04:40 -06:00
|
|
|
format!("Expected func but got {:?}", def.func),
|
2018-08-21 23:05:06 -05:00
|
|
|
error::ErrorType::TypeFail,
|
2019-01-06 15:45:00 -06:00
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2018-08-21 23:05:06 -05:00
|
|
|
}
|
|
|
|
};
|
2019-01-06 20:56:08 -06:00
|
|
|
return match maybe_target.as_ref() {
|
2019-01-16 19:07:03 -06:00
|
|
|
&Val::List(ref elems) => self.eval_functional_list_processing(elems, macdef, typ),
|
|
|
|
&Val::Tuple(ref fs) => self.eval_functional_tuple_processing(fs, macdef, typ),
|
2019-01-18 19:58:57 -06:00
|
|
|
// TODO(jwall): Strings?
|
|
|
|
&Val::Str(ref s) => self.eval_functional_string_processing(s, macdef, typ),
|
2019-02-20 20:45:33 -06:00
|
|
|
other => Err(error::BuildError::with_pos(
|
2019-01-06 20:56:08 -06:00
|
|
|
format!(
|
|
|
|
"Expected List or Tuple as target but got {:?}",
|
|
|
|
other.type_name()
|
|
|
|
),
|
2019-01-06 15:45:00 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.target.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed()),
|
2019-01-06 15:45:00 -06:00
|
|
|
};
|
2018-03-15 19:08:33 -05:00
|
|
|
}
|
|
|
|
|
2019-01-08 20:32:16 -06:00
|
|
|
fn record_assert_result(&mut self, msg: &str, is_success: bool) {
|
|
|
|
if !is_success {
|
2019-01-13 14:20:08 -06:00
|
|
|
let msg = format!("{} - NOT OK: {}\n", self.assert_collector.counter, msg);
|
2019-01-13 13:47:48 -06:00
|
|
|
self.assert_collector.summary.push_str(&msg);
|
2019-01-08 20:32:16 -06:00
|
|
|
self.assert_collector.failures.push_str(&msg);
|
|
|
|
self.assert_collector.success = false;
|
2019-01-13 13:47:48 -06:00
|
|
|
} else {
|
2019-01-13 14:20:08 -06:00
|
|
|
let msg = format!("{} - OK: {}\n", self.assert_collector.counter, msg);
|
2019-01-13 13:47:48 -06:00
|
|
|
self.assert_collector.summary.push_str(&msg);
|
2019-01-08 20:32:16 -06:00
|
|
|
}
|
2019-01-13 14:20:08 -06:00
|
|
|
self.assert_collector.counter += 1;
|
2019-01-08 20:32:16 -06:00
|
|
|
}
|
|
|
|
|
2019-02-06 20:44:15 -06:00
|
|
|
fn eval_assert(&mut self, expr: &Expression, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2018-06-04 21:45:44 -05:00
|
|
|
if !self.validate_mode {
|
2019-01-24 16:58:27 -06:00
|
|
|
// we are not in validate_mode so build_asserts are noops.
|
2018-06-04 21:45:44 -05:00
|
|
|
return Ok(Rc::new(Val::Empty));
|
|
|
|
}
|
2019-01-08 20:32:16 -06:00
|
|
|
let ok = match self.eval_expr(expr, scope) {
|
2018-06-04 21:45:44 -05:00
|
|
|
Ok(v) => v,
|
|
|
|
Err(e) => {
|
2018-08-29 20:12:54 -05:00
|
|
|
// failure!
|
2019-01-08 20:32:16 -06:00
|
|
|
let msg = format!("CompileError: {}\n", e);
|
2019-01-24 16:58:27 -06:00
|
|
|
self.record_assert_result(&msg, false);
|
2018-08-29 20:12:54 -05:00
|
|
|
return Ok(Rc::new(Val::Empty));
|
2018-06-04 21:45:44 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-08 20:32:16 -06:00
|
|
|
match ok.as_ref() {
|
|
|
|
&Val::Tuple(ref fs) => {
|
|
|
|
let ok_field = match find_in_fieldlist("ok", fs) {
|
|
|
|
Some(ref val) => match val.as_ref() {
|
|
|
|
&Val::Boolean(b) => b,
|
|
|
|
_ => {
|
|
|
|
let msg = format!(
|
2019-02-01 19:17:31 -06:00
|
|
|
"TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}",
|
2019-01-08 20:32:16 -06:00
|
|
|
ok.as_ref(), expr.pos().line, expr.pos().column
|
|
|
|
);
|
|
|
|
self.record_assert_result(&msg, false);
|
|
|
|
return Ok(Rc::new(Val::Empty));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let msg = format!(
|
2019-02-01 19:17:31 -06:00
|
|
|
"TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}",
|
2019-01-08 20:32:16 -06:00
|
|
|
ok.as_ref(), expr.pos().line, expr.pos().column
|
|
|
|
);
|
|
|
|
self.record_assert_result(&msg, false);
|
|
|
|
return Ok(Rc::new(Val::Empty));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let desc = match find_in_fieldlist("desc", fs) {
|
|
|
|
Some(ref val) => match val.as_ref() {
|
|
|
|
Val::Str(ref s) => s.clone(),
|
|
|
|
_ => {
|
|
|
|
let msg = format!(
|
2019-02-01 19:17:31 -06:00
|
|
|
"TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}",
|
2019-01-08 20:32:16 -06:00
|
|
|
ok, expr.pos().line, expr.pos().column
|
|
|
|
);
|
|
|
|
self.record_assert_result(&msg, false);
|
|
|
|
return Ok(Rc::new(Val::Empty));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let msg = format!(
|
2019-02-01 19:17:31 -06:00
|
|
|
"TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}\n",
|
2019-01-08 20:32:16 -06:00
|
|
|
ok, expr.pos().line, expr.pos().column
|
|
|
|
);
|
|
|
|
self.record_assert_result(&msg, false);
|
|
|
|
return Ok(Rc::new(Val::Empty));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
self.record_assert_result(&desc, ok_field);
|
|
|
|
}
|
|
|
|
&Val::Empty
|
|
|
|
| &Val::Boolean(_)
|
|
|
|
| &Val::Env(_)
|
|
|
|
| &Val::Float(_)
|
|
|
|
| &Val::Int(_)
|
|
|
|
| &Val::Str(_)
|
|
|
|
| &Val::List(_)
|
2019-01-24 20:04:40 -06:00
|
|
|
| &Val::Func(_)
|
2019-01-08 20:32:16 -06:00
|
|
|
| &Val::Module(_) => {
|
|
|
|
// record an assertion type-failure result.
|
2018-06-04 21:45:44 -05:00
|
|
|
let msg = format!(
|
2019-01-08 20:32:16 -06:00
|
|
|
"TYPE FAIL - Expected tuple with ok and desc fields got {} at line: {} column: {}\n",
|
|
|
|
ok, expr.pos().line, expr.pos().column
|
2018-06-04 21:45:44 -05:00
|
|
|
);
|
2019-01-08 20:32:16 -06:00
|
|
|
self.record_assert_result(&msg, false);
|
|
|
|
return Ok(Rc::new(Val::Empty));
|
2018-05-30 23:00:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(ok)
|
|
|
|
}
|
|
|
|
|
2019-01-05 14:27:49 -06:00
|
|
|
fn get_file_as_string(&self, pos: &Position, path: &str) -> Result<String, Box<dyn Error>> {
|
|
|
|
let normalized = match self.find_file(path, false) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(e) => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-05 14:27:49 -06:00
|
|
|
format!("Error finding file {} {}", path, e),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-05 14:27:49 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut f = match File::open(&normalized) {
|
|
|
|
Ok(f) => f,
|
|
|
|
Err(e) => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-05 14:27:49 -06:00
|
|
|
format!("Error opening file {} {}", normalized.to_string_lossy(), e),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-05 14:27:49 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut contents = String::new();
|
|
|
|
f.read_to_string(&mut contents)?;
|
|
|
|
Ok(contents)
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
pub fn eval_include(&self, def: &IncludeDef) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-04 10:01:49 -06:00
|
|
|
return if def.typ.fragment == "str" {
|
2019-01-05 14:27:49 -06:00
|
|
|
Ok(Rc::new(Val::Str(
|
|
|
|
self.get_file_as_string(&def.path.pos, &def.path.fragment)?,
|
2019-01-04 10:01:49 -06:00
|
|
|
)))
|
2019-01-05 14:27:49 -06:00
|
|
|
} else {
|
|
|
|
let maybe_importer = self.import_registry.get_importer(&def.typ.fragment);
|
|
|
|
match maybe_importer {
|
|
|
|
Some(importer) => {
|
|
|
|
let file_contents =
|
|
|
|
self.get_file_as_string(&def.path.pos, &def.path.fragment)?;
|
2019-02-19 15:32:04 -06:00
|
|
|
let val = if file_contents.len() == 0 {
|
|
|
|
eprintln!("including an empty file. Use NULL as the result");
|
|
|
|
Rc::new(Val::Empty)
|
|
|
|
} else {
|
2019-02-20 20:29:32 -06:00
|
|
|
match importer.import(file_contents.as_bytes()) {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(e) => {
|
|
|
|
let err = Box::new(error::BuildError::with_pos(
|
|
|
|
format!(
|
|
|
|
"{} include failed for {}",
|
|
|
|
&def.typ.fragment, &def.path.fragment
|
|
|
|
),
|
|
|
|
error::ErrorType::IncludeError,
|
|
|
|
def.pos.clone(),
|
|
|
|
));
|
|
|
|
return Err(err.wrap_cause(e).to_boxed());
|
|
|
|
}
|
|
|
|
}
|
2019-02-19 15:32:04 -06:00
|
|
|
};
|
2019-01-05 14:27:49 -06:00
|
|
|
Ok(val)
|
|
|
|
}
|
2019-02-20 20:45:33 -06:00
|
|
|
None => Err(error::BuildError::with_pos(
|
2019-01-05 14:27:49 -06:00
|
|
|
format!("Unknown include conversion type {}", def.typ.fragment),
|
|
|
|
error::ErrorType::Unsupported,
|
|
|
|
def.typ.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed()),
|
2019-01-05 14:27:49 -06:00
|
|
|
}
|
2019-01-04 10:01:49 -06:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-01-06 20:56:08 -06:00
|
|
|
fn eval_func_op(&self, def: &FuncOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
match def {
|
|
|
|
FuncOpDef::Filter(ref def) => {
|
|
|
|
self.eval_functional_processing(def, ProcessingOpType::Filter, scope)
|
|
|
|
}
|
|
|
|
FuncOpDef::Map(ref def) => {
|
|
|
|
self.eval_functional_processing(def, ProcessingOpType::Map, scope)
|
|
|
|
}
|
|
|
|
FuncOpDef::Reduce(ref def) => self.eval_reduce_op(def, scope),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-10 18:38:14 -06:00
|
|
|
pub fn eval_range(&self, def: &RangeDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
|
|
|
let start = self.eval_expr(&def.start, scope)?;
|
|
|
|
let start = match start.as_ref() {
|
|
|
|
&Val::Int(i) => i,
|
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-10 18:38:14 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected an integer for range start but got ({})",
|
2019-01-10 18:38:14 -06:00
|
|
|
start.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.start.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-10 18:38:14 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
// See if there was a step.
|
|
|
|
let step = match &def.step {
|
|
|
|
Some(step) => {
|
|
|
|
let step = self.eval_expr(&step, scope)?;
|
|
|
|
match step.as_ref() {
|
|
|
|
&Val::Int(i) => i,
|
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-10 18:38:14 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected an integer for range step but got ({})",
|
2019-01-10 18:38:14 -06:00
|
|
|
step.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.start.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-10 18:38:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get the end.
|
|
|
|
let end = self.eval_expr(&def.end, scope)?;
|
|
|
|
let end = match end.as_ref() {
|
|
|
|
&Val::Int(i) => i,
|
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-01-10 18:38:14 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected an integer for range start but got ({})",
|
2019-01-10 18:38:14 -06:00
|
|
|
end.type_name()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.start.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-10 18:38:14 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let vec = (start..end + 1)
|
|
|
|
.step_by(step as usize)
|
|
|
|
.map(|i| Rc::new(Val::Int(i)))
|
|
|
|
.collect();
|
|
|
|
Ok(Rc::new(Val::List(vec)))
|
|
|
|
}
|
|
|
|
|
2019-01-19 10:34:58 -06:00
|
|
|
pub fn eval_is_check(
|
|
|
|
&self,
|
|
|
|
def: &BinaryOpDef,
|
|
|
|
scope: &Scope,
|
|
|
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
2019-01-19 13:06:43 -06:00
|
|
|
let tval = self.eval_expr(def.right.as_ref(), scope)?;
|
|
|
|
let typ = match tval.as_ref() {
|
|
|
|
Val::Str(ref s) => s.clone(),
|
2019-01-19 10:34:58 -06:00
|
|
|
_ => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected string expression but got ({})", tval),
|
2019-01-19 10:34:58 -06:00
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.right.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-19 10:34:58 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let val = self.eval_expr(def.left.as_ref(), scope)?;
|
|
|
|
let result = match typ.as_str() {
|
2019-01-18 18:44:29 -06:00
|
|
|
"str" => val.is_str(),
|
|
|
|
"bool" => val.is_bool(),
|
|
|
|
"null" => val.is_empty(),
|
|
|
|
"int" => val.is_int(),
|
|
|
|
"float" => val.is_float(),
|
|
|
|
"tuple" => val.is_tuple(),
|
|
|
|
"list" => val.is_list(),
|
2019-01-24 20:04:40 -06:00
|
|
|
"func" => val.is_func(),
|
2019-01-18 18:44:29 -06:00
|
|
|
"module" => val.is_module(),
|
|
|
|
other => {
|
2019-02-20 20:45:33 -06:00
|
|
|
return Err(error::BuildError::with_pos(
|
2019-02-08 20:57:13 -06:00
|
|
|
format!("Expected valid type name but got ({})", other),
|
2019-01-18 18:44:29 -06:00
|
|
|
error::ErrorType::TypeFail,
|
2019-01-19 10:34:58 -06:00
|
|
|
def.right.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed());
|
2019-01-18 18:44:29 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Rc::new(Val::Boolean(result)))
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:49:13 -06:00
|
|
|
// Evals a single Expression in the context of a running Builder.
|
2017-12-03 18:24:26 -06:00
|
|
|
// It does not mutate the builders collected state at all.
|
2019-01-06 20:56:08 -06:00
|
|
|
pub fn eval_expr(&self, expr: &Expression, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
2017-12-03 18:24:26 -06:00
|
|
|
match expr {
|
2018-12-31 17:13:58 -06:00
|
|
|
&Expression::Simple(ref val) => self.eval_value(val, scope),
|
|
|
|
&Expression::Binary(ref def) => self.eval_binary(def, scope),
|
|
|
|
&Expression::Copy(ref def) => self.eval_copy(def, scope),
|
2019-01-10 18:38:14 -06:00
|
|
|
&Expression::Range(ref def) => self.eval_range(def, scope),
|
2018-12-31 17:13:58 -06:00
|
|
|
&Expression::Grouped(ref expr) => self.eval_expr(expr, scope),
|
|
|
|
&Expression::Format(ref def) => self.eval_format(def, scope),
|
|
|
|
&Expression::Call(ref def) => self.eval_call(def, scope),
|
2019-01-24 20:04:40 -06:00
|
|
|
&Expression::Func(ref def) => {
|
2019-01-14 18:23:39 -06:00
|
|
|
let mut def_clone = def.clone();
|
2019-01-24 20:04:40 -06:00
|
|
|
self.eval_func_def(&mut def_clone, scope)
|
2019-01-14 18:23:39 -06:00
|
|
|
}
|
|
|
|
&Expression::Module(ref def) => {
|
|
|
|
let mut def_clone = def.clone();
|
|
|
|
self.eval_module_def(&mut def_clone, scope)
|
|
|
|
}
|
2018-12-31 17:13:58 -06:00
|
|
|
&Expression::Select(ref def) => self.eval_select(def, scope),
|
2019-01-06 20:56:08 -06:00
|
|
|
&Expression::FuncOp(ref def) => self.eval_func_op(def, scope),
|
2019-01-04 10:01:49 -06:00
|
|
|
&Expression::Include(ref def) => self.eval_include(def),
|
2019-01-13 09:37:44 -06:00
|
|
|
&Expression::Import(ref def) => self.eval_import(def),
|
2019-01-19 11:18:24 -06:00
|
|
|
&Expression::Fail(ref def) => {
|
|
|
|
let err = self.eval_expr(&def.message, scope)?;
|
|
|
|
return if let Val::Str(ref s) = err.as_ref() {
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-01-19 11:18:24 -06:00
|
|
|
s.clone(),
|
|
|
|
error::ErrorType::UserDefined,
|
|
|
|
def.pos.clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2019-01-19 11:18:24 -06:00
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-01-19 11:18:24 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected string for message but got ({})",
|
2019-01-19 11:18:24 -06:00
|
|
|
def.message.as_ref()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.message.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2019-01-19 11:18:24 -06:00
|
|
|
};
|
|
|
|
}
|
2019-01-24 16:53:02 -06:00
|
|
|
&Expression::Not(ref def) => {
|
|
|
|
let val = self.eval_expr(&def.expr, scope)?;
|
|
|
|
return if let Val::Boolean(b) = val.as_ref() {
|
|
|
|
Ok(Rc::new(Val::Boolean(!b)))
|
|
|
|
} else {
|
2019-02-20 20:45:33 -06:00
|
|
|
Err(error::BuildError::with_pos(
|
2019-01-24 16:53:02 -06:00
|
|
|
format!(
|
2019-02-08 20:57:13 -06:00
|
|
|
"Expected boolean for expression but got ({})",
|
2019-01-24 16:53:02 -06:00
|
|
|
def.expr.as_ref()
|
|
|
|
),
|
|
|
|
error::ErrorType::TypeFail,
|
|
|
|
def.expr.pos().clone(),
|
2019-02-20 20:45:33 -06:00
|
|
|
)
|
|
|
|
.to_boxed())
|
2019-01-24 16:53:02 -06:00
|
|
|
};
|
|
|
|
}
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
|
2018-04-16 20:04:17 -05:00
|
|
|
#[cfg(test)]
|
2018-05-22 18:02:44 -05:00
|
|
|
mod compile_test;
|
2018-04-16 20:04:17 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[cfg(test)]
|
2018-05-22 18:02:44 -05:00
|
|
|
mod test;
|