mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-24 18:39:50 -04:00
Basic Evaluation Logic for the Builder.
This commit is contained in:
parent
72d03e21dd
commit
8a5c121f26
619
src/build.rs
619
src/build.rs
@ -1,23 +1,30 @@
|
|||||||
use parse::{parse, Statement, Expression, Value, FieldList};
|
use parse::{parse, Statement, Expression, Value, FieldList, SelectorList};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::convert::From;
|
use std::collections::hash_map::Entry;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Display,Formatter};
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug,PartialEq)]
|
#[derive(Debug,PartialEq)]
|
||||||
pub enum BuildError {
|
pub enum BuildError {
|
||||||
TypeFail(msg: String) {
|
TypeFail(msg: String) {
|
||||||
description("Eval Error")
|
description("Type Error")
|
||||||
display("Eval Error {}", msg)
|
display("Type Error {}", msg)
|
||||||
|
}
|
||||||
|
Unsupported(msg: String) {
|
||||||
|
description("Unsupported Operation")
|
||||||
|
display("Unsupported Operation {}", msg)
|
||||||
}
|
}
|
||||||
NoSuchSymbol(msg: String) {
|
NoSuchSymbol(msg: String) {
|
||||||
description("Eval Error")
|
description("Eval Error")
|
||||||
display("Eval Error {}", msg)
|
display("No Such Variable {}", msg)
|
||||||
}
|
}
|
||||||
TODO(msg: String) {
|
TODO(msg: String) {
|
||||||
description("Eval Error")
|
description("TODO Error")
|
||||||
display("Eval Error {}", msg)
|
display("TODO Error {}", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,26 +33,75 @@ quick_error! {
|
|||||||
type BuildResult = Result<(), Box<Error>>;
|
type BuildResult = Result<(), Box<Error>>;
|
||||||
|
|
||||||
/// Val is the type of a value for a field in a Tuple.
|
/// Val is the type of a value for a field in a Tuple.
|
||||||
#[derive(PartialEq,Debug)]
|
#[derive(PartialEq,Debug,Clone)]
|
||||||
pub enum Val<'a> {
|
pub enum Val<'a> {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
String(&'a str),
|
String(String),
|
||||||
Symbol(&'a str),
|
Tuple(Vec<(&'a str, Rc<Val<'a>>)>),
|
||||||
Tuple(FieldList<'a>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<Value<'a>> for Val<'a> {
|
impl<'a> Val<'a> {
|
||||||
fn from(v: Value<'a>) -> Self {
|
|
||||||
match v {
|
pub fn type_name(&self) -> String {
|
||||||
Value::Int(i) => Val::Int(i),
|
match self {
|
||||||
Value::Float(f) => Val::Float(f),
|
&Val::Int(_) => "Integer".to_string(),
|
||||||
Value::String(s) => Val::String(s),
|
&Val::Float(_) => "Float".to_string(),
|
||||||
Value::Symbol(s) => Val::Symbol(s),
|
&Val::String(_) => "String".to_string(),
|
||||||
Value::Tuple(ref fields) => {
|
&Val::Tuple(_) => "Tuple".to_string(),
|
||||||
Val::Tuple(Vec::new())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_equal(&self, target: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
&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 },
|
||||||
|
&Val::Tuple(_) => if let &Val::Tuple(_) = target { true } else { false },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fields(&self) -> Option<&Vec<(&'a str, Rc<Val<'a>>)>> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Val<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
// TODO(jwall): These should render better than this.
|
||||||
|
write!(f, "{}", self.type_name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +111,7 @@ pub struct Tuple<'a>(HashMap<&'a str, Val<'a>>);
|
|||||||
|
|
||||||
/// Builder parses one or more statements into a out Tuple.
|
/// Builder parses one or more statements into a out Tuple.
|
||||||
pub struct Builder<'a> {
|
pub struct Builder<'a> {
|
||||||
/// env is the set of key value pairs provided at build time.
|
/// env is the immutable set of key value pairs provided at build time.
|
||||||
env: HashMap<&'a str, &'a str>,
|
env: HashMap<&'a str, &'a str>,
|
||||||
/// assets are other parsed files from import statements. They
|
/// assets are other parsed files from import statements. They
|
||||||
/// are keyed by the normalized import path. This acts as a cache
|
/// are keyed by the normalized import path. This acts as a cache
|
||||||
@ -63,28 +119,66 @@ pub struct Builder<'a> {
|
|||||||
/// multiple times.
|
/// multiple times.
|
||||||
assets: HashMap<&'a str, Tuple<'a>>,
|
assets: HashMap<&'a str, Tuple<'a>>,
|
||||||
/// out is our built output.
|
/// out is our built output.
|
||||||
out: Tuple<'a>,
|
out: HashMap<&'a str, Rc<Val<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
/// new_builder constructs Builder with initialized fields ready to parse.
|
/// new_builder constructs Builder with initialized fields ready to parse.
|
||||||
pub fn new_builder() -> Self {
|
fn from(&self, v: Value<'a>) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
match v {
|
||||||
|
Value::Int(i) => Ok(Rc::new(Val::Int(i))),
|
||||||
|
Value::Float(f) => Ok(Rc::new(Val::Float(f))),
|
||||||
|
Value::String(s) => Ok(Rc::new(Val::String(s.to_string()))),
|
||||||
|
Value::Symbol(s) => {
|
||||||
|
self.lookup_sym(s).ok_or(Box::new(
|
||||||
|
BuildError::NoSuchSymbol(s.to_string())))
|
||||||
|
},
|
||||||
|
Value::Tuple(fields) => {
|
||||||
|
// TODO(jwall): We need to resolve the expressions here.
|
||||||
|
// TODO(jwall): Don't forget we want a stable order for the fields
|
||||||
|
// in a tuple to make Vec comparisons easier later on.
|
||||||
|
Err(Box::new(
|
||||||
|
BuildError::TODO(
|
||||||
|
"Value::Tuple to Val::Tuple is not yet impolemented.".to_string())))
|
||||||
|
},
|
||||||
|
Value::Selector(selector_list) => {
|
||||||
|
self.lookup_selector(selector_list)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
Builder {
|
Builder {
|
||||||
env: HashMap::new(),
|
env: HashMap::new(),
|
||||||
assets: HashMap::new(),
|
assets: HashMap::new(),
|
||||||
out: Tuple(HashMap::new()),
|
out: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_dir(&self, name: &str) -> BuildResult {
|
pub fn build_dir(&mut self, name: &str) -> BuildResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_file(&self, name: &str) -> BuildResult {
|
pub fn build_file(&mut self, name: &str) -> BuildResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&self, ast: Vec<Statement>) -> BuildResult {
|
pub fn build(&mut self, ast: Vec<Statement>) -> BuildResult {
|
||||||
// TODO(jwall):
|
// TODO(jwall):
|
||||||
for stmt in ast.iter() {
|
for stmt in ast.iter() {
|
||||||
self.build_stmt(stmt);
|
self.build_stmt(stmt);
|
||||||
@ -92,45 +186,223 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_expr(&'a self, expr: Expression<'a>) -> Result<Val, Box<Error>> {
|
fn lookup_sym(&'a self, sym: &str) -> Option<Rc<Val>> {
|
||||||
|
if self.out.contains_key(sym) {
|
||||||
|
Some(self.out[sym].clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_in_fieldlist(target: &str, fs: &Vec<(&str, Rc<Val<'a>>)>) -> Option<Rc<Val<'a>>> {
|
||||||
|
for (key, val) in fs.iter().cloned() {
|
||||||
|
if target == key {
|
||||||
|
return Some(val.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_selector(&'a self, sl: SelectorList) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
let len = sl.len();
|
||||||
|
if len > 0 {
|
||||||
|
println!("Looking up symbol {}", sl[0]);
|
||||||
|
if let Some(v) = self.lookup_sym(sl[0]) {
|
||||||
|
let mut it = sl.iter().skip(1).peekable();
|
||||||
|
if it.peek().is_none() {
|
||||||
|
return Ok(v.clone());
|
||||||
|
}
|
||||||
|
if let &Val::Tuple(ref fs) = v.as_ref() {
|
||||||
|
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() {
|
||||||
|
return Err(Box::new(BuildError::NoSuchSymbol(
|
||||||
|
format!("Attempted to dereference non-tuple {:?} at field {}.", sl, k))));
|
||||||
|
}
|
||||||
|
// This unwrap is safe because we already checked for
|
||||||
|
// Tuple above.
|
||||||
|
let fs = vref.get_fields().unwrap();
|
||||||
|
if let Some(vv) = Self::find_in_fieldlist(k, fs) {
|
||||||
|
if vv.is_tuple() {
|
||||||
|
stack.push_back(vv.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if it.peek().is_some() {
|
||||||
|
return Err(Box::new(BuildError::NoSuchSymbol(
|
||||||
|
format!("Unable to match selector path {:?}", sl))));
|
||||||
|
} else {
|
||||||
|
return Ok(vv.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO(jwall): A better error for this would be nice.
|
||||||
|
return Err(Box::new(BuildError::NoSuchSymbol(
|
||||||
|
format!("Unable to match selector path {:?}", sl))));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Err(Box::new(BuildError::TypeFail(
|
||||||
|
format!("{} is not a Tuple", sl[0]))));
|
||||||
|
}
|
||||||
|
return Err(Box::new(BuildError::NoSuchSymbol(
|
||||||
|
format!("Unable to find Symbol {}", sl[0]))));
|
||||||
|
}
|
||||||
|
return Err(Box::new(BuildError::NoSuchSymbol(
|
||||||
|
"Attempted to lookup an empty selector".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// eval_expr evals a single Expression in the context of a running Builder.
|
||||||
|
// It does not mutate the builders collected state at all.
|
||||||
|
pub fn eval_expr(&'a self, expr: Expression<'a>) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::Simple(val) => {
|
Expression::Simple(val) => {
|
||||||
Ok(Val::from(val))
|
self.from(val)
|
||||||
},
|
},
|
||||||
Expression::Add(v, expr) => {
|
Expression::Add(v, expr) => {
|
||||||
Err(Box::new(
|
let expr_result = try!(self.eval_expr(*expr));
|
||||||
BuildError::TODO(
|
let v = try!(self.from(*v));
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
match *v {
|
||||||
|
Val::Int(i) => {
|
||||||
|
eval_binary_expr!(&Val::Int(ii), expr_result,
|
||||||
|
Val::Int(i + ii), "Integer")
|
||||||
|
},
|
||||||
|
Val::Float(f) => {
|
||||||
|
eval_binary_expr!(&Val::Float(ff), expr_result,
|
||||||
|
Val::Float(f + ff), "Float")
|
||||||
|
},
|
||||||
|
Val::String(ref s) => {
|
||||||
|
match expr_result.as_ref() {
|
||||||
|
&Val::String(ref ss) => {
|
||||||
|
return Ok(Rc::new(
|
||||||
|
Val::String([s.to_string(), ss.clone()].concat())))
|
||||||
|
},
|
||||||
|
val => {
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::TypeFail(
|
||||||
|
format!("Expected String but got {:?}", val))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ref expr => {
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::Unsupported(
|
||||||
|
format!("{} does not support the '+' operation", expr.type_name()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Expression::Sub(v, expr) => {
|
Expression::Sub(v, expr) => {
|
||||||
Err(Box::new(
|
let expr_result = try!(self.eval_expr(*expr));
|
||||||
BuildError::TODO(
|
let v = try!(self.from(*v));
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
match *v {
|
||||||
|
Val::Int(i) => {
|
||||||
|
eval_binary_expr!(&Val::Int(ii), expr_result,
|
||||||
|
Val::Int(i - ii), "Integer")
|
||||||
|
},
|
||||||
|
Val::Float(f) => {
|
||||||
|
eval_binary_expr!(&Val::Float(ff), expr_result,
|
||||||
|
Val::Float(f - ff), "Float")
|
||||||
|
},
|
||||||
|
ref expr => {
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::Unsupported(
|
||||||
|
format!("{} does not support the '-' operation", expr.type_name()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Expression::Mul(v, expr) => {
|
Expression::Mul(v, expr) => {
|
||||||
Err(Box::new(
|
let expr_result = try!(self.eval_expr(*expr));
|
||||||
BuildError::TODO(
|
let v = try!(self.from(*v));
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
match *v {
|
||||||
|
Val::Int(i) => {
|
||||||
|
eval_binary_expr!(&Val::Int(ii), expr_result,
|
||||||
|
Val::Int(i * ii), "Integer")
|
||||||
|
},
|
||||||
|
Val::Float(f) => {
|
||||||
|
eval_binary_expr!(&Val::Float(ff), expr_result,
|
||||||
|
Val::Float(f * ff), "Float")
|
||||||
|
},
|
||||||
|
ref expr => {
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::Unsupported(
|
||||||
|
format!("{} does not support the '*' operation", expr.type_name()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Expression::Div(v, expr) => {
|
Expression::Div(v, expr) => {
|
||||||
Err(Box::new(
|
let expr_result = try!(self.eval_expr(*expr));
|
||||||
BuildError::TODO(
|
let v = try!(self.from(*v));
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
match *v {
|
||||||
|
Val::Int(i) => {
|
||||||
|
eval_binary_expr!(&Val::Int(ii), expr_result,
|
||||||
|
Val::Int(i / ii), "Integer")
|
||||||
},
|
},
|
||||||
Expression::Copy(sel, fields) => {
|
Val::Float(f) => {
|
||||||
Err(Box::new(
|
eval_binary_expr!(&Val::Float(ff), expr_result,
|
||||||
BuildError::TODO(
|
Val::Float(f / ff), "Float")
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
|
||||||
},
|
},
|
||||||
Expression::Selector(sel) => {
|
ref expr => {
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::Unsupported(
|
||||||
|
format!("{} does not support the '*' operation", expr.type_name()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expression::Copy(sel, mut fields) => {
|
||||||
|
let v = try!(self.lookup_selector(sel));
|
||||||
|
if let Val::Tuple(ref src_fields) = *v {
|
||||||
|
let mut m = HashMap::<&str, Rc<Val>>::new();
|
||||||
|
// loop through fields and build up a hasmap
|
||||||
|
for &(ref key, ref val) in src_fields.iter() {
|
||||||
|
if let Entry::Vacant(v) = m.entry(*key) {
|
||||||
|
v.insert(val.clone());
|
||||||
|
} else {
|
||||||
|
// TODO(jwall): Is this an error?
|
||||||
|
return Err(Box::new(
|
||||||
|
BuildError::TypeFail(
|
||||||
|
format!("Duplicate field: {} in tuple", *key))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (key, val) in fields.drain(0..) {
|
||||||
|
let expr_result = try!(self.eval_expr(val));
|
||||||
|
match m.entry(key) {
|
||||||
|
Entry::Vacant(mut v) => {
|
||||||
|
v.insert(expr_result);
|
||||||
|
},
|
||||||
|
Entry::Occupied(mut v) => {
|
||||||
|
// Ensure that the new type matches the old type.
|
||||||
|
// TODO(jwall): This copy is ugly but I don't think it's possible
|
||||||
|
// to both compare and replace this at the same time.
|
||||||
|
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 {}",
|
||||||
|
src_val.type_name(), key, expr_result.type_name()))));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let mut new_fields: Vec<(&str, Rc<Val>)> = m.drain().collect();
|
||||||
|
// 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.
|
||||||
|
new_fields.sort_by(|a, b| a.0.cmp(b.0));
|
||||||
|
return Ok(Rc::new(Val::Tuple(new_fields)));
|
||||||
|
}
|
||||||
Err(Box::new(
|
Err(Box::new(
|
||||||
BuildError::TODO(
|
BuildError::TypeFail(
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
format!("Expected Tuple got {}", v))))
|
||||||
},
|
},
|
||||||
Expression::Grouped(expr) => {
|
Expression::Grouped(expr) => {
|
||||||
Err(Box::new(
|
return self.eval_expr(*expr);
|
||||||
BuildError::TODO(
|
|
||||||
"TODO(jwall): Unimplemented Expression".to_string())))
|
|
||||||
},
|
},
|
||||||
Expression::Call{lambda: sel, arglist: args} => {
|
Expression::Call{lambda: sel, arglist: args} => {
|
||||||
Err(Box::new(
|
Err(Box::new(
|
||||||
@ -165,3 +437,246 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{Builder,Val};
|
||||||
|
use parse::{Expression, Value};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
fn test_expr_to_val(mut cases: Vec<(Expression,Val)>, b: Builder) {
|
||||||
|
for tpl in cases.drain(0..) {
|
||||||
|
assert_eq!(b.eval_expr(tpl.0).unwrap(), Rc::new(tpl.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_div_expr() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Div(Box::new(Value::Int(2)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Int(1)),
|
||||||
|
(Expression::Div(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Float(2.0)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Expected Float")]
|
||||||
|
fn test_eval_div_expr_fail() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Div(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_mul_expr() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Mul(Box::new(Value::Int(2)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Int(4)),
|
||||||
|
(Expression::Mul(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Float(2.0)))),
|
||||||
|
Val::Float(4.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Expected Float")]
|
||||||
|
fn test_eval_mul_expr_fail() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Mul(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_subtract_expr() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Sub(Box::new(Value::Int(2)), Box::new(Expression::Simple(Value::Int(1)))),
|
||||||
|
Val::Int(1)),
|
||||||
|
(Expression::Sub(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Float(1.0)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Expected Float")]
|
||||||
|
fn test_eval_subtract_expr_fail() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Sub(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_add_expr() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Add(Box::new(Value::Int(1)), Box::new(Expression::Simple(Value::Int(1)))),
|
||||||
|
Val::Int(2)),
|
||||||
|
(Expression::Add(Box::new(Value::Float(1.0)), Box::new(Expression::Simple(Value::Float(1.0)))),
|
||||||
|
Val::Float(2.0)),
|
||||||
|
(Expression::Add(Box::new(Value::String("foo")), Box::new(Expression::Simple(Value::String("bar")))),
|
||||||
|
Val::String("foobar".to_string())),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Expected Float")]
|
||||||
|
fn test_eval_add_expr_fail() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Add(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Int(2)))),
|
||||||
|
Val::Float(1.0)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_simple_expr() {
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Simple(Value::Int(1)), Val::Int(1)),
|
||||||
|
(Expression::Simple(Value::Float(2.0)), Val::Float(2.0)),
|
||||||
|
(Expression::Simple(Value::String("foo")), Val::String("foo".to_string())),
|
||||||
|
], Builder::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_simple_lookup_expr() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
b.out.entry("var1").or_insert(Rc::new(Val::Int(1)));
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Simple(Value::Symbol("var1")), Val::Int(1)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_simple_lookup_error() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
b.out.entry("var1").or_insert(Rc::new(Val::Int(1)));
|
||||||
|
assert!(b.eval_expr(Expression::Simple(Value::Symbol("var"))).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_selector_expr() {
|
||||||
|
// TODO(jwall): Tests for this expression.
|
||||||
|
let mut b = Builder::new();
|
||||||
|
b.out.entry("var1").or_insert(Rc::new(Val::Tuple(vec![
|
||||||
|
("lvl1", Rc::new(Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("lvl2", Rc::new(Val::Int(3))),
|
||||||
|
]
|
||||||
|
))),
|
||||||
|
])));
|
||||||
|
b.out.entry("var2").or_insert(Rc::new(Val::Int(2)));
|
||||||
|
b.out.entry("var3").or_insert(Rc::new(Val::Tuple(vec![
|
||||||
|
("lvl1", Rc::new(Val::Int(4)))
|
||||||
|
])));
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Simple(Value::Selector(vec!["var1"])), Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("lvl1", Rc::new(Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("lvl2", Rc::new(Val::Int(3))),
|
||||||
|
]
|
||||||
|
))),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
(Expression::Simple(Value::Selector(vec!["var1","lvl1"])), Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("lvl2", Rc::new(Val::Int(3))),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
(Expression::Simple(Value::Selector(vec!["var1","lvl1", "lvl2"])), Val::Int(3)),
|
||||||
|
(Expression::Simple(Value::Selector(vec!["var2"])), Val::Int(2)),
|
||||||
|
(Expression::Simple(Value::Selector(vec!["var3", "lvl1"])), Val::Int(4)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Unable to find Symbol tpl1")]
|
||||||
|
fn test_expr_copy_no_such_tuple() {
|
||||||
|
let mut b = Builder::new();
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Copy(vec!["tpl1"], Vec::new()),
|
||||||
|
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();
|
||||||
|
b.out.entry("tpl1").or_insert(Rc::new(Val::Int(1)));
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Copy(vec!["tpl1"], Vec::new()),
|
||||||
|
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();
|
||||||
|
b.out.entry("tpl1").or_insert(Rc::new(Val::Tuple(vec![
|
||||||
|
("fld1", Rc::new(Val::Int(1))),
|
||||||
|
])));
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Copy(vec!["tpl1"],
|
||||||
|
vec![("fld1", Expression::Simple(Value::String("2")))]),
|
||||||
|
Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("fld1", Rc::new(Val::String("2".to_string()))),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(jwall): What about the duplicate field error?
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expr_copy() {
|
||||||
|
// TODO(jwall): Tests for this expression.
|
||||||
|
let mut b = Builder::new();
|
||||||
|
b.out.entry("tpl1").or_insert(Rc::new(Val::Tuple(vec![
|
||||||
|
("fld1", Rc::new(Val::Int(1))),
|
||||||
|
])));
|
||||||
|
test_expr_to_val(vec![
|
||||||
|
(Expression::Copy(vec!["tpl1"],
|
||||||
|
vec![("fld2", Expression::Simple(Value::String("2")))]),
|
||||||
|
// 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![
|
||||||
|
("fld1", Rc::new(Val::Int(1))),
|
||||||
|
("fld2", Rc::new(Val::String("2".to_string()))),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
// Overwrite a field in the copy
|
||||||
|
(Expression::Copy(vec!["tpl1"],
|
||||||
|
vec![
|
||||||
|
("fld1", Expression::Simple(Value::Int(3))),
|
||||||
|
("fld2", Expression::Simple(Value::String("2"))),
|
||||||
|
]),
|
||||||
|
Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("fld1", Rc::new(Val::Int(3))),
|
||||||
|
("fld2", Rc::new(Val::String("2".to_string()))),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
// The source tuple is still unmodified.
|
||||||
|
(Expression::Simple(Value::Selector(vec!["tpl1"])),
|
||||||
|
Val::Tuple(
|
||||||
|
vec![
|
||||||
|
("fld1", Rc::new(Val::Int(1))),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
], b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
770
src/parse.rs
770
src/parse.rs
@ -8,12 +8,11 @@ quick_error! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use nom::{alpha, is_alphanumeric, digit, IResult};
|
use nom::{alpha, is_alphanumeric, digit};
|
||||||
|
|
||||||
type ParseResult<O> = Result<O, Box<Error>>;
|
type ParseResult<O> = Result<O, Box<Error>>;
|
||||||
|
|
||||||
@ -21,17 +20,33 @@ pub type FieldList<'a> = Vec<(&'a str, Expression<'a>)>; // str is expected to b
|
|||||||
pub type SelectorList<'a> = Vec<&'a str>; // str is expected to always be a symbol.
|
pub type SelectorList<'a> = Vec<&'a str>; // str is expected to always be a symbol.
|
||||||
|
|
||||||
/// Value represents a Value in the UCG parsed AST.
|
/// Value represents a Value in the UCG parsed AST.
|
||||||
#[derive(Debug,PartialEq)]
|
#[derive(Debug,PartialEq,Clone)]
|
||||||
pub enum Value<'a> {
|
pub enum Value<'a> {
|
||||||
|
// Constant Values
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
String(&'a str),
|
String(&'a str),
|
||||||
Symbol(&'a str),
|
Symbol(&'a str),
|
||||||
|
// Complex Values
|
||||||
Tuple(FieldList<'a>),
|
Tuple(FieldList<'a>),
|
||||||
|
Selector(SelectorList<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Value<'a> {
|
||||||
|
pub fn type_name(&self) -> String {
|
||||||
|
match self {
|
||||||
|
&Value::Int(_) => "Integer".to_string(),
|
||||||
|
&Value::Float(_) => "Float".to_string(),
|
||||||
|
&Value::String(_) => "String".to_string(),
|
||||||
|
&Value::Symbol(_) => "Symbol".to_string(),
|
||||||
|
&Value::Tuple(_) => "Tuple".to_string(),
|
||||||
|
&Value::Selector(_) => "Selector".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expression encodes an expression. Expressions compute a value from operands.
|
/// Expression encodes an expression. Expressions compute a value from operands.
|
||||||
#[derive(Debug,PartialEq)]
|
#[derive(Debug,PartialEq,Clone)]
|
||||||
pub enum Expression<'a> {
|
pub enum Expression<'a> {
|
||||||
// Base Expression
|
// Base Expression
|
||||||
Simple(Value<'a>),
|
Simple(Value<'a>),
|
||||||
@ -44,7 +59,6 @@ pub enum Expression<'a> {
|
|||||||
|
|
||||||
// Complex Expressions
|
// Complex Expressions
|
||||||
Copy(SelectorList<'a>, FieldList<'a>),
|
Copy(SelectorList<'a>, FieldList<'a>),
|
||||||
Selector(SelectorList<'a>),
|
|
||||||
Grouped(Box<Expression<'a>>),
|
Grouped(Box<Expression<'a>>),
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
@ -83,7 +97,6 @@ pub enum Statement<'a> {
|
|||||||
|
|
||||||
// sentinels and punctuation
|
// sentinels and punctuation
|
||||||
named!(doublequote, tag!("\""));
|
named!(doublequote, tag!("\""));
|
||||||
named!(singlequote, tag!("'"));
|
|
||||||
named!(comma, tag!(","));
|
named!(comma, tag!(","));
|
||||||
named!(lbrace, tag!("{"));
|
named!(lbrace, tag!("{"));
|
||||||
named!(rbrace, tag!("}"));
|
named!(rbrace, tag!("}"));
|
||||||
@ -95,7 +108,6 @@ named!(minus, tag!("-"));
|
|||||||
named!(mul, tag!("*"));
|
named!(mul, tag!("*"));
|
||||||
named!(div, tag!("/"));
|
named!(div, tag!("/"));
|
||||||
named!(equal, tag!("="));
|
named!(equal, tag!("="));
|
||||||
named!(slashes, tag!("//"));
|
|
||||||
named!(semicolon, tag!(";"));
|
named!(semicolon, tag!(";"));
|
||||||
named!(fatcomma, tag!("=>"));
|
named!(fatcomma, tag!("=>"));
|
||||||
|
|
||||||
@ -194,20 +206,6 @@ named!(number<Value>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_number_parsing() {
|
|
||||||
assert!(number(&b"."[..]).is_err() );
|
|
||||||
assert!(number(&b". "[..]).is_err() );
|
|
||||||
assert_eq!(number(&b"1.0"[..]),
|
|
||||||
IResult::Done(&b""[..], Value::Float(1.0)) );
|
|
||||||
assert_eq!(number(&b"1."[..]),
|
|
||||||
IResult::Done(&b""[..], Value::Float(1.0)) );
|
|
||||||
assert_eq!(number(&b"1"[..]),
|
|
||||||
IResult::Done(&b""[..], Value::Int(1)) );
|
|
||||||
assert_eq!(number(&b".1"[..]),
|
|
||||||
IResult::Done(&b""[..], Value::Float(0.1)) );
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(value<Value>, alt!(number | quoted_value | symbol | tuple));
|
named!(value<Value>, alt!(number | quoted_value | symbol | tuple));
|
||||||
|
|
||||||
named!(
|
named!(
|
||||||
@ -221,21 +219,6 @@ named!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_field_value_parse() {
|
|
||||||
assert!(field_value(&b"foo"[..]).is_incomplete() );
|
|
||||||
assert!(field_value(&b"foo ="[..]).is_incomplete() );
|
|
||||||
|
|
||||||
assert_eq!(field_value(&b"foo = 1"[..]),
|
|
||||||
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Int(1)))) );
|
|
||||||
assert_eq!(field_value(&b"foo = \"1\""[..]),
|
|
||||||
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::String("1")))) );
|
|
||||||
assert_eq!(field_value(&b"foo = bar"[..]),
|
|
||||||
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Symbol("bar")))) );
|
|
||||||
assert_eq!(field_value(&b"foo = bar "[..]),
|
|
||||||
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Symbol("bar")))) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to make the return types work for down below.
|
// Helper function to make the return types work for down below.
|
||||||
fn vec_to_tuple<'a>(v: FieldList<'a>) -> ParseResult<Value<'a>> {
|
fn vec_to_tuple<'a>(v: FieldList<'a>) -> ParseResult<Value<'a>> {
|
||||||
Ok(Value::Tuple(v))
|
Ok(Value::Tuple(v))
|
||||||
@ -255,43 +238,6 @@ named!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tuple_parse() {
|
|
||||||
assert!(tuple(&b"{"[..]).is_incomplete() );
|
|
||||||
assert!(tuple(&b"{ foo"[..]).is_incomplete() );
|
|
||||||
assert!(tuple(&b"{ foo ="[..]).is_incomplete() );
|
|
||||||
assert!(tuple(&b"{ foo = 1"[..]).is_incomplete() );
|
|
||||||
assert!(tuple(&b"{ foo = 1,"[..]).is_err() );
|
|
||||||
assert!(tuple(&b"{ foo = 1, bar ="[..]).is_err() );
|
|
||||||
|
|
||||||
assert_eq!(tuple(&b"{ }"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Value::Tuple(
|
|
||||||
vec![])));
|
|
||||||
|
|
||||||
assert_eq!(tuple(&b"{ foo = 1 }"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Value::Tuple(
|
|
||||||
vec![
|
|
||||||
("foo", Expression::Simple(Value::Int(1)))
|
|
||||||
])));
|
|
||||||
|
|
||||||
assert_eq!(tuple(&b"{ foo = 1, bar = \"1\" }"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Value::Tuple(
|
|
||||||
vec![
|
|
||||||
("foo", Expression::Simple(Value::Int(1))),
|
|
||||||
("bar", Expression::Simple(Value::String("1")))
|
|
||||||
])));
|
|
||||||
assert_eq!(tuple(&b"{ foo = 1, bar = {} }"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Value::Tuple(
|
|
||||||
vec![
|
|
||||||
("foo", Expression::Simple(Value::Int(1))),
|
|
||||||
("bar", Expression::Simple(Value::Tuple(Vec::new())))
|
|
||||||
])));
|
|
||||||
}
|
|
||||||
|
|
||||||
// keywords
|
// keywords
|
||||||
named!(let_word, tag!("let"));
|
named!(let_word, tag!("let"));
|
||||||
named!(select_word, tag!("select"));
|
named!(select_word, tag!("select"));
|
||||||
@ -385,32 +331,6 @@ named!(grouped_expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_grouped_expression_parse() {
|
|
||||||
assert!(grouped_expression(&b"foo"[..]).is_err() );
|
|
||||||
assert!(grouped_expression(&b"(foo"[..]).is_incomplete() );
|
|
||||||
assert_eq!(grouped_expression(&b"(foo)"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Grouped(
|
|
||||||
Box::new(
|
|
||||||
Expression::Simple(
|
|
||||||
Value::Symbol("foo")))))
|
|
||||||
);
|
|
||||||
assert_eq!(grouped_expression(&b"(1 + 1)"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Grouped(
|
|
||||||
Box::new(
|
|
||||||
Expression::Add(
|
|
||||||
Box::new(Value::Int(1)),
|
|
||||||
Box::new(Expression::Simple(
|
|
||||||
Value::Int(1)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(selector_list<SelectorList>, separated_nonempty_list!(dot, field));
|
named!(selector_list<SelectorList>, separated_nonempty_list!(dot, field));
|
||||||
|
|
||||||
fn tuple_to_copy<'a>(t: (SelectorList<'a>, FieldList<'a>)) -> ParseResult<Expression<'a>> {
|
fn tuple_to_copy<'a>(t: (SelectorList<'a>, FieldList<'a>)) -> ParseResult<Expression<'a>> {
|
||||||
@ -430,25 +350,6 @@ named!(copy_expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_copy_parse() {
|
|
||||||
assert!(copy_expression(&b"{}"[..]).is_err() );
|
|
||||||
assert!(copy_expression(&b"foo"[..]).is_incomplete() );
|
|
||||||
assert!(copy_expression(&b"foo{"[..]).is_incomplete() );
|
|
||||||
assert_eq!(copy_expression(&b"foo{}"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Copy(vec!["foo"],
|
|
||||||
Vec::new())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(copy_expression(&b"foo{bar=1}"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Copy(vec!["foo"],
|
|
||||||
vec![("bar", Expression::Simple(Value::Int(1)))])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_to_lambda<'a>(t: (Vec<Value<'a>>, Value<'a>)) -> ParseResult<Expression<'a>> {
|
fn tuple_to_lambda<'a>(t: (Vec<Value<'a>>, Value<'a>)) -> ParseResult<Expression<'a>> {
|
||||||
match t.1 {
|
match t.1 {
|
||||||
Value::Tuple(v) => {
|
Value::Tuple(v) => {
|
||||||
@ -466,17 +367,6 @@ fn tuple_to_lambda<'a>(t: (Vec<Value<'a>>, Value<'a>)) -> ParseResult<Expression
|
|||||||
|
|
||||||
named!(arglist<Vec<Value> >, separated_list!(ws!(comma), symbol));
|
named!(arglist<Vec<Value> >, separated_list!(ws!(comma), symbol));
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arglist_parse() {
|
|
||||||
assert!(arglist(&b"arg"[..]).is_done());
|
|
||||||
assert!(arglist(&b"arg1, arg2"[..]).is_done());
|
|
||||||
assert_eq!(arglist(&b"arg1, arg2"[..]), IResult::Done(&b""[..],
|
|
||||||
vec![
|
|
||||||
Value::Symbol("arg1"),
|
|
||||||
Value::Symbol("arg2")
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(lambda_expression<Expression>,
|
named!(lambda_expression<Expression>,
|
||||||
map_res!(
|
map_res!(
|
||||||
do_parse!(
|
do_parse!(
|
||||||
@ -492,33 +382,6 @@ named!(lambda_expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_lambda_expression_parsing() {
|
|
||||||
assert!(lambda_expression(&b"foo"[..]).is_err() );
|
|
||||||
assert!(lambda_expression(&b"lambda \"foo\""[..]).is_err() );
|
|
||||||
assert!(lambda_expression(&b"lambda 1"[..]).is_err() );
|
|
||||||
assert!(lambda_expression(&b"lambda"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda ("[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg, arg2"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg1, arg2) =>"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg1, arg2) => {"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg1, arg2) => { foo"[..]).is_incomplete() );
|
|
||||||
assert!(lambda_expression(&b"lambda (arg1, arg2) => { foo ="[..]).is_incomplete() );
|
|
||||||
|
|
||||||
assert_eq!(lambda_expression(&b"lambda (arg1, arg2) => {foo=1,bar=2}"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Lambda{
|
|
||||||
arglist: vec![Value::Symbol("arg1"),
|
|
||||||
Value::Symbol("arg2")],
|
|
||||||
tuple: vec![("foo", Expression::Simple(Value::Int(1))),
|
|
||||||
("bar", Expression::Simple(Value::Int(2)))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_to_select<'a>(t: (Expression<'a>, Expression<'a>, Value<'a>))
|
fn tuple_to_select<'a>(t: (Expression<'a>, Expression<'a>, Value<'a>))
|
||||||
-> ParseResult<Expression<'a>> {
|
-> ParseResult<Expression<'a>> {
|
||||||
match t.2 {
|
match t.2 {
|
||||||
@ -536,8 +399,6 @@ fn tuple_to_select<'a>(t: (Expression<'a>, Expression<'a>, Value<'a>))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(select_selector<Value>, alt!(symbol | quoted_value | number));
|
|
||||||
|
|
||||||
named!(select_expression<Expression>,
|
named!(select_expression<Expression>,
|
||||||
map_res!(
|
map_res!(
|
||||||
terminated!(do_parse!(
|
terminated!(do_parse!(
|
||||||
@ -551,28 +412,8 @@ named!(select_expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
fn tuple_to_call<'a>(t: (Value<'a>, Vec<Expression<'a>>)) -> ParseResult<Expression<'a>> {
|
||||||
fn test_select_parse() {
|
if let Value::Selector(sl) = t.0 {
|
||||||
assert!(select_expression(&b"select"[..]).is_incomplete());
|
|
||||||
assert!(select_expression(&b"select foo"[..]).is_incomplete());
|
|
||||||
assert!(select_expression(&b"select foo, 1"[..]).is_incomplete());
|
|
||||||
assert!(select_expression(&b"select foo, 1, {"[..]).is_incomplete());
|
|
||||||
|
|
||||||
assert_eq!(select_expression(&b"select foo, 1, { foo = 2 };"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Select{
|
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("foo"))),
|
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
|
||||||
tuple: vec![
|
|
||||||
("foo", Expression::Simple(Value::Int(2)))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_to_call<'a>(t: (Expression<'a>, Vec<Expression<'a>>)) -> ParseResult<Expression<'a>> {
|
|
||||||
if let Expression::Selector(sl) = t.0 {
|
|
||||||
Ok(Expression::Call {
|
Ok(Expression::Call {
|
||||||
lambda: sl,
|
lambda: sl,
|
||||||
arglist: t.1,
|
arglist: t.1,
|
||||||
@ -582,21 +423,21 @@ fn tuple_to_call<'a>(t: (Expression<'a>, Vec<Expression<'a>>)) -> ParseResult<Ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vec_to_selector_expression<'a>(v: SelectorList<'a>) -> ParseResult<Expression<'a>> {
|
fn vec_to_selector_value<'a>(v: SelectorList<'a>) -> ParseResult<Value<'a>> {
|
||||||
Ok(Expression::Selector(v))
|
Ok(Value::Selector(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(selector_expression<Expression>,
|
named!(selector_value<Value>,
|
||||||
map_res!(
|
map_res!(
|
||||||
ws!(selector_list),
|
ws!(selector_list),
|
||||||
vec_to_selector_expression
|
vec_to_selector_value
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
named!(call_expression<Expression>,
|
named!(call_expression<Expression>,
|
||||||
map_res!(
|
map_res!(
|
||||||
do_parse!(
|
do_parse!(
|
||||||
lambda: selector_expression >>
|
lambda: selector_value >>
|
||||||
lparen >>
|
lparen >>
|
||||||
args: ws!(separated_list!(ws!(comma), expression)) >>
|
args: ws!(separated_list!(ws!(comma), expression)) >>
|
||||||
rparen >>
|
rparen >>
|
||||||
@ -606,39 +447,6 @@ named!(call_expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_call_parse() {
|
|
||||||
assert!(call_expression(&b"foo"[..]).is_incomplete() );
|
|
||||||
assert!(call_expression(&b"foo ("[..]).is_incomplete() );
|
|
||||||
assert!(call_expression(&b"foo (1"[..]).is_incomplete() );
|
|
||||||
assert!(call_expression(&b"foo (1,"[..]).is_err() );
|
|
||||||
assert!(call_expression(&b"foo (1,2"[..]).is_incomplete() );
|
|
||||||
|
|
||||||
assert_eq!(call_expression(&b"foo (1, \"foo\")"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Call{
|
|
||||||
lambda: vec!["foo"],
|
|
||||||
arglist: vec![
|
|
||||||
Expression::Simple(Value::Int(1)),
|
|
||||||
Expression::Simple(Value::String("foo")),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(call_expression(&b"foo.bar (1, \"foo\")"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Expression::Call{
|
|
||||||
lambda: vec!["foo","bar"],
|
|
||||||
arglist: vec![
|
|
||||||
Expression::Simple(Value::Int(1)),
|
|
||||||
Expression::Simple(Value::String("foo")),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters
|
// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters
|
||||||
// alot. We need to process alternatives in order of decreasing
|
// alot. We need to process alternatives in order of decreasing
|
||||||
// specificity. Unfortunately this means we are required to go in a
|
// specificity. Unfortunately this means we are required to go in a
|
||||||
@ -664,8 +472,185 @@ named!(expression<Expression>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
fn expression_to_statement(v: Expression) -> ParseResult<Statement> {
|
||||||
fn test_expression_parse() {
|
Ok(Statement::Expression(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(expression_statement<Statement>,
|
||||||
|
map_res!(
|
||||||
|
terminated!(ws!(expression), semicolon),
|
||||||
|
expression_to_statement
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
fn tuple_to_let<'a>(t: (&'a str, Expression<'a>)) -> ParseResult<Statement<'a>> {
|
||||||
|
Ok(Statement::Let {
|
||||||
|
name: t.0,
|
||||||
|
value: t.1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(let_statement<Statement>,
|
||||||
|
map_res!(
|
||||||
|
terminated!(do_parse!(
|
||||||
|
let_word >>
|
||||||
|
name: ws!(field) >>
|
||||||
|
equal >>
|
||||||
|
val: ws!(expression) >>
|
||||||
|
(name, val)
|
||||||
|
), semicolon),
|
||||||
|
tuple_to_let
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
fn tuple_to_import<'a>(t: (&'a str, &'a str)) -> ParseResult<Statement<'a>> {
|
||||||
|
Ok(Statement::Import {
|
||||||
|
name: t.0,
|
||||||
|
path: t.1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(import_statement<Statement>,
|
||||||
|
map_res!(
|
||||||
|
terminated!(do_parse!(
|
||||||
|
import_word >>
|
||||||
|
path: ws!(quoted) >>
|
||||||
|
as_word >>
|
||||||
|
name: ws!(field) >>
|
||||||
|
(name, path)
|
||||||
|
), semicolon),
|
||||||
|
tuple_to_import
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(statement<Statement>,
|
||||||
|
alt_complete!(
|
||||||
|
import_statement |
|
||||||
|
let_statement |
|
||||||
|
expression_statement
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(pub parse<Vec<Statement> >, many1!(ws!(statement)));
|
||||||
|
|
||||||
|
// TODO(jwall): Full Statement parsing tests.
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::str::from_utf8;
|
||||||
|
use super::{Statement, Expression, Value};
|
||||||
|
use super::{number, parse, field_value, tuple, grouped_expression, copy_expression};
|
||||||
|
use super::{arglist, lambda_expression, select_expression, call_expression, expression};
|
||||||
|
use super::{expression_statement, let_statement, import_statement, statement};
|
||||||
|
use nom::IResult;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_statement_parse() {
|
||||||
|
assert_eq!(statement(&b"import \"foo\" as foo;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Import{
|
||||||
|
path: "foo",
|
||||||
|
name: "foo"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert!(statement(&b"import foo"[..]).is_err() );
|
||||||
|
|
||||||
|
assert_eq!(statement(&b"let foo = 1.0 ;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Let{name: "foo",
|
||||||
|
value: Expression::Simple(Value::Float(1.0))}));
|
||||||
|
assert_eq!(statement(&b"1.0;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Float(1.0)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_import_parse() {
|
||||||
|
assert!(import_statement(&b"import"[..]).is_incomplete());
|
||||||
|
assert!(import_statement(&b"import \"foo\""[..]).is_incomplete());
|
||||||
|
assert!(import_statement(&b"import \"foo\" as"[..]).is_incomplete());
|
||||||
|
assert!(import_statement(&b"import \"foo\" as foo"[..]).is_incomplete());
|
||||||
|
|
||||||
|
assert_eq!(import_statement(&b"import \"foo\" as foo;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Import{
|
||||||
|
path: "foo",
|
||||||
|
name: "foo"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_let_statement_parse() {
|
||||||
|
assert!(let_statement(&b"foo"[..]).is_err() );
|
||||||
|
assert!(let_statement(&b"let \"foo\""[..]).is_err() );
|
||||||
|
assert!(let_statement(&b"let 1"[..]).is_err() );
|
||||||
|
assert!(let_statement(&b"let"[..]).is_incomplete() );
|
||||||
|
assert!(let_statement(&b"let foo"[..]).is_incomplete() );
|
||||||
|
assert!(let_statement(&b"let foo ="[..]).is_incomplete() );
|
||||||
|
assert!(let_statement(&b"let foo = "[..]).is_incomplete() );
|
||||||
|
assert!(let_statement(&b"let foo = 1"[..]).is_incomplete() );
|
||||||
|
|
||||||
|
assert_eq!(let_statement(&b"let foo = 1.0 ;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Let{name: "foo",
|
||||||
|
value: Expression::Simple(Value::Float(1.0))}));
|
||||||
|
assert_eq!(let_statement(&b"let foo= 1.0;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Let{name: "foo",
|
||||||
|
value: Expression::Simple(Value::Float(1.0))}));
|
||||||
|
assert_eq!(let_statement(&b"let foo =1.0;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Let{name: "foo",
|
||||||
|
value: Expression::Simple(Value::Float(1.0))}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expression_statement_parse() {
|
||||||
|
assert!(expression_statement(&b"foo"[..]).is_incomplete() );
|
||||||
|
assert_eq!(expression_statement(&b"1.0;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Float(1.0)))));
|
||||||
|
assert_eq!(expression_statement(&b"1.0 ;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Float(1.0)))));
|
||||||
|
assert_eq!(expression_statement(&b" 1.0;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Float(1.0)))));
|
||||||
|
assert_eq!(expression_statement(&b"foo;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Symbol("foo")))));
|
||||||
|
assert_eq!(expression_statement(&b"foo ;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Symbol("foo")))));
|
||||||
|
assert_eq!(expression_statement(&b" foo;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::Symbol("foo")))));
|
||||||
|
assert_eq!(expression_statement(&b"\"foo\";"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::String("foo")))));
|
||||||
|
assert_eq!(expression_statement(&b"\"foo\" ;"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::String("foo")))));
|
||||||
|
assert_eq!(expression_statement(&b" \"foo\";"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Statement::Expression(
|
||||||
|
Expression::Simple(Value::String("foo")))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expression_parse() {
|
||||||
assert_eq!(expression(&b"1"[..]),
|
assert_eq!(expression(&b"1"[..]),
|
||||||
IResult::Done(&b""[..], Expression::Simple(Value::Int(1))));
|
IResult::Done(&b""[..], Expression::Simple(Value::Int(1))));
|
||||||
assert_eq!(expression(&b"1 + 1"[..]),
|
assert_eq!(expression(&b"1 + 1"[..]),
|
||||||
@ -748,178 +733,212 @@ fn test_expression_parse() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_to_statement(v: Expression) -> ParseResult<Statement> {
|
#[test]
|
||||||
Ok(Statement::Expression(v))
|
fn test_call_parse() {
|
||||||
}
|
assert!(call_expression(&b"foo"[..]).is_incomplete() );
|
||||||
|
assert!(call_expression(&b"foo ("[..]).is_incomplete() );
|
||||||
|
assert!(call_expression(&b"foo (1"[..]).is_incomplete() );
|
||||||
|
assert!(call_expression(&b"foo (1,"[..]).is_err() );
|
||||||
|
assert!(call_expression(&b"foo (1,2"[..]).is_incomplete() );
|
||||||
|
|
||||||
named!(expression_statement<Statement>,
|
assert_eq!(call_expression(&b"foo (1, \"foo\")"[..]),
|
||||||
map_res!(
|
|
||||||
terminated!(ws!(expression), semicolon),
|
|
||||||
expression_to_statement
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_expression_statement_parse() {
|
|
||||||
assert!(expression_statement(&b"foo"[..]).is_incomplete() );
|
|
||||||
assert_eq!(expression_statement(&b"1.0;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Statement::Expression(
|
Expression::Call{
|
||||||
Expression::Simple(Value::Float(1.0)))));
|
lambda: vec!["foo"],
|
||||||
assert_eq!(expression_statement(&b"1.0 ;"[..]),
|
arglist: vec![
|
||||||
IResult::Done(&b""[..],
|
Expression::Simple(Value::Int(1)),
|
||||||
Statement::Expression(
|
Expression::Simple(Value::String("foo")),
|
||||||
Expression::Simple(Value::Float(1.0)))));
|
],
|
||||||
assert_eq!(expression_statement(&b" 1.0;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::Float(1.0)))));
|
|
||||||
assert_eq!(expression_statement(&b"foo;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::Symbol("foo")))));
|
|
||||||
assert_eq!(expression_statement(&b"foo ;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::Symbol("foo")))));
|
|
||||||
assert_eq!(expression_statement(&b" foo;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::Symbol("foo")))));
|
|
||||||
assert_eq!(expression_statement(&b"\"foo\";"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::String("foo")))));
|
|
||||||
assert_eq!(expression_statement(&b"\"foo\" ;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::String("foo")))));
|
|
||||||
assert_eq!(expression_statement(&b" \"foo\";"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Expression(
|
|
||||||
Expression::Simple(Value::String("foo")))));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_to_let<'a>(t: (&'a str, Expression<'a>)) -> ParseResult<Statement<'a>> {
|
|
||||||
Ok(Statement::Let {
|
|
||||||
name: t.0,
|
|
||||||
value: t.1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(let_statement<Statement>,
|
|
||||||
map_res!(
|
|
||||||
terminated!(do_parse!(
|
|
||||||
let_word >>
|
|
||||||
name: ws!(field) >>
|
|
||||||
equal >>
|
|
||||||
val: ws!(expression) >>
|
|
||||||
(name, val)
|
|
||||||
), semicolon),
|
|
||||||
tuple_to_let
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_let_statement_parse() {
|
|
||||||
assert!(let_statement(&b"foo"[..]).is_err() );
|
|
||||||
assert!(let_statement(&b"let \"foo\""[..]).is_err() );
|
|
||||||
assert!(let_statement(&b"let 1"[..]).is_err() );
|
|
||||||
assert!(let_statement(&b"let"[..]).is_incomplete() );
|
|
||||||
assert!(let_statement(&b"let foo"[..]).is_incomplete() );
|
|
||||||
assert!(let_statement(&b"let foo ="[..]).is_incomplete() );
|
|
||||||
assert!(let_statement(&b"let foo = "[..]).is_incomplete() );
|
|
||||||
assert!(let_statement(&b"let foo = 1"[..]).is_incomplete() );
|
|
||||||
|
|
||||||
assert_eq!(let_statement(&b"let foo = 1.0 ;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Let{name: "foo",
|
|
||||||
value: Expression::Simple(Value::Float(1.0))}));
|
|
||||||
assert_eq!(let_statement(&b"let foo= 1.0;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Let{name: "foo",
|
|
||||||
value: Expression::Simple(Value::Float(1.0))}));
|
|
||||||
assert_eq!(let_statement(&b"let foo =1.0;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Let{name: "foo",
|
|
||||||
value: Expression::Simple(Value::Float(1.0))}));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tuple_to_import<'a>(t: (&'a str, &'a str)) -> ParseResult<Statement<'a>> {
|
|
||||||
Ok(Statement::Import {
|
|
||||||
name: t.0,
|
|
||||||
path: t.1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(import_statement<Statement>,
|
|
||||||
map_res!(
|
|
||||||
terminated!(do_parse!(
|
|
||||||
import_word >>
|
|
||||||
path: ws!(quoted) >>
|
|
||||||
as_word >>
|
|
||||||
name: ws!(field) >>
|
|
||||||
(name, path)
|
|
||||||
), semicolon),
|
|
||||||
tuple_to_import
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_import_parse() {
|
|
||||||
assert!(import_statement(&b"import"[..]).is_incomplete());
|
|
||||||
assert!(import_statement(&b"import \"foo\""[..]).is_incomplete());
|
|
||||||
assert!(import_statement(&b"import \"foo\" as"[..]).is_incomplete());
|
|
||||||
assert!(import_statement(&b"import \"foo\" as foo"[..]).is_incomplete());
|
|
||||||
|
|
||||||
assert_eq!(import_statement(&b"import \"foo\" as foo;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
|
||||||
Statement::Import{
|
|
||||||
path: "foo",
|
|
||||||
name: "foo"
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
named!(statement<Statement>,
|
assert_eq!(call_expression(&b"foo.bar (1, \"foo\")"[..]),
|
||||||
alt_complete!(
|
|
||||||
import_statement |
|
|
||||||
let_statement |
|
|
||||||
expression_statement
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_statement_parse() {
|
|
||||||
assert_eq!(statement(&b"import \"foo\" as foo;"[..]),
|
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Statement::Import{
|
Expression::Call{
|
||||||
path: "foo",
|
lambda: vec!["foo","bar"],
|
||||||
name: "foo"
|
arglist: vec![
|
||||||
|
Expression::Simple(Value::Int(1)),
|
||||||
|
Expression::Simple(Value::String("foo")),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert!(statement(&b"import foo"[..]).is_err() );
|
}
|
||||||
|
|
||||||
assert_eq!(statement(&b"let foo = 1.0 ;"[..]),
|
#[test]
|
||||||
|
fn test_select_parse() {
|
||||||
|
assert!(select_expression(&b"select"[..]).is_incomplete());
|
||||||
|
assert!(select_expression(&b"select foo"[..]).is_incomplete());
|
||||||
|
assert!(select_expression(&b"select foo, 1"[..]).is_incomplete());
|
||||||
|
assert!(select_expression(&b"select foo, 1, {"[..]).is_incomplete());
|
||||||
|
|
||||||
|
assert_eq!(select_expression(&b"select foo, 1, { foo = 2 };"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Statement::Let{name: "foo",
|
Expression::Select{
|
||||||
value: Expression::Simple(Value::Float(1.0))}));
|
val: Box::new(Expression::Simple(Value::Symbol("foo"))),
|
||||||
assert_eq!(statement(&b"1.0;"[..]),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
|
tuple: vec![
|
||||||
|
("foo", Expression::Simple(Value::Int(2)))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_expression_parsing() {
|
||||||
|
assert!(lambda_expression(&b"foo"[..]).is_err() );
|
||||||
|
assert!(lambda_expression(&b"lambda \"foo\""[..]).is_err() );
|
||||||
|
assert!(lambda_expression(&b"lambda 1"[..]).is_err() );
|
||||||
|
assert!(lambda_expression(&b"lambda"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda ("[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg, arg2"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg1, arg2) =>"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg1, arg2) => {"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg1, arg2) => { foo"[..]).is_incomplete() );
|
||||||
|
assert!(lambda_expression(&b"lambda (arg1, arg2) => { foo ="[..]).is_incomplete() );
|
||||||
|
|
||||||
|
assert_eq!(lambda_expression(&b"lambda (arg1, arg2) => {foo=1,bar=2}"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Statement::Expression(
|
Expression::Lambda{
|
||||||
Expression::Simple(Value::Float(1.0)))));
|
arglist: vec![Value::Symbol("arg1"),
|
||||||
}
|
Value::Symbol("arg2")],
|
||||||
|
tuple: vec![("foo", Expression::Simple(Value::Int(1))),
|
||||||
|
("bar", Expression::Simple(Value::Int(2)))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
named!(pub parse<Vec<Statement> >, many1!(ws!(statement)));
|
#[test]
|
||||||
|
fn test_arglist_parse() {
|
||||||
|
assert!(arglist(&b"arg"[..]).is_done());
|
||||||
|
assert!(arglist(&b"arg1, arg2"[..]).is_done());
|
||||||
|
assert_eq!(arglist(&b"arg1, arg2"[..]), IResult::Done(&b""[..],
|
||||||
|
vec![
|
||||||
|
Value::Symbol("arg1"),
|
||||||
|
Value::Symbol("arg2")
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(jwall): Full Statement parsing tests.
|
#[test]
|
||||||
|
fn test_copy_parse() {
|
||||||
|
assert!(copy_expression(&b"{}"[..]).is_err() );
|
||||||
|
assert!(copy_expression(&b"foo"[..]).is_incomplete() );
|
||||||
|
assert!(copy_expression(&b"foo{"[..]).is_incomplete() );
|
||||||
|
assert_eq!(copy_expression(&b"foo{}"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Expression::Copy(vec!["foo"],
|
||||||
|
Vec::new())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(copy_expression(&b"foo{bar=1}"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Expression::Copy(vec!["foo"],
|
||||||
|
vec![("bar", Expression::Simple(Value::Int(1)))])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() {
|
fn test_grouped_expression_parse() {
|
||||||
|
assert!(grouped_expression(&b"foo"[..]).is_err() );
|
||||||
|
assert!(grouped_expression(&b"(foo"[..]).is_incomplete() );
|
||||||
|
assert_eq!(grouped_expression(&b"(foo)"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Expression::Grouped(
|
||||||
|
Box::new(
|
||||||
|
Expression::Simple(
|
||||||
|
Value::Symbol("foo")))))
|
||||||
|
);
|
||||||
|
assert_eq!(grouped_expression(&b"(1 + 1)"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Expression::Grouped(
|
||||||
|
Box::new(
|
||||||
|
Expression::Add(
|
||||||
|
Box::new(Value::Int(1)),
|
||||||
|
Box::new(Expression::Simple(
|
||||||
|
Value::Int(1)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_parse() {
|
||||||
|
assert!(tuple(&b"{"[..]).is_incomplete() );
|
||||||
|
assert!(tuple(&b"{ foo"[..]).is_incomplete() );
|
||||||
|
assert!(tuple(&b"{ foo ="[..]).is_incomplete() );
|
||||||
|
assert!(tuple(&b"{ foo = 1"[..]).is_incomplete() );
|
||||||
|
assert!(tuple(&b"{ foo = 1,"[..]).is_err() );
|
||||||
|
assert!(tuple(&b"{ foo = 1, bar ="[..]).is_err() );
|
||||||
|
|
||||||
|
assert_eq!(tuple(&b"{ }"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Value::Tuple(
|
||||||
|
vec![])));
|
||||||
|
|
||||||
|
assert_eq!(tuple(&b"{ foo = 1 }"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Value::Tuple(
|
||||||
|
vec![
|
||||||
|
("foo", Expression::Simple(Value::Int(1)))
|
||||||
|
])));
|
||||||
|
|
||||||
|
assert_eq!(tuple(&b"{ foo = 1, bar = \"1\" }"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Value::Tuple(
|
||||||
|
vec![
|
||||||
|
("foo", Expression::Simple(Value::Int(1))),
|
||||||
|
("bar", Expression::Simple(Value::String("1")))
|
||||||
|
])));
|
||||||
|
assert_eq!(tuple(&b"{ foo = 1, bar = {} }"[..]),
|
||||||
|
IResult::Done(&b""[..],
|
||||||
|
Value::Tuple(
|
||||||
|
vec![
|
||||||
|
("foo", Expression::Simple(Value::Int(1))),
|
||||||
|
("bar", Expression::Simple(Value::Tuple(Vec::new())))
|
||||||
|
])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_field_value_parse() {
|
||||||
|
assert!(field_value(&b"foo"[..]).is_incomplete() );
|
||||||
|
assert!(field_value(&b"foo ="[..]).is_incomplete() );
|
||||||
|
|
||||||
|
assert_eq!(field_value(&b"foo = 1"[..]),
|
||||||
|
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Int(1)))) );
|
||||||
|
assert_eq!(field_value(&b"foo = \"1\""[..]),
|
||||||
|
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::String("1")))) );
|
||||||
|
assert_eq!(field_value(&b"foo = bar"[..]),
|
||||||
|
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Symbol("bar")))) );
|
||||||
|
assert_eq!(field_value(&b"foo = bar "[..]),
|
||||||
|
IResult::Done(&b""[..], ("foo", Expression::Simple(Value::Symbol("bar")))) );
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number_parsing() {
|
||||||
|
assert!(number(&b"."[..]).is_err() );
|
||||||
|
assert!(number(&b". "[..]).is_err() );
|
||||||
|
assert_eq!(number(&b"1.0"[..]),
|
||||||
|
IResult::Done(&b""[..], Value::Float(1.0)) );
|
||||||
|
assert_eq!(number(&b"1."[..]),
|
||||||
|
IResult::Done(&b""[..], Value::Float(1.0)) );
|
||||||
|
assert_eq!(number(&b"1"[..]),
|
||||||
|
IResult::Done(&b""[..], Value::Int(1)) );
|
||||||
|
assert_eq!(number(&b".1"[..]),
|
||||||
|
IResult::Done(&b""[..], Value::Float(0.1)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse() {
|
||||||
let bad_input = &b"import mylib as lib;"[..];
|
let bad_input = &b"import mylib as lib;"[..];
|
||||||
let bad_result = parse(bad_input);
|
let bad_result = parse(bad_input);
|
||||||
assert!(bad_result.is_err() );
|
assert!(bad_result.is_err() );
|
||||||
@ -947,4 +966,5 @@ fn test_parse() {
|
|||||||
Box::new(Expression::Simple(Value::Int(1))))
|
Box::new(Expression::Simple(Value::Int(1))))
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user