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.
|
2017-07-25 20:45:58 -05:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Read;
|
2017-06-08 22:15:48 -05:00
|
|
|
use std::error::Error;
|
2017-11-05 15:26:52 -06:00
|
|
|
use std::collections::{HashSet, HashMap, VecDeque};
|
2017-06-10 11:09:24 -05:00
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
use std::fmt;
|
2017-11-05 15:26:52 -06:00
|
|
|
use std::fmt::{Display, Formatter};
|
2017-07-19 18:42:31 -05:00
|
|
|
use std::ops::Deref;
|
|
|
|
use std::rc::Rc;
|
2017-11-05 15:26:52 -06:00
|
|
|
use std::convert::From;
|
2017-06-08 22:15:48 -05:00
|
|
|
|
2017-07-25 20:45:58 -05:00
|
|
|
use nom;
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
use tokenizer::Span;
|
2017-08-12 14:48:28 -05:00
|
|
|
use ast::*;
|
2017-08-08 21:02:54 -05:00
|
|
|
use format;
|
2017-08-12 14:48:28 -05:00
|
|
|
use parse::parse;
|
|
|
|
|
|
|
|
impl MacroDef {
|
2017-11-05 15:26:52 -06:00
|
|
|
pub fn eval(&self,
|
|
|
|
mut args: Vec<Rc<Val>>)
|
|
|
|
-> Result<Vec<(Positioned<String>, Rc<Val>)>, Box<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
|
|
|
|
// macro call error.
|
|
|
|
if args.len() > self.argdefs.len() {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::BadArgLen("Macro called with too many args"
|
|
|
|
.to_string())));
|
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.
|
|
|
|
// If the expressions reference Symbols not defined in the MacroDef that is also an error.
|
|
|
|
// TODO(jwall): We should probably enforce that the Expression Symbols must be in argdefs rules
|
|
|
|
// at Macro definition time not evaluation time.
|
2017-11-06 21:06:30 -06:00
|
|
|
let mut scope = HashMap::<Positioned<String>, Rc<Val>>::new();
|
2017-08-12 14:48:28 -05:00
|
|
|
for (i, arg) in args.drain(0..).enumerate() {
|
2017-11-06 21:06:30 -06:00
|
|
|
scope.entry(self.argdefs[i].clone()).or_insert(arg.clone());
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
let b = Builder::new_with_scope(scope);
|
2017-11-05 15:26:52 -06:00
|
|
|
let mut result: Vec<(Positioned<String>, Rc<Val>)> = Vec::new();
|
2017-08-12 14:48:28 -05:00
|
|
|
for &(ref key, ref expr) in self.fields.iter() {
|
|
|
|
// We clone the expressions here because this macro may be consumed
|
|
|
|
// multiple times in the future.
|
2017-11-05 15:26:52 -06:00
|
|
|
let val = try!(b.eval_expr(expr));
|
|
|
|
result.push((key.into(), val.clone()));
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
2017-08-08 21:02:54 -05:00
|
|
|
|
2017-06-08 22:15:48 -05:00
|
|
|
quick_error! {
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
|
|
pub enum BuildError {
|
|
|
|
TypeFail(msg: String) {
|
2017-06-10 11:09:24 -05:00
|
|
|
description("Type Error")
|
|
|
|
display("Type Error {}", msg)
|
|
|
|
}
|
2017-07-25 20:45:58 -05:00
|
|
|
DuplicateBinding(msg: String) {
|
|
|
|
description("Atttempt to add duplicate binding in file")
|
|
|
|
display("Atttempt to add duplicate binding in file {}", msg)
|
|
|
|
}
|
|
|
|
IncompleteParse(msg: String) {
|
|
|
|
description("Incomplete Parse of file")
|
|
|
|
display("Incomplete Parse of file {}", msg)
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
Unsupported(msg: String) {
|
|
|
|
description("Unsupported Operation")
|
|
|
|
display("Unsupported Operation {}", msg)
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
NoSuchSymbol(msg: String) {
|
|
|
|
description("Eval Error")
|
2017-06-10 11:09:24 -05:00
|
|
|
display("No Such Variable {}", msg)
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
2017-07-19 18:42:31 -05:00
|
|
|
BadArgLen(msg: String) {
|
|
|
|
description("Eval Error")
|
|
|
|
display("Bad Argument Length {}", msg)
|
|
|
|
}
|
2017-08-08 21:02:54 -05:00
|
|
|
FormatError(msg: String) {
|
|
|
|
description("String Format Error")
|
|
|
|
display("String format Error {}", msg)
|
|
|
|
}
|
2017-06-08 22:15:48 -05:00
|
|
|
TODO(msg: String) {
|
2017-06-10 11:09:24 -05:00
|
|
|
description("TODO Error")
|
|
|
|
display("TODO Error {}", msg)
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BuildResult is the result of a build.
|
|
|
|
type BuildResult = Result<(), Box<Error>>;
|
|
|
|
|
2017-08-12 14:48:28 -05:00
|
|
|
/// Val is the Intermediate representation of a compiled UCG AST.
|
2017-06-10 11:09:24 -05:00
|
|
|
#[derive(PartialEq,Debug,Clone)]
|
2017-07-19 18:42:31 -05:00
|
|
|
pub enum Val {
|
2017-06-08 22:15:48 -05:00
|
|
|
Int(i64),
|
|
|
|
Float(f64),
|
2017-06-10 11:09:24 -05:00
|
|
|
String(String),
|
2017-11-26 12:22:58 -05:00
|
|
|
List(Vec<Rc<Val>>),
|
2017-11-05 15:26:52 -06:00
|
|
|
Tuple(Vec<(Positioned<String>, Rc<Val>)>),
|
2017-07-19 18:42:31 -05:00
|
|
|
Macro(MacroDef),
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
|
2017-07-19 18:42:31 -05:00
|
|
|
impl Val {
|
2017-06-10 11:09:24 -05:00
|
|
|
pub fn type_name(&self) -> String {
|
|
|
|
match self {
|
|
|
|
&Val::Int(_) => "Integer".to_string(),
|
|
|
|
&Val::Float(_) => "Float".to_string(),
|
|
|
|
&Val::String(_) => "String".to_string(),
|
2017-11-26 12:22:58 -05:00
|
|
|
&Val::List(_) => "List".to_string(),
|
2017-06-10 11:09:24 -05:00
|
|
|
&Val::Tuple(_) => "Tuple".to_string(),
|
2017-07-12 20:55:03 -05:00
|
|
|
&Val::Macro(_) => "Macro".to_string(),
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
|
|
|
|
pub fn type_equal(&self, target: &Self) -> bool {
|
|
|
|
match self {
|
2017-11-05 15:26:52 -06:00
|
|
|
&Val::Int(_) => {
|
|
|
|
if let &Val::Int(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&Val::Float(_) => {
|
|
|
|
if let &Val::Float(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&Val::String(_) => {
|
|
|
|
if let &Val::String(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2017-11-26 12:22:58 -05:00
|
|
|
&Val::List(_) => {
|
|
|
|
if let &Val::List(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
&Val::Tuple(_) => {
|
|
|
|
if let &Val::Tuple(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&Val::Macro(_) => {
|
|
|
|
if let &Val::Macro(_) = target {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
pub fn get_fields(&self) -> Option<&Vec<(Positioned<String>, Rc<Val>)>> {
|
2017-06-10 11:09:24 -05:00
|
|
|
if let &Val::Tuple(ref fs) = self {
|
|
|
|
Some(fs)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_int(&self) -> bool {
|
|
|
|
if let &Val::Int(_) = self {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_float(&self) -> bool {
|
|
|
|
if let &Val::Float(_) = self {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_string(&self) -> bool {
|
|
|
|
if let &Val::String(_) = self {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_tuple(&self) -> bool {
|
|
|
|
if let &Val::Tuple(_) = self {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-19 18:42:31 -05:00
|
|
|
impl Display for Val {
|
2017-06-10 11:09:24 -05:00
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
// TODO(jwall): These should render better than this.
|
|
|
|
write!(f, "{}", self.type_name())
|
|
|
|
}
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
|
2017-08-08 21:02:54 -05:00
|
|
|
impl From<Val> for String {
|
|
|
|
fn from(v: Val) -> String {
|
|
|
|
match v {
|
|
|
|
Val::Int(ref i) => format!("{}", i),
|
|
|
|
Val::Float(ref f) => format!("{}", f),
|
|
|
|
Val::String(ref s) => s.to_string(),
|
|
|
|
val => format!("<{}>", val),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-12 20:30:52 -05:00
|
|
|
/// ValueMap defines a set of values in a parsed file.
|
2017-11-06 21:06:30 -06:00
|
|
|
type ValueMap = HashMap<Positioned<String>, Rc<Val>>;
|
2017-06-08 22:15:48 -05:00
|
|
|
|
|
|
|
/// Builder parses one or more statements into a out Tuple.
|
2017-07-19 18:42:31 -05:00
|
|
|
pub struct Builder {
|
2017-06-08 22:15:48 -05:00
|
|
|
/// assets are other parsed files from import statements. They
|
|
|
|
/// are keyed by the normalized import path. This acts as a cache
|
|
|
|
/// so multiple imports of the same file don't have to be parsed
|
|
|
|
/// multiple times.
|
2017-07-25 20:45:58 -05:00
|
|
|
assets: ValueMap,
|
|
|
|
// List of file paths we have already parsed.
|
|
|
|
files: HashSet<String>,
|
2017-06-08 22:15:48 -05:00
|
|
|
/// out is our built output.
|
2017-07-25 20:45:58 -05:00
|
|
|
out: ValueMap,
|
2017-07-29 12:57:34 -05:00
|
|
|
/// last is the result of the last statement.
|
2017-11-15 22:41:55 -06:00
|
|
|
pub last: Option<Rc<Val>>,
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! eval_binary_expr {
|
|
|
|
($case:pat, $rside:ident, $result:expr, $msg:expr) => {
|
|
|
|
match $rside.as_ref() {
|
|
|
|
$case => {
|
|
|
|
return Ok(Rc::new($result))
|
|
|
|
},
|
|
|
|
val => {
|
|
|
|
return Err(Box::new(
|
|
|
|
BuildError::TypeFail(
|
|
|
|
format!("Expected {} but got {}", $msg, val))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
|
2017-07-19 18:42:31 -05:00
|
|
|
impl Builder {
|
2017-06-08 22:15:48 -05:00
|
|
|
/// new_builder constructs Builder with initialized fields ready to parse.
|
2017-11-05 15:26:52 -06:00
|
|
|
fn value_to_val(&self, v: &Value) -> Result<Rc<Val>, Box<Error>> {
|
2017-06-10 11:09:24 -05:00
|
|
|
match v {
|
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))),
|
|
|
|
&Value::String(ref s) => Ok(Rc::new(Val::String(s.val.to_string()))),
|
|
|
|
&Value::Symbol(ref s) => {
|
|
|
|
self.lookup_sym(&(s.into()))
|
|
|
|
.ok_or(Box::new(BuildError::NoSuchSymbol(format!("Unable to find {}", s.val))))
|
|
|
|
}
|
|
|
|
&Value::Tuple(ref tuple_node) => {
|
|
|
|
let fields = tuple_node.val();
|
|
|
|
let mut new_fields = Vec::<(Positioned<String>, Rc<Val>)>::new();
|
|
|
|
for &(ref name, ref expr) in fields.iter() {
|
2017-07-29 12:05:39 -05:00
|
|
|
let val = try!(self.eval_expr(expr));
|
2017-11-05 15:26:52 -06:00
|
|
|
new_fields.push((name.into(), val));
|
2017-07-29 12:05:39 -05:00
|
|
|
}
|
|
|
|
new_fields.sort_by(|a, b| a.0.cmp(&b.0));
|
|
|
|
Ok(Rc::new(Val::Tuple(new_fields)))
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&Value::Selector(ref selector_list_node) => {
|
|
|
|
self.lookup_selector(&selector_list_node.val)
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new() -> Self {
|
2017-06-08 22:15:48 -05:00
|
|
|
Builder {
|
|
|
|
assets: HashMap::new(),
|
2017-07-25 20:45:58 -05:00
|
|
|
files: HashSet::new(),
|
2017-06-10 11:09:24 -05:00
|
|
|
out: HashMap::new(),
|
2017-07-29 12:57:34 -05:00
|
|
|
last: None,
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
pub fn new_with_scope(scope: ValueMap) -> Self {
|
2017-07-19 18:42:31 -05:00
|
|
|
Builder {
|
|
|
|
assets: HashMap::new(),
|
2017-07-25 20:45:58 -05:00
|
|
|
files: HashSet::new(),
|
2017-07-19 18:42:31 -05:00
|
|
|
out: scope,
|
2017-07-29 12:57:34 -05:00
|
|
|
last: None,
|
2017-07-19 18:42:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 22:41:55 -06:00
|
|
|
pub fn get_out_by_name(&self, name: &str) -> Option<Rc<Val>> {
|
|
|
|
let key = Positioned {
|
|
|
|
pos: Position {
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
|
|
|
},
|
|
|
|
val: name.to_string(),
|
|
|
|
};
|
|
|
|
self.lookup_sym(&key)
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
pub fn build(&mut self, ast: &Vec<Statement>) -> BuildResult {
|
|
|
|
for stmt in ast.iter() {
|
2017-07-25 20:45:58 -05:00
|
|
|
try!(self.build_stmt(stmt));
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2017-08-12 14:48:28 -05:00
|
|
|
pub fn build_file_string(&mut self, name: &str, input: String) -> BuildResult {
|
2017-11-05 15:26:52 -06:00
|
|
|
match parse(Span::new(&input)) {
|
2017-11-15 22:41:55 -06:00
|
|
|
nom::IResult::Done(_span, stmts) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
for stmt in stmts.iter() {
|
2017-08-12 14:48:28 -05:00
|
|
|
try!(self.build_stmt(stmt));
|
|
|
|
}
|
|
|
|
Ok(())
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
nom::IResult::Error(err) => Err(Box::new(err)),
|
2017-11-05 15:26:52 -06:00
|
|
|
nom::IResult::Incomplete(_) => {
|
|
|
|
Err(Box::new(BuildError::IncompleteParse(format!("Could not parse input from \
|
|
|
|
file: {}",
|
|
|
|
name))))
|
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build_file(&mut self, name: &str) -> BuildResult {
|
|
|
|
let mut f = try!(File::open(name));
|
|
|
|
let mut s = String::new();
|
2017-11-26 12:22:58 -05:00
|
|
|
// TODO(jwall): It would be nice to be able to do this while streaming
|
2017-08-12 14:48:28 -05:00
|
|
|
try!(f.read_to_string(&mut s));
|
|
|
|
self.build_file_string(name, s)
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn build_stmt(&mut self, stmt: &Statement) -> BuildResult {
|
2017-08-12 14:48:28 -05:00
|
|
|
match stmt {
|
2017-11-05 15:26:52 -06:00
|
|
|
&Statement::Let { name: ref sym, value: ref expr } => {
|
2017-08-12 14:48:28 -05:00
|
|
|
let val = try!(self.eval_expr(expr));
|
|
|
|
self.last = Some(val.clone());
|
2017-11-06 21:06:30 -06:00
|
|
|
match self.out.entry(sym.into()) {
|
2017-08-12 14:48:28 -05:00
|
|
|
Entry::Occupied(e) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::DuplicateBinding(format!("Let binding \
|
|
|
|
for {:?} already \
|
|
|
|
exists",
|
|
|
|
e.key()))));
|
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
Entry::Vacant(e) => {
|
|
|
|
e.insert(val);
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-08-12 14:48:28 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
&Statement::Import { path: ref val, name: ref sym } => {
|
|
|
|
if !self.files.contains(val) {
|
|
|
|
// Only parse the file once on import.
|
2017-11-06 21:06:30 -06:00
|
|
|
let positioned_sym = sym.into();
|
|
|
|
if self.assets.get(&positioned_sym).is_none() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let mut b = Self::new();
|
|
|
|
try!(b.build_file(&val));
|
2017-11-08 18:55:10 -06:00
|
|
|
let fields: Vec<(Positioned<String>, Rc<Val>)> = b.out.drain().collect();
|
2017-08-12 14:48:28 -05:00
|
|
|
let result = Rc::new(Val::Tuple(fields));
|
2017-11-06 21:06:30 -06:00
|
|
|
self.assets.entry(positioned_sym).or_insert(result.clone());
|
2017-11-05 15:26:52 -06:00
|
|
|
self.files.insert(val.clone());
|
2017-08-12 14:48:28 -05:00
|
|
|
self.last = Some(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
&Statement::Expression(ref expr) => {
|
2017-08-12 14:48:28 -05:00
|
|
|
self.last = Some(try!(self.eval_expr(expr)));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn lookup_sym(&self, sym: &Positioned<String>) -> Option<Rc<Val>> {
|
2017-11-06 21:06:30 -06:00
|
|
|
if self.out.contains_key(sym) {
|
|
|
|
return Some(self.out[sym].clone());
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-11-06 21:06:30 -06:00
|
|
|
if self.assets.contains_key(sym) {
|
|
|
|
return Some(self.assets[sym].clone());
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-07-25 20:45:58 -05:00
|
|
|
None
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn find_in_fieldlist(target: &str, fs: &Vec<(Positioned<String>, Rc<Val>)>) -> Option<Rc<Val>> {
|
2017-06-10 11:09:24 -05:00
|
|
|
for (key, val) in fs.iter().cloned() {
|
2017-11-05 15:26:52 -06:00
|
|
|
if target == &key.val {
|
|
|
|
return Some(val.clone());
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
return None;
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn lookup_selector(&self, sl: &SelectorList) -> Result<Rc<Val>, Box<Error>> {
|
2017-06-10 11:09:24 -05:00
|
|
|
let len = sl.len();
|
|
|
|
if len > 0 {
|
2017-11-05 15:26:52 -06:00
|
|
|
let pos_sl = (&sl[0]).into();
|
|
|
|
if let Some(v) = self.lookup_sym(&pos_sl) {
|
2017-06-10 11:09:24 -05:00
|
|
|
let mut it = sl.iter().skip(1).peekable();
|
|
|
|
if it.peek().is_none() {
|
|
|
|
return Ok(v.clone());
|
|
|
|
}
|
2017-07-29 13:02:47 -05:00
|
|
|
if let &Val::Tuple(_) = v.as_ref() {
|
2017-06-10 11:09:24 -05:00
|
|
|
let mut stack = VecDeque::new();
|
|
|
|
stack.push_back(v.clone());
|
|
|
|
loop {
|
|
|
|
let vref = stack.pop_front().unwrap();
|
|
|
|
if it.peek().is_none() {
|
|
|
|
return Ok(vref.clone());
|
|
|
|
}
|
|
|
|
// This unwrap is safe because we already checked for
|
|
|
|
// None above.
|
|
|
|
let k = it.next().unwrap();
|
|
|
|
if !vref.is_tuple() {
|
2017-11-05 15:26:52 -06:00
|
|
|
// TODO(jeremy) BuildErrors should take a token so they can
|
|
|
|
// render the location of the error.
|
|
|
|
return Err(Box::new(BuildError::NoSuchSymbol(format!("Attempted \
|
|
|
|
to dereference \
|
|
|
|
non-tuple \
|
|
|
|
{:?} at field \
|
|
|
|
{}.",
|
|
|
|
sl,
|
|
|
|
k.fragment))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
// This unwrap is safe because we already checked for
|
|
|
|
// Tuple above.
|
|
|
|
let fs = vref.get_fields().unwrap();
|
2017-11-05 15:26:52 -06:00
|
|
|
if let Some(vv) = Self::find_in_fieldlist(&k.fragment, fs) {
|
2017-06-10 11:09:24 -05:00
|
|
|
if vv.is_tuple() {
|
|
|
|
stack.push_back(vv.clone());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if it.peek().is_some() {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to \
|
|
|
|
match selector \
|
|
|
|
path {:?}",
|
|
|
|
sl))));
|
2017-06-10 11:09:24 -05:00
|
|
|
} else {
|
|
|
|
return Ok(vv.clone());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO(jwall): A better error for this would be nice.
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to \
|
|
|
|
match selector \
|
|
|
|
path {:?}",
|
|
|
|
sl))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::TypeFail(format!("{} is not a Tuple",
|
|
|
|
sl[0].fragment))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::NoSuchSymbol(format!("Unable to find Symbol {}",
|
|
|
|
sl[0].fragment))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::NoSuchSymbol("Attempted to lookup an empty selector"
|
|
|
|
.to_string())));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// eval_expr evals a single Expression in the context of a running Builder.
|
|
|
|
// It does not mutate the builders collected state at all.
|
2017-11-05 15:26:52 -06:00
|
|
|
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
2017-07-19 18:42:31 -05:00
|
|
|
// TODO(jwall): We probably don't want to consume these expressions.
|
|
|
|
// Take a reference instead?
|
2017-06-08 22:15:48 -05:00
|
|
|
match expr {
|
2017-11-05 15:26:52 -06:00
|
|
|
&Expression::Simple(ref val) => self.value_to_val(val),
|
|
|
|
&Expression::Binary(ref def) => {
|
|
|
|
let kind = &def.kind;
|
|
|
|
let v = &def.left;
|
|
|
|
let expr = &def.right;
|
|
|
|
let expr_result = try!(self.eval_expr(expr));
|
2017-08-12 14:48:28 -05:00
|
|
|
let v = try!(self.value_to_val(v));
|
2017-09-21 00:06:43 -05:00
|
|
|
match kind {
|
2017-11-05 15:26:52 -06:00
|
|
|
&BinaryExprType::Add => {
|
2017-09-21 00:06:43 -05:00
|
|
|
match *v {
|
|
|
|
Val::Int(i) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii),
|
|
|
|
expr_result,
|
|
|
|
Val::Int(i + ii),
|
|
|
|
"Integer")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
Val::Float(f) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff),
|
|
|
|
expr_result,
|
|
|
|
Val::Float(f + ff),
|
|
|
|
"Float")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
Val::String(ref s) => {
|
|
|
|
match expr_result.as_ref() {
|
|
|
|
&Val::String(ref ss) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Ok(Rc::new(Val::String([s.to_string(), ss.clone()]
|
|
|
|
.concat())))
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
val => {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::TypeFail(format!("Expected \
|
|
|
|
String \
|
|
|
|
but got \
|
|
|
|
{:?}",
|
|
|
|
val))))
|
2017-09-21 00:06:43 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
ref expr => {
|
2017-06-10 11:09:24 -05:00
|
|
|
return Err(Box::new(
|
2017-09-21 00:06:43 -05:00
|
|
|
BuildError::Unsupported(
|
|
|
|
format!("{} does not support the '+' operation", expr.type_name()))))
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&BinaryExprType::Sub => {
|
2017-09-21 00:06:43 -05:00
|
|
|
match *v {
|
|
|
|
Val::Int(i) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii),
|
|
|
|
expr_result,
|
|
|
|
Val::Int(i - ii),
|
|
|
|
"Integer")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
Val::Float(f) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff),
|
|
|
|
expr_result,
|
|
|
|
Val::Float(f - ff),
|
|
|
|
"Float")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
ref expr => {
|
|
|
|
return Err(Box::new(
|
|
|
|
BuildError::Unsupported(
|
|
|
|
format!("{} does not support the '-' operation", expr.type_name()))))
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&BinaryExprType::Mul => {
|
2017-09-21 00:06:43 -05:00
|
|
|
match *v {
|
|
|
|
Val::Int(i) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii),
|
|
|
|
expr_result,
|
|
|
|
Val::Int(i * ii),
|
|
|
|
"Integer")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
Val::Float(f) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff),
|
|
|
|
expr_result,
|
|
|
|
Val::Float(f * ff),
|
|
|
|
"Float")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
ref expr => {
|
|
|
|
return Err(Box::new(
|
|
|
|
BuildError::Unsupported(
|
|
|
|
format!("{} does not support the '*' operation", expr.type_name()))))
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&BinaryExprType::Div => {
|
2017-09-21 00:06:43 -05:00
|
|
|
match *v {
|
|
|
|
Val::Int(i) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Int(ii),
|
|
|
|
expr_result,
|
|
|
|
Val::Int(i / ii),
|
|
|
|
"Integer")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
Val::Float(f) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
eval_binary_expr!(&Val::Float(ff),
|
|
|
|
expr_result,
|
|
|
|
Val::Float(f / ff),
|
|
|
|
"Float")
|
|
|
|
}
|
2017-09-21 00:06:43 -05:00
|
|
|
ref expr => {
|
|
|
|
return Err(Box::new(
|
|
|
|
BuildError::Unsupported(
|
|
|
|
format!("{} does not support the '*' operation", expr.type_name()))))
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&Expression::Copy(ref def) => {
|
|
|
|
let v = try!(self.lookup_selector(&def.selector));
|
2017-06-10 11:09:24 -05:00
|
|
|
if let Val::Tuple(ref src_fields) = *v {
|
2017-11-06 21:06:30 -06:00
|
|
|
let mut m = HashMap::<Positioned<String>, Rc<Val>>::new();
|
|
|
|
// loop through fields and build up a hahsmap
|
2017-06-10 11:09:24 -05:00
|
|
|
for &(ref key, ref val) in src_fields.iter() {
|
2017-11-06 21:06:30 -06:00
|
|
|
if let Entry::Vacant(v) = m.entry(key.clone()) {
|
2017-06-10 11:09:24 -05:00
|
|
|
v.insert(val.clone());
|
|
|
|
} else {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::TypeFail(format!("Duplicate \
|
|
|
|
field: {} in \
|
|
|
|
tuple",
|
|
|
|
key.val))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
for &(ref key, ref val) in def.fields.iter() {
|
2017-06-10 11:09:24 -05:00
|
|
|
let expr_result = try!(self.eval_expr(val));
|
2017-11-06 21:06:30 -06:00
|
|
|
match m.entry(key.into()) {
|
2017-07-29 13:02:47 -05:00
|
|
|
Entry::Vacant(v) => {
|
2017-06-10 11:09:24 -05:00
|
|
|
v.insert(expr_result);
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
Entry::Occupied(mut v) => {
|
|
|
|
// Ensure that the new type matches the old type.
|
|
|
|
let src_val = v.get().clone();
|
|
|
|
if src_val.type_equal(&expr_result) {
|
|
|
|
v.insert(expr_result);
|
|
|
|
} else {
|
|
|
|
return Err(Box::new(
|
|
|
|
BuildError::TypeFail(
|
|
|
|
format!("Expected type {} for field {} but got {}",
|
2017-11-05 15:26:52 -06:00
|
|
|
src_val.type_name(), key.fragment, expr_result.type_name()))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
};
|
|
|
|
}
|
2017-11-06 21:06:30 -06:00
|
|
|
let mut new_fields: Vec<(Positioned<String>, Rc<Val>)> = m.drain().collect();
|
2017-06-10 11:09:24 -05:00
|
|
|
// We want a stable order for the fields to make comparing tuples
|
|
|
|
// easier in later code. So we sort by the field name before constructing a new tuple.
|
2017-07-19 18:42:31 -05:00
|
|
|
new_fields.sort_by(|a, b| a.0.cmp(&b.0));
|
2017-06-10 11:09:24 -05:00
|
|
|
return Ok(Rc::new(Val::Tuple(new_fields)));
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
Err(Box::new(BuildError::TypeFail(format!("Expected Tuple got {}", v))))
|
|
|
|
}
|
|
|
|
&Expression::Grouped(ref expr) => {
|
|
|
|
return self.eval_expr(expr);
|
|
|
|
}
|
2017-11-26 12:22:58 -05:00
|
|
|
&Expression::List(ref def) => {
|
|
|
|
let mut vals = Vec::new();
|
|
|
|
for expr in def.elems.iter() {
|
|
|
|
vals.push(try!(self.eval_expr(expr)));
|
|
|
|
}
|
|
|
|
return Ok(Rc::new(Val::List(vals)));
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
&Expression::Format(ref def) => {
|
|
|
|
let tmpl = &def.template;
|
|
|
|
let args = &def.args;
|
2017-08-08 21:02:54 -05:00
|
|
|
let mut vals = Vec::new();
|
2017-11-05 15:26:52 -06:00
|
|
|
for v in args.iter() {
|
2017-08-08 21:02:54 -05:00
|
|
|
let rcv = try!(self.eval_expr(v));
|
|
|
|
vals.push(rcv.deref().clone());
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
let formatter = format::Formatter::new(tmpl.clone(), vals);
|
2017-08-08 21:02:54 -05:00
|
|
|
Ok(Rc::new(Val::String(try!(formatter.render()))))
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&Expression::Call(ref def) => {
|
|
|
|
let sel = &def.macroref;
|
|
|
|
let args = &def.arglist;
|
2017-07-19 18:42:31 -05:00
|
|
|
let v = try!(self.lookup_selector(sel));
|
|
|
|
if let &Val::Macro(ref m) = v.deref() {
|
|
|
|
// Congratulations this is actually a macro.
|
|
|
|
let mut argvals: Vec<Rc<Val>> = Vec::new();
|
2017-11-05 15:26:52 -06:00
|
|
|
for arg in args.iter() {
|
2017-07-19 18:42:31 -05:00
|
|
|
argvals.push(try!(self.eval_expr(arg)));
|
|
|
|
}
|
|
|
|
let fields = try!(m.eval(argvals));
|
|
|
|
return Ok(Rc::new(Val::Tuple(fields)));
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
Err(Box::new(BuildError::TypeFail(// We should pretty print the selectors here.
|
|
|
|
format!("{} is not a Macro", v))))
|
|
|
|
}
|
|
|
|
&Expression::Macro(ref def) => {
|
2017-09-07 14:29:32 -05:00
|
|
|
match def.validate_symbols() {
|
2017-11-05 15:26:52 -06:00
|
|
|
Ok(()) => Ok(Rc::new(Val::Macro(def.clone()))),
|
|
|
|
Err(set) => {
|
|
|
|
Err(Box::new(BuildError::NoSuchSymbol(format!("Macro has the following \
|
|
|
|
undefined symbols: {:?}",
|
|
|
|
set))))
|
|
|
|
}
|
2017-09-07 14:29:32 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
&Expression::Select(ref def) => {
|
|
|
|
let target = &def.val;
|
|
|
|
let def_expr = &def.default;
|
|
|
|
let fields = &def.tuple;
|
2017-07-19 18:42:31 -05:00
|
|
|
// First resolve the target expression.
|
2017-11-05 15:26:52 -06:00
|
|
|
let v = try!(self.eval_expr(target));
|
2017-07-19 18:42:31 -05:00
|
|
|
// Second ensure that the expression resolves to a string.
|
|
|
|
if let &Val::String(ref name) = v.deref() {
|
|
|
|
// Third find the field with that name in the tuple.
|
2017-11-05 15:26:52 -06:00
|
|
|
for &(ref fname, ref val_expr) in fields.iter() {
|
|
|
|
if &fname.fragment == name {
|
2017-07-19 18:42:31 -05:00
|
|
|
// Fourth return the result of evaluating that field.
|
|
|
|
return self.eval_expr(val_expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise return the default
|
2017-11-05 15:26:52 -06:00
|
|
|
return self.eval_expr(def_expr);
|
2017-07-19 18:42:31 -05:00
|
|
|
} else {
|
2017-11-05 15:26:52 -06:00
|
|
|
return Err(Box::new(BuildError::TypeFail(format!("Expected String but got \
|
|
|
|
{} in Select expression",
|
|
|
|
v.type_name()))));
|
2017-07-19 18:42:31 -05:00
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
2017-06-08 22:15:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2017-08-12 14:48:28 -05:00
|
|
|
use super::{Builder, Val, MacroDef, SelectDef, CallDef};
|
|
|
|
use ast::*;
|
2017-06-10 11:09:24 -05:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) {
|
2017-06-10 11:09:24 -05:00
|
|
|
for tpl in cases.drain(0..) {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_div_expr() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Div,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(make_value_node(2, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Int(1)),
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Div,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Float(make_value_node(2.0, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected Float")]
|
|
|
|
fn test_eval_div_expr_fail() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Div,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_mul_expr() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Mul,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(make_value_node(2, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Int(4)),
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Mul,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Float(make_value_node(2.0, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(4.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected Float")]
|
|
|
|
fn test_eval_mul_expr_fail() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Mul,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(20, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_subtract_expr() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Sub,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(make_value_node(2, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(1, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Int(1)),
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Sub,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Float(make_value_node(1.0, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected Float")]
|
|
|
|
fn test_eval_subtract_expr_fail() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Sub,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_add_expr() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(make_value_node(1, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(1, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Int(2)),
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(1.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Float(make_value_node(1.0, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(2.0)),
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::String(make_value_node("foo".to_string(), 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::String(make_value_node("bar".to_string(), 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::String("foobar".to_string())),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected Float")]
|
|
|
|
fn test_eval_add_expr_fail() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-09-21 00:06:43 -05:00
|
|
|
(Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Float(make_value_node(2.0, 1, 1)),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 00:06:43 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Float(1.0)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_simple_expr() {
|
|
|
|
test_expr_to_val(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Int(make_value_node(1, 1, 1))), Val::Int(1)),
|
|
|
|
(Expression::Simple(Value::Float(make_value_node(2.0, 1, 1))), Val::Float(2.0)),
|
|
|
|
(Expression::Simple(Value::String(make_value_node("foo".to_string(), 1, 1))),
|
2017-07-29 12:05:39 -05:00
|
|
|
Val::String("foo".to_string())),
|
2017-11-05 15:26:52 -06:00
|
|
|
(Expression::Simple(Value::Tuple(make_value_node(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("bar", Position{line: 1, column: 1}), Expression::Simple(Value::Int(make_value_node(1, 1, 1))))
|
|
|
|
], 1, 1))),
|
2017-11-08 20:06:16 -06:00
|
|
|
Val::Tuple(vec![(Positioned::new("bar".to_string(), Position{line: 1, column: 1}),
|
2017-11-05 15:26:52 -06:00
|
|
|
Rc::new(Val::Int(1)))])),
|
|
|
|
],
|
|
|
|
Builder::new());
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_simple_lookup_expr() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out
|
|
|
|
.entry(Positioned::new("var1".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Int(1)));
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Symbol(make_value_node("var1".to_string(), 1, 1))), Val::Int(1)),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_simple_lookup_error() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out
|
|
|
|
.entry(Positioned::new("var1".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Int(1)));
|
2017-11-06 21:06:30 -06:00
|
|
|
let expr = Expression::Simple(Value::Symbol(make_value_node("var".to_string(), 1, 1)));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(b.eval_expr(&expr).is_err());
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eval_selector_expr() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out.entry(Positioned::new("var1".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("lvl1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Tuple(
|
2017-06-10 11:09:24 -05:00
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("lvl2".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(3))),
|
2017-06-10 11:09:24 -05:00
|
|
|
]
|
|
|
|
))),
|
|
|
|
])));
|
2017-11-05 15:26:52 -06:00
|
|
|
b.out
|
2017-11-08 20:06:16 -06:00
|
|
|
.entry(Positioned::new("var2".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Int(2)));
|
|
|
|
b.out
|
2017-11-15 22:41:55 -06:00
|
|
|
.entry(Positioned::new("var3".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Tuple(vec![(Positioned::new("lvl1".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}),
|
2017-11-05 15:26:52 -06:00
|
|
|
Rc::new(Val::Int(4)))])));
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var1", Position{line: 1, column: 1})], 1, 1))), Val::Tuple(
|
2017-06-10 11:09:24 -05:00
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("lvl1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Tuple(
|
2017-06-10 11:09:24 -05:00
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("lvl2".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(3))),
|
2017-06-10 11:09:24 -05:00
|
|
|
]
|
|
|
|
))),
|
|
|
|
]
|
|
|
|
)),
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var1", Position{line: 1, column: 1}),
|
|
|
|
Token::new("lvl1", Position{line: 1, column: 1})], 1, 1))),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Tuple(
|
2017-06-10 11:09:24 -05:00
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("lvl2".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(3))),
|
2017-06-10 11:09:24 -05:00
|
|
|
]
|
|
|
|
)),
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var1", Position{line: 1, column: 1}),
|
|
|
|
Token::new("lvl1", Position{line: 1, column: 1}),
|
|
|
|
Token::new("lvl2", Position{line: 1, column: 1})], 1, 1))),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Int(3)),
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var2", Position{line: 1, column: 1})], 1, 1))),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Int(2)),
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var3", Position{line: 1, column: 1}),
|
|
|
|
Token::new("lvl1", Position{line: 1, column: 1})], 1, 1))),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Int(4)),
|
2017-06-10 11:09:24 -05:00
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Unable to find Symbol tpl1")]
|
|
|
|
fn test_expr_copy_no_such_tuple() {
|
2017-08-12 14:48:28 -05:00
|
|
|
let b = Builder::new();
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Expression::Copy(CopyDef{selector: vec![Token::new("tpl1", Position{line: 1, column: 1})], fields: Vec::new(), pos: Position{line: 1, column: 0}}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Tuple(Vec::new())),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected Tuple got Integer")]
|
|
|
|
fn test_expr_copy_not_a_tuple() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out
|
|
|
|
.entry(Positioned::new("tpl1".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Int(1)));
|
2017-06-10 11:09:24 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Expression::Copy(CopyDef{selector: vec![Token::new("tpl1", Position{line: 1, column: 1})], fields: Vec::new(), pos: Position{line: 1, column: 0}}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Tuple(Vec::new())),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Expected type Integer for field fld1 but got String")]
|
|
|
|
fn test_expr_copy_field_type_error() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out.entry(Positioned::new("tpl1".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
2017-06-10 11:09:24 -05:00
|
|
|
])));
|
|
|
|
test_expr_to_val(vec![
|
2017-09-21 08:10:09 -05:00
|
|
|
(Expression::Copy(
|
|
|
|
CopyDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: vec![Token::new("tpl1", Position{line: 1, column: 1})],
|
|
|
|
fields: vec![(Token::new("fld1", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1))))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0}}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Tuple(
|
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 1}), Rc::new(Val::String("2".to_string()))),
|
2017-06-10 11:09:24 -05:00
|
|
|
],
|
|
|
|
)),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expr_copy() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out.entry(Positioned::new("tpl1".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
2017-06-10 11:09:24 -05:00
|
|
|
])));
|
|
|
|
test_expr_to_val(vec![
|
2017-09-21 08:10:09 -05:00
|
|
|
(Expression::Copy(
|
|
|
|
CopyDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: vec![Token::new("tpl1", Position{line: 1, column: 1})],
|
|
|
|
fields: vec![(Token::new("fld2", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1))))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 08:10:09 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
// Add a new field to the copy
|
|
|
|
Val::Tuple(
|
|
|
|
// NOTE(jwall): The order of these is important in order to ensure
|
|
|
|
// that the compare assertion is correct. The ordering has no
|
|
|
|
// semantics though so at some point we should probably be less restrictive.
|
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
|
|
|
(Positioned::new("fld2".to_string(), Position{line: 1, column: 1}), Rc::new(Val::String("2".to_string()))),
|
2017-06-10 11:09:24 -05:00
|
|
|
],
|
|
|
|
)),
|
|
|
|
// Overwrite a field in the copy
|
2017-09-21 08:10:09 -05:00
|
|
|
(Expression::Copy(
|
|
|
|
CopyDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: vec![Token::new("tpl1", Position{line: 1, column: 1})],
|
2017-09-21 08:10:09 -05:00
|
|
|
fields: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("fld1", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::Int(make_value_node(3, 1, 1)))),
|
|
|
|
(Token::new("fld2", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1)))),
|
2017-09-21 08:10:09 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-09-21 08:10:09 -05:00
|
|
|
}),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Tuple(
|
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(3))),
|
|
|
|
(Positioned::new("fld2".to_string(), Position{line: 1, column: 0}), Rc::new(Val::String("2".to_string()))),
|
2017-06-10 11:09:24 -05:00
|
|
|
],
|
|
|
|
)),
|
|
|
|
// The source tuple is still unmodified.
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("tpl1", Position{line: 1, column: 1})], 1, 1))),
|
2017-06-10 11:09:24 -05:00
|
|
|
Val::Tuple(
|
|
|
|
vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("fld1".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
2017-06-10 11:09:24 -05:00
|
|
|
],
|
|
|
|
)),
|
|
|
|
], b);
|
|
|
|
}
|
2017-07-19 18:42:31 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_macro_call() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out.entry(Positioned::new("tstmac".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Macro(MacroDef{
|
|
|
|
argdefs: vec![Positioned::new("arg1".to_string(), Position{line: 1, column: 0})],
|
2017-07-19 18:42:31 -05:00
|
|
|
fields: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}), Expression::Simple(Value::Symbol(make_value_node("arg1".to_string(), 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-07-19 18:42:31 -05:00
|
|
|
})));
|
|
|
|
test_expr_to_val(vec![
|
2017-08-12 14:48:28 -05:00
|
|
|
(Expression::Call(CallDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
macroref: vec![Token::new("tstmac", Position{line: 1, column: 1})],
|
|
|
|
arglist: vec![Expression::Simple(Value::String(make_value_node("bar".to_string(), 1, 1)))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-08-12 14:48:28 -05:00
|
|
|
}),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Tuple(vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("foo".to_string(), Position{line: 1, column: 1}),
|
2017-11-05 15:26:52 -06:00
|
|
|
Rc::new(Val::String("bar".to_string()))),
|
2017-07-19 18:42:31 -05:00
|
|
|
])),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "Unable to find arg1")]
|
|
|
|
fn test_macro_hermetic() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-05 15:26:52 -06:00
|
|
|
b.out
|
2017-11-08 20:06:16 -06:00
|
|
|
.entry(Positioned::new("arg1".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
2017-11-05 15:26:52 -06:00
|
|
|
.or_insert(Rc::new(Val::String("bar".to_string())));
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out.entry(Positioned::new("tstmac".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Macro(MacroDef{
|
|
|
|
argdefs: vec![Positioned::new("arg2".to_string(), Position{line: 1, column: 0})],
|
2017-07-19 18:42:31 -05:00
|
|
|
fields: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}), Expression::Simple(Value::Symbol(make_value_node("arg1".to_string(), 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-07-19 18:42:31 -05:00
|
|
|
})));
|
|
|
|
test_expr_to_val(vec![
|
2017-08-12 14:48:28 -05:00
|
|
|
(Expression::Call(CallDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
macroref: vec![Token::new("tstmac", Position{line: 1, column: 1})],
|
|
|
|
arglist: vec![Expression::Simple(Value::String(make_value_node("bar".to_string(), 1, 1)))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 1},
|
2017-08-12 14:48:28 -05:00
|
|
|
}),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Tuple(vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("foo".to_string(), Position{line: 1, column: 0}), Rc::new(Val::String("bar".to_string()))),
|
2017-07-19 18:42:31 -05:00
|
|
|
])),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_select_expr() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-05 15:26:52 -06:00
|
|
|
b.out
|
2017-11-08 20:06:16 -06:00
|
|
|
.entry(Positioned::new("foo".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
2017-11-05 15:26:52 -06:00
|
|
|
.or_insert(Rc::new(Val::String("bar".to_string())));
|
|
|
|
b.out
|
2017-11-08 20:06:16 -06:00
|
|
|
.entry(Positioned::new("baz".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
2017-11-05 15:26:52 -06:00
|
|
|
.or_insert(Rc::new(Val::String("boo".to_string())));
|
2017-07-19 18:42:31 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-08-12 14:48:28 -05:00
|
|
|
(Expression::Select(SelectDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
val: Box::new(Expression::Simple(Value::Symbol(make_value_node("foo".to_string(), 1, 1)))),
|
|
|
|
default: Box::new(Expression::Simple(Value::Int(make_value_node(1, 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
tuple: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}), Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1)))),
|
|
|
|
(Token::new("bar", Position{line: 1, column: 1}), Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-08-12 14:48:28 -05:00
|
|
|
}),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Int(2)),
|
2017-08-12 14:48:28 -05:00
|
|
|
(Expression::Select(SelectDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
val: Box::new(Expression::Simple(Value::Symbol(make_value_node("baz".to_string(), 1, 1)))),
|
|
|
|
default: Box::new(Expression::Simple(Value::Int(make_value_node(1, 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
tuple: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("bar", Position{line: 1, column: 1}), Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
|
|
|
(Token::new("quux", Position{line: 1, column: 1}), Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-08-12 14:48:28 -05:00
|
|
|
}),
|
2017-07-19 18:42:31 -05:00
|
|
|
// If the field doesn't exist then we get the default.
|
|
|
|
Val::Int(1)),
|
|
|
|
], b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected ="Expected String but got Integer in Select expression")]
|
|
|
|
fn test_select_expr_not_a_string() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.out
|
|
|
|
.entry(Positioned::new("foo".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
}))
|
|
|
|
.or_insert(Rc::new(Val::Int(4)));
|
2017-07-19 18:42:31 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-08-12 14:48:28 -05:00
|
|
|
(Expression::Select(SelectDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
val: Box::new(Expression::Simple(Value::Symbol(make_value_node("foo".to_string(), 1, 1)))),
|
|
|
|
default: Box::new(Expression::Simple(Value::Int(make_value_node(1, 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
tuple: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("bar", Position{line: 1, column: 1}), Expression::Simple(Value::Int(make_value_node(2, 1, 1)))),
|
|
|
|
(Token::new("quux", Position{line: 1, column: 1}), Expression::Simple(Value::String(make_value_node("2".to_string(), 1, 1)))),
|
2017-07-19 18:42:31 -05:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-08-12 14:48:28 -05:00
|
|
|
}),
|
2017-07-19 18:42:31 -05:00
|
|
|
Val::Int(2)),
|
|
|
|
], b);
|
|
|
|
}
|
2017-07-25 20:45:58 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_let_statement() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-05 15:26:52 -06:00
|
|
|
let stmt = Statement::Let {
|
2017-11-08 18:55:10 -06:00
|
|
|
name: Token::new("foo",
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 1,
|
|
|
|
}),
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::String(make_value_node("bar".to_string(), 1, 1))),
|
2017-11-05 15:26:52 -06:00
|
|
|
};
|
|
|
|
b.build_stmt(&stmt).unwrap();
|
2017-07-25 20:45:58 -05:00
|
|
|
test_expr_to_val(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Symbol(make_value_node("foo".to_string(), 1, 1))),
|
2017-07-25 20:45:58 -05:00
|
|
|
Val::String("bar".to_string())),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-07-25 20:45:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_build_file_string() {
|
|
|
|
let mut b = Builder::new();
|
2017-08-12 14:48:28 -05:00
|
|
|
b.build_file_string("foo.ucg", "let foo = 1;".to_string()).unwrap();
|
2017-11-08 20:06:16 -06:00
|
|
|
let key = Positioned::new("foo".to_string(),
|
|
|
|
Position {
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
});
|
2017-11-06 21:06:30 -06:00
|
|
|
assert!(b.out.contains_key(&key));
|
2017-07-25 20:45:58 -05:00
|
|
|
}
|
2017-07-29 12:17:38 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_asset_symbol_lookups() {
|
|
|
|
let mut b = Builder::new();
|
2017-11-08 20:06:16 -06:00
|
|
|
b.assets.entry(Positioned::new("foo".to_string(), Position{line: 1, column: 0})).or_insert(Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("bar".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("quux".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
2017-07-29 12:17:38 -05:00
|
|
|
]))),
|
|
|
|
])));
|
|
|
|
test_expr_to_val(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Expression::Simple(Value::Symbol(make_value_node("foo".to_string(), 1, 1))),
|
2017-07-29 12:17:38 -05:00
|
|
|
Val::Tuple(vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
(Positioned::new("bar".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Tuple(vec![
|
|
|
|
(Positioned::new("quux".to_string(), Position{line: 1, column: 0}), Rc::new(Val::Int(1))),
|
2017-07-29 12:17:38 -05:00
|
|
|
]))),
|
|
|
|
])),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
|
|
|
b);
|
2017-07-29 12:17:38 -05:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|