From 4265b0177b443ec4af7cdeb25a7437ab0c57fb8f Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 16 Apr 2018 20:04:17 -0500 Subject: [PATCH] split ast handling into a separate module directory. --- src/ast/mod.rs | 18 ++ src/{ast.rs => ast/tree.rs} | 73 ++++-- src/build.rs | 454 ++++++++++++++---------------------- src/convert/env.rs | 4 +- src/convert/json.rs | 2 +- src/error.rs | 2 +- src/format.rs | 2 +- src/lib.rs | 31 ++- src/parse.rs | 2 +- src/tokenizer.rs | 2 +- 10 files changed, 264 insertions(+), 326 deletions(-) create mode 100644 src/ast/mod.rs rename src/{ast.rs => ast/tree.rs} (91%) diff --git a/src/ast/mod.rs b/src/ast/mod.rs new file mode 100644 index 0000000..2710913 --- /dev/null +++ b/src/ast/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2017 Jeremy Wall +// +// 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; diff --git a/src/ast.rs b/src/ast/tree.rs similarity index 91% rename from src/ast.rs rename to src/ast/tree.rs index edced00..dc1b787 100644 --- a/src/ast.rs +++ b/src/ast/tree.rs @@ -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; @@ -108,10 +106,10 @@ impl Borrow for Token { /// Helper macro for making a Positioned Value. macro_rules! value_node { - ($v:expr, $p:expr) => { + ($v: expr, $p: expr) => { Positioned::new_with_pos($v, $p) }; - ($v:expr, $l:expr, $c:expr) => { + ($v: expr, $l: expr, $c: expr) => { Positioned::new($v, $l, $c) }; } @@ -119,31 +117,31 @@ macro_rules! value_node { /// Helper macro for making a Token. #[allow(unused_macros)] macro_rules! make_tok { - ( EOF => $l:expr, $c:expr ) => { + (EOF => $l: expr, $c: expr) => { Token::new("", TokenType::END, $l, $c) }; - ( WS => $l:expr, $c:expr ) => { + (WS => $l: expr, $c: expr) => { Token::new("", TokenType::WS, $l, $c) }; - ( CMT => $e:expr, $l:expr, $c:expr ) => { + (CMT => $e: expr, $l: expr, $c: expr) => { Token::new($e, TokenType::COMMENT, $l, $c) }; - ( QUOT => $e:expr, $l:expr, $c:expr ) => { + (QUOT => $e: expr, $l: expr, $c: expr) => { Token::new($e, TokenType::QUOTED, $l, $c) }; - ( PUNCT => $e:expr, $l:expr, $c:expr ) => { + (PUNCT => $e: expr, $l: expr, $c: expr) => { Token::new($e, TokenType::PUNCT, $l, $c) }; - ( DIGIT => $e:expr, $l:expr, $c:expr ) => { + (DIGIT => $e: expr, $l: expr, $c: expr) => { Token::new($e, TokenType::DIGIT, $l, $c) }; - ( $e:expr, $l:expr, $c:expr ) => { + ($e: expr, $l: expr, $c: expr) => { Token::new($e, TokenType::BAREWORD, $l, $c) }; } @@ -151,15 +149,15 @@ macro_rules! make_tok { /// Helper macro for making expressions. #[allow(unused_macros)] macro_rules! make_expr { - ( $e:expr ) => { + ($e: expr) => { make_expr!($e, 1, 1) }; - ( $e:expr, $l:expr, $c:expr ) => { + ($e: expr, $l: expr, $c: expr) => { Expression::Simple(Value::Symbol(Positioned::new($e.to_string(), $l, $c))) }; - ( $e:expr => int, $l:expr, $c:expr ) => { + ($e: expr => int, $l: expr, $c: expr) => { Expression::Simple(Value::Int(Positioned::new($e, $l, $c))) }; } @@ -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, + pub right: Box, + 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, pub right: Box, 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), }), diff --git a/src/build.rs b/src/build.rs index f11eeef..f783584 100644 --- a/src/build.rs +++ b/src/build.rs @@ -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; @@ -295,18 +295,20 @@ pub struct Builder { } macro_rules! eval_binary_expr { - ($case:pat, $pos:ident, $rside:ident, $result:expr, $msg:expr) => { + ($case: pat, $pos: ident, $rside: ident, $result: expr, $msg: 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, Box> { + match parse(Span::new(input)) { Ok(stmts) => { + let mut out: Option> = 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 { + fn build_import(&mut self, def: &ImportDef) -> Result, Box> { + let sym = &def.name; + let positioned_sym = sym.into(); if !self.files.contains(&def.path.fragment) { // Only parse the file once on import. - let sym = &def.name; - let positioned_sym = sym.into(); 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, Box> { 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, Box> { match stmt { - &Statement::Let(ref def) => { - try!(self.build_let(def)); - } - &Statement::Import(ref def) => { - try!(self.build_import(def)); - } - &Statement::Expression(ref expr) => { - self.last = Some(try!(self.eval_expr(expr))); - } - }; - Ok(()) + &Statement::Let(ref def) => self.build_let(def), + &Statement::Import(ref def) => self.build_import(def), + &Statement::Expression(ref expr) => self.eval_expr(expr), + } } fn lookup_sym(&self, sym: &Positioned) -> Option> { @@ -859,21 +875,27 @@ impl Builder { fn eval_binary(&self, def: &BinaryOpDef) -> Result, Box> { 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, Box> { + 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, Box> { - // 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>(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( diff --git a/src/convert/env.rs b/src/convert/env.rs index d77f245..627629a 100644 --- a/src/convert/env.rs +++ b/src/convert/env.rs @@ -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, Rc)>, + flds: &Vec<(Positioned, Rc)>, w: &mut Write, ) -> Result<()> { for &(ref name, ref val) in flds.iter() { diff --git a/src/convert/json.rs b/src/convert/json.rs index bc086bc..22a73f8 100644 --- a/src/convert/json.rs +++ b/src/convert/json.rs @@ -36,7 +36,7 @@ impl JsonConverter { fn convert_tuple( &self, - items: &Vec<(ast::Positioned, Rc)>, + items: &Vec<(ast::tree::Positioned, Rc)>, ) -> Result { let mut mp = serde_json::Map::new(); for &(ref k, ref v) in items.iter() { diff --git a/src/error.rs b/src/error.rs index 462f312..b6ab6a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ use std::error; use std::fmt; -use ast::*; +use ast::tree::*; use nom; diff --git a/src/format.rs b/src/format.rs index 4f8f627..44ec36e 100644 --- a/src/format.rs +++ b/src/format.rs @@ -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. diff --git a/src/lib.rs b/src/lib.rs index 4137fc0..1ee1e8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/parse.rs b/src/parse.rs index 51b4bfe..93b538d 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -21,7 +21,7 @@ use nom; use nom::InputLength; use nom::IResult; -use ast::*; +use ast::tree::*; use tokenizer::*; use error; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index d0f00be..0d5c595 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -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;