split ast handling into a separate module directory.

This commit is contained in:
Jeremy Wall 2018-04-16 20:04:17 -05:00
parent 65a3c48110
commit 4265b0177b
10 changed files with 264 additions and 326 deletions

18
src/ast/mod.rs Normal file
View File

@ -0,0 +1,18 @@
// 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.
//! The definitions of the ucg AST and Tokens.
#[macro_use]
pub mod tree;

View File

@ -11,8 +11,6 @@
// 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.
//! The definitions of the ucg AST and Tokens.
use std;
use std::collections::HashSet;
use std::borrow::Borrow;
@ -543,10 +541,13 @@ impl MacroDef {
while stack.len() > 0 {
match stack.pop().unwrap() {
&Expression::Binary(ref bexpr) => {
let mut syms_set = self.validate_value_symbols(&mut stack, &bexpr.left);
bad_symbols.extend(syms_set.drain());
stack.push(&bexpr.left);
stack.push(&bexpr.right);
}
&Expression::Compare(ref cexpr) => {
stack.push(&cexpr.left);
stack.push(&cexpr.right);
}
&Expression::Grouped(ref expr) => {
stack.push(expr);
}
@ -602,6 +603,10 @@ pub enum BinaryExprType {
Sub,
Mul,
Div,
}
#[derive(Debug, PartialEq, Clone)]
pub enum CompareType {
Equal,
GT,
LT,
@ -610,11 +615,19 @@ pub enum BinaryExprType {
LTEqual,
}
#[derive(Debug, PartialEq, Clone)]
pub struct ComparisonDef {
pub kind: CompareType,
pub left: Box<Expression>,
pub right: Box<Expression>,
pub pos: Position,
}
/// Represents an expression with a left and a right side.
#[derive(Debug, PartialEq, Clone)]
pub struct BinaryOpDef {
pub kind: BinaryExprType,
pub left: Value,
pub left: Box<Expression>,
pub right: Box<Expression>,
pub pos: Position,
}
@ -665,6 +678,7 @@ pub enum Expression {
// Binary expressions
Binary(BinaryOpDef),
Compare(ComparisonDef),
// Complex Expressions
Copy(CopyDef),
@ -682,6 +696,7 @@ impl Expression {
match self {
&Expression::Simple(ref v) => v.pos(),
&Expression::Binary(ref def) => &def.pos,
&Expression::Compare(ref def) => &def.pos,
&Expression::Copy(ref def) => &def.pos,
&Expression::Grouped(ref expr) => expr.pos(),
&Expression::Format(ref def) => &def.pos,
@ -733,7 +748,11 @@ mod ast_test {
make_tok!("f1", 1, 1),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Symbol(value_node!("foo".to_string(), 1, 1)),
left: Box::new(Expression::Simple(Value::Symbol(value_node!(
"foo".to_string(),
1,
1
)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -753,7 +772,11 @@ mod ast_test {
make_tok!("f1", 1, 1),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Symbol(value_node!("bar".to_string(), 1, 1)),
left: Box::new(Expression::Simple(Value::Symbol(value_node!(
"bar".to_string(),
1,
1
)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -775,8 +798,10 @@ mod ast_test {
make_tok!("f1", 1, 1),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Selector(make_selector!(make_expr!("foo", 1, 1) => [
make_tok!("quux", 1, 1) ] => 1, 1)),
left: Box::new(Expression::Simple(Value::Selector(
make_selector!(make_expr!("foo", 1, 1) => [
make_tok!("quux", 1, 1) ] => 1, 1),
))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -796,8 +821,10 @@ mod ast_test {
make_tok!("f1", 1, 1),
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Selector(make_selector!(make_expr!("bar", 1, 1) => [
make_tok!("quux", 1, 1) ] => 1, 1)),
left: Box::new(Expression::Simple(Value::Selector(
make_selector!(make_expr!("bar", 1, 1) => [
make_tok!("quux", 1, 1) ] => 1, 1),
))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),

View File

@ -26,7 +26,7 @@ use std::rc::Rc;
use std::convert::From;
use tokenizer::Span;
use ast::*;
use ast::tree::*;
use format;
use parse::parse;
use error;
@ -299,14 +299,16 @@ macro_rules! eval_binary_expr {
match $rside.as_ref() {
$case => {
return Ok(Rc::new($result));
},
}
val => {
return Err(Box::new(
error::Error::new(
format!("Expected {} but got {}", $msg, val), error::ErrorType::TypeFail, $pos.clone())));
}
return Err(Box::new(error::Error::new(
format!("Expected {} but got {}", $msg, val),
error::ErrorType::TypeFail,
$pos.clone(),
)));
}
}
};
}
impl Builder {
@ -390,19 +392,28 @@ impl Builder {
Ok(())
}
/// Builds a string of ucg syntax.
pub fn build_file_string(&mut self, input: String) -> BuildResult {
match parse(Span::new(&input)) {
pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<Error>> {
match parse(Span::new(input)) {
Ok(stmts) => {
let mut out: Option<Rc<Val>> = None;
for stmt in stmts.iter() {
try!(self.build_stmt(stmt));
out = Some(try!(self.build_stmt(stmt)));
}
match out {
None => return Ok(Rc::new(Val::Empty)),
Some(val) => Ok(val),
}
Ok(())
}
Err(err) => Err(Box::new(err)),
}
}
/// Builds a string of ucg syntax.
pub fn build_file_string(&mut self, input: String) -> BuildResult {
self.last = Some(try!(self.eval_string(&input)));
Ok(())
}
/// Builds a ucg file at the named path.
pub fn build_file(&mut self, name: &str) -> BuildResult {
let mut f = try!(File::open(name));
@ -412,11 +423,11 @@ impl Builder {
self.build_file_string(s)
}
fn build_import(&mut self, def: &ImportDef) -> BuildResult {
if !self.files.contains(&def.path.fragment) {
// Only parse the file once on import.
fn build_import(&mut self, def: &ImportDef) -> Result<Rc<Val>, Box<Error>> {
let sym = &def.name;
let positioned_sym = sym.into();
if !self.files.contains(&def.path.fragment) {
// Only parse the file once on import.
if self.assets.get(&positioned_sym).is_none() {
let mut b = Self::new();
try!(b.build_file(&def.path.fragment));
@ -424,15 +435,27 @@ impl Builder {
let result = Rc::new(Val::Tuple(fields));
self.assets.entry(positioned_sym).or_insert(result.clone());
self.files.insert(def.path.fragment.clone());
self.last = Some(result);
return Ok(result);
} else {
return Ok(self.assets.get(&positioned_sym).unwrap().clone());
}
} else {
return match self.assets.get(&positioned_sym) {
None => {
// some kind of error here I think.
Err(Box::new(error::Error::new(
"Unknown Error processing import",
error::ErrorType::Unsupported,
def.name.pos.clone(),
)))
}
Some(val) => Ok(val.clone()),
};
}
Ok(())
}
fn build_let(&mut self, def: &LetDef) -> BuildResult {
fn build_let(&mut self, def: &LetDef) -> Result<Rc<Val>, Box<Error>> {
let val = try!(self.eval_expr(&def.value));
self.last = Some(val.clone());
let name = &def.name;
match self.out.entry(name.into()) {
Entry::Occupied(e) => {
@ -448,25 +471,18 @@ impl Builder {
)));
}
Entry::Vacant(e) => {
e.insert(val);
e.insert(val.clone());
}
}
Ok(())
Ok(val)
}
fn build_stmt(&mut self, stmt: &Statement) -> BuildResult {
fn build_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<Error>> {
match stmt {
&Statement::Let(ref def) => {
try!(self.build_let(def));
&Statement::Let(ref def) => self.build_let(def),
&Statement::Import(ref def) => self.build_import(def),
&Statement::Expression(ref expr) => self.eval_expr(expr),
}
&Statement::Import(ref def) => {
try!(self.build_import(def));
}
&Statement::Expression(ref expr) => {
self.last = Some(try!(self.eval_expr(expr)));
}
};
Ok(())
}
fn lookup_sym(&self, sym: &Positioned<String>) -> Option<Rc<Val>> {
@ -859,21 +875,27 @@ impl Builder {
fn eval_binary(&self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
let kind = &def.kind;
let v = &def.left;
let expr = &def.right;
let right = try!(self.eval_expr(expr));
let left = try!(self.value_to_val(v));
let left = try!(self.eval_expr(&def.left));
let right = try!(self.eval_expr(&def.right));
match kind {
&BinaryExprType::Add => self.add_vals(&def.pos, left, right),
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
&BinaryExprType::Mul => self.multiply_vals(&def.pos, left, right),
&BinaryExprType::Div => self.divide_vals(&def.pos, left, right),
&BinaryExprType::Equal => self.do_deep_equal(&def.pos, left, right),
&BinaryExprType::GT => self.do_gt(&def.pos, left, right),
&BinaryExprType::LT => self.do_lt(&def.pos, left, right),
&BinaryExprType::GTEqual => self.do_gtequal(&def.pos, left, right),
&BinaryExprType::LTEqual => self.do_ltequal(&def.pos, left, right),
&BinaryExprType::NotEqual => self.do_not_deep_equal(&def.pos, left, right),
}
}
fn eval_compare(&self, def: &ComparisonDef) -> Result<Rc<Val>, Box<Error>> {
let kind = &def.kind;
let left = try!(self.eval_expr(&def.left));
let right = try!(self.eval_expr(&def.right));
match kind {
&CompareType::Equal => self.do_deep_equal(&def.pos, left, right),
&CompareType::GT => self.do_gt(&def.pos, left, right),
&CompareType::LT => self.do_lt(&def.pos, left, right),
&CompareType::GTEqual => self.do_gtequal(&def.pos, left, right),
&CompareType::LTEqual => self.do_ltequal(&def.pos, left, right),
&CompareType::NotEqual => self.do_not_deep_equal(&def.pos, left, right),
}
}
@ -1020,7 +1042,7 @@ impl Builder {
return self.eval_expr(val_expr);
}
}
// Otherwise return the default
// Otherwise return the default.
return self.eval_expr(def_expr);
} else {
return Err(Box::new(error::Error::new(
@ -1071,11 +1093,11 @@ impl Builder {
// 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(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
// TODO(jwall): We probably don't want to consume these expressions.
// Take a reference instead?
// TODO(jwall): We need a rewrite step to handle operator precendence order.
match expr {
&Expression::Simple(ref val) => self.value_to_val(val),
&Expression::Binary(ref def) => self.eval_binary(def),
&Expression::Compare(ref def) => self.eval_compare(def),
&Expression::Copy(ref def) => self.eval_copy(def),
&Expression::Grouped(ref expr) => self.eval_expr(expr),
&Expression::Format(ref def) => self.eval_format(def),
@ -1087,6 +1109,95 @@ impl Builder {
}
}
#[cfg(test)]
mod compile_test {
use super::{Builder, Val};
fn assert_build<S: Into<String>>(input: S, assert: &str) {
let mut b = Builder::new();
b.build_file_string(input.into()).unwrap();
let result = b.eval_string(assert).unwrap();
if let &Val::Boolean(ok) = result.as_ref() {
assert!(ok, format!("'{}' is not true", assert));
} else {
assert!(
false,
format!("'{}' does not evaluate to a boolean: {:?}", assert, result)
);
}
}
#[test]
fn test_comparisons() {
let input = "
let one = 1;
let two = 2;
let foo = \"foo\";
let bar = \"bar\";
let tpl1 = {
foo = \"bar\",
one = 1
};
let tpl2 = tpl1{};
let tpl3 = {
bar = \"foo\",
two = 1
};
let list = [1, 2, 3];
let list2 = list;
let list3 = [1, 2];
";
assert_build(input, "one == one;");
assert_build(input, "one >= one;");
assert_build(input, "two > one;");
assert_build(input, "two >= two;");
assert_build(input, "tpl1 == tpl2;");
assert_build(input, "tpl1 != tpl3;");
assert_build(input, "list == list2;");
assert_build(input, "list != list3;");
}
#[test]
fn test_deep_comparison() {
let input = "
let tpl1 = {
foo = \"bar\",
lst = [1, 2, 3],
inner = {
fld = \"value\"
}
};
let copy = tpl1;
let extra = tpl1{one = 1};
let less = {
foo = \"bar\"
};
";
assert_build(input, "tpl1.inner == copy.inner;");
assert_build(input, "tpl1.inner.fld == copy.inner.fld;");
assert_build(input, "tpl1.lst == copy.lst;");
assert_build(input, "tpl1.foo == copy.foo;");
assert_build(input, "tpl1 == copy;");
assert_build(input, "tpl1 != extra;");
assert_build(input, "tpl1 != less;");
}
#[test]
fn test_expression_comparisons() {
assert_build("", "2 == 1+1;");
assert_build("", "(1+1) == 2;");
assert_build("", "(1+1) == (1+1);");
assert_build("", "(\"foo\" + \"bar\") == \"foobar\";");
}
#[test]
fn test_binary_operator_precedence() {
assert_build("let result = 2 * 2 + 1;", "result == 6;");
assert_build("let result = (2 * 2) + 1;", "result == 5;");
}
}
#[cfg(test)]
mod test {
use super::{Builder, CallDef, MacroDef, SelectDef, Val};
@ -1107,7 +1218,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Div,
left: Value::Int(value_node!(2, 1, 1)),
left: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1116,7 +1227,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Div,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1136,7 +1247,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Div,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1155,7 +1266,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Value::Int(value_node!(2, 1, 1)),
left: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1164,7 +1275,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1184,7 +1295,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Mul,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(20, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1203,7 +1314,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Sub,
left: Value::Int(value_node!(2, 1, 1)),
left: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1212,7 +1323,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Sub,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1232,7 +1343,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Sub,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1251,7 +1362,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Int(value_node!(1, 1, 1)),
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1260,7 +1371,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Float(value_node!(1.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1269,7 +1380,11 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::String(value_node!("foo".to_string(), 1, 1)),
left: Box::new(Expression::Simple(Value::String(value_node!(
"foo".to_string(),
1,
1
)))),
right: Box::new(Expression::Simple(Value::String(value_node!(
"bar".to_string(),
1,
@ -1282,7 +1397,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::List(ListDef {
left: Box::new(Expression::Simple(Value::List(ListDef {
elems: vec![
Expression::Simple(Value::String(value_node!(
"foo".to_string(),
@ -1291,7 +1406,7 @@ mod test {
))),
],
pos: Position::new(1, 1),
}),
}))),
right: Box::new(Expression::Simple(Value::List(ListDef {
elems: vec![
Expression::Simple(Value::String(value_node!(
@ -1323,7 +1438,7 @@ mod test {
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Add,
left: Value::Float(value_node!(2.0, 1, 1)),
left: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
@ -1334,223 +1449,6 @@ mod test {
);
}
#[test]
fn test_eval_equal_exprs() {
let b = Builder::new();
test_expr_to_val(
vec![
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Int(value_node!(2, 1, 1)),
right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 1)))),
pos: Position::new(1, 0),
}),
Val::Boolean(true),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Float(value_node!(2.0, 1, 1)),
right: Box::new(Expression::Simple(Value::Float(value_node!(2.0, 1, 1)))),
pos: Position::new(1, 0),
}),
Val::Boolean(true),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::String(value_node!("foo".to_string(), 1, 1)),
right: Box::new(Expression::Simple(Value::String(value_node!(
"foo".to_string(),
1,
1
)))),
pos: Position::new(1, 0),
}),
Val::Boolean(true),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)),
right: Box::new(Expression::Simple(Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)))),
pos: Position::new(1, 0),
}),
Val::Boolean(true),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)),
right: Box::new(Expression::Simple(Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blush".to_string(),
1,
1
))),
),
],
1,
1
)))),
pos: Position::new(1, 0),
}),
Val::Boolean(false),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)),
right: Box::new(Expression::Simple(Value::Tuple(value_node!(
vec![
(
make_tok!("bosh", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)))),
pos: Position::new(1, 0),
}),
Val::Boolean(false),
),
(
Expression::Binary(BinaryOpDef {
kind: BinaryExprType::Equal,
left: Value::Tuple(value_node!(
vec![
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)),
right: Box::new(Expression::Simple(Value::Tuple(value_node!(
vec![
(
make_tok!("bash", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("bar", 1, 1),
Expression::Simple(Value::Int(value_node!(1, 1, 1))),
),
(
make_tok!("foo", 1, 1),
Expression::Simple(Value::String(value_node!(
"blah".to_string(),
1,
1
))),
),
],
1,
1
)))),
pos: Position::new(1, 0),
}),
Val::Boolean(false),
),
],
b,
);
}
#[test]
fn test_eval_simple_expr() {
test_expr_to_val(

View File

@ -17,7 +17,7 @@ use std::rc::Rc;
use std::io::Write;
use std::io::Result;
use ast;
use ast::tree::*;
use build::Val;
use convert::traits::Converter;
@ -31,7 +31,7 @@ impl EnvConverter {
fn convert_tuple(
&self,
flds: &Vec<(ast::Positioned<String>, Rc<Val>)>,
flds: &Vec<(Positioned<String>, Rc<Val>)>,
w: &mut Write,
) -> Result<()> {
for &(ref name, ref val) in flds.iter() {

View File

@ -36,7 +36,7 @@ impl JsonConverter {
fn convert_tuple(
&self,
items: &Vec<(ast::Positioned<String>, Rc<Val>)>,
items: &Vec<(ast::tree::Positioned<String>, Rc<Val>)>,
) -> Result<serde_json::Value> {
let mut mp = serde_json::Map::new();
for &(ref k, ref v) in items.iter() {

View File

@ -16,7 +16,7 @@
use std::error;
use std::fmt;
use ast::*;
use ast::tree::*;
use nom;

View File

@ -16,7 +16,7 @@
use std::clone::Clone;
use std::error::Error;
use ast::*;
use ast::tree::*;
use error;
/// Implements the logic for format strings in UCG format expressions.

View File

@ -243,11 +243,13 @@
//!
//! The `>`, `<`, `>=`, and `>=` operators are only supported on numeric types.
//!
//! ``ucg
//! ```ucg
//! 1 > 2; // result is false
//! 2 < 3; // result is true
//! 10 > "9"; // This is a compile error.
//! (1+2) == 3
//! ```
//! The comparison operators expect either a simple value or a grouped expression as their left operand.
//!
//! The equality operators `==` and `!=` are supported for all types and will perform deep equal comparisons on complex
//! types.
@ -266,25 +268,18 @@
//! Note that tuple fields are ordered so a tuple will only be equal if the fields are both in the same order and
//! have the same values in them.
//!
//! ##### Boolean
//! ##### Operator Precedence
//!
//! ucg supports the following operators, `+`, `-`, `*`, `/`, `==`, `!=`, `>`, `<`, `>=`, `<=`, `&&`, `||` Each one is type safe and
//! infers the types from the values they operate on. The operators expect both the left and right operands to be of the same
//! type. All of the operators except `&&` and `||` are valid on integers and floats. The + operator can additionally concatenate
//! strings or arrays. The `&&` and `||` operators are only valid on booleans.
//! ucg operators evaluate the right side first. If you want to enforce a different
//! order of operations you will need to use parentheses to group them. This is
//! most evident when mixing numeric and comparison operators.
//!
//! ```ucg
//! 1 + 1; // result is 2
//! "foo " + "bar" // result is "foo bar"
//! [1,2] + [3,4]; // result is [1,2,3,4]
//! 1 == 1; // result is true.
//! 1 > 1; // result is false
//! 1 >= 1; // result is true;
//! 1+1 == 2; // this will be a type error. The + expects an integer but it has a boolean expression
//! // on the right.
//! (1+1) == 2; // this succeeds because the parentheses force evaluation of the addition first.
//! ```
//!
//! The Equality comparison can be done on any type and will perform a deep equal comparison.
//!
//!
//! #### Copy expressions
//!
//! ucg Tuples support a form of reuse with copy on write semantics. You can copy a tuple and selectively overwrite fields or add new
@ -421,9 +416,9 @@ pub mod error;
mod format;
pub use ast::Value;
pub use ast::Expression;
pub use ast::Statement;
pub use ast::tree::Value;
pub use ast::tree::Expression;
pub use ast::tree::Statement;
pub use parse::parse;
pub use build::Builder;

View File

@ -21,7 +21,7 @@ use nom;
use nom::InputLength;
use nom::IResult;
use ast::*;
use ast::tree::*;
use tokenizer::*;
use error;

View File

@ -17,7 +17,7 @@ use nom_locate::LocatedSpan;
use nom;
use nom::{alpha, digit, is_alphanumeric, multispace};
use nom::{InputIter, InputLength, Slice};
use ast::*;
use ast::tree::*;
use error;
use std;
use std::result::Result;