mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
split ast handling into a separate module directory.
This commit is contained in:
parent
65a3c48110
commit
4265b0177b
18
src/ast/mod.rs
Normal file
18
src/ast/mod.rs
Normal 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;
|
@ -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<str> 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<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),
|
||||
}),
|
454
src/build.rs
454
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<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 {
|
||||
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.
|
||||
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<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::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<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(
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
use ast::*;
|
||||
use ast::tree::*;
|
||||
|
||||
use nom;
|
||||
|
||||
|
@ -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.
|
||||
|
31
src/lib.rs
31
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;
|
||||
|
@ -21,7 +21,7 @@ use nom;
|
||||
use nom::InputLength;
|
||||
use nom::IResult;
|
||||
|
||||
use ast::*;
|
||||
use ast::tree::*;
|
||||
use tokenizer::*;
|
||||
use error;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user