mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
Add comparison operators.
This commit is contained in:
parent
f132449379
commit
37bb75b891
3
TODO.md
3
TODO.md
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
## Boolean operations and type
|
## Boolean operations and type
|
||||||
|
|
||||||
* equality (for everything)
|
|
||||||
* contains (for lists or strings)
|
* contains (for lists or strings)
|
||||||
* less than or greater than (for numeric types)
|
|
||||||
|
|
||||||
## Query Language (Experimental)
|
## Query Language (Experimental)
|
||||||
|
|
||||||
@ -26,6 +24,7 @@ Some options here could be:
|
|||||||
|
|
||||||
# Minor Fixes and Polish
|
# Minor Fixes and Polish
|
||||||
|
|
||||||
|
* Casting between types?
|
||||||
* Better error messages.
|
* Better error messages.
|
||||||
* Allow trailing commas.
|
* Allow trailing commas.
|
||||||
* Flags should allow different seperators for prefixed flags.
|
* Flags should allow different seperators for prefixed flags.
|
||||||
|
@ -74,8 +74,6 @@ pub enum TokenType {
|
|||||||
PUNCT,
|
PUNCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(jwall): We should probably implement copy for this.
|
|
||||||
|
|
||||||
/// Defines a Token representing a building block of UCG syntax.
|
/// Defines a Token representing a building block of UCG syntax.
|
||||||
///
|
///
|
||||||
/// Token's are passed to the parser stage to be parsed into an AST.
|
/// Token's are passed to the parser stage to be parsed into an AST.
|
||||||
@ -604,6 +602,12 @@ pub enum BinaryExprType {
|
|||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
|
Equal,
|
||||||
|
GT,
|
||||||
|
LT,
|
||||||
|
NotEqual,
|
||||||
|
GTEqual,
|
||||||
|
LTEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an expression with a left and a right side.
|
/// Represents an expression with a left and a right side.
|
||||||
|
404
src/build.rs
404
src/build.rs
@ -113,6 +113,65 @@ impl Val {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jwall): Unit Tests for this.
|
||||||
|
pub fn equal(&self, target: &Self, pos: Position) -> Result<bool, error::Error> {
|
||||||
|
// first we do a type equality comparison
|
||||||
|
match (self, target) {
|
||||||
|
// Empty values are always equal.
|
||||||
|
(&Val::Empty, &Val::Empty) => Ok(true),
|
||||||
|
(&Val::Int(ref i), &Val::Int(ref ii)) => Ok(i == ii),
|
||||||
|
(&Val::Float(ref f), &Val::Float(ref ff)) => Ok(f == ff),
|
||||||
|
(&Val::Boolean(ref b), &Val::Boolean(ref bb)) => Ok(b == bb),
|
||||||
|
(&Val::String(ref s), &Val::String(ref ss)) => Ok(s == ss),
|
||||||
|
(&Val::List(ref ldef), &Val::List(ref lldef)) => {
|
||||||
|
if ldef.len() != lldef.len() {
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
for (i, v) in ldef.iter().enumerate() {
|
||||||
|
// TODO(jwall): We should probably do a slightly better error message here.
|
||||||
|
try!(v.equal(lldef[i].as_ref(), pos.clone()));
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(&Val::Tuple(ref ldef), &Val::Tuple(ref lldef)) => {
|
||||||
|
if ldef.len() != lldef.len() {
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
for (i, v) in ldef.iter().enumerate() {
|
||||||
|
let field_target = &lldef[i];
|
||||||
|
eprintln!(
|
||||||
|
"left field: '{}', right field: '{}'",
|
||||||
|
v.0.val, field_target.0.val
|
||||||
|
);
|
||||||
|
if v.0.val != field_target.0.val {
|
||||||
|
// field name equality
|
||||||
|
eprintln!("Field Not equal!!!");
|
||||||
|
return Ok(false);
|
||||||
|
} else {
|
||||||
|
eprintln!("Field Equal!!!");
|
||||||
|
// field value equality.
|
||||||
|
if !try!(v.1.equal(field_target.1.as_ref(), v.0.pos.clone())) {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(&Val::Macro(_), &Val::Macro(_)) => Err(error::Error::new(
|
||||||
|
"Macros are not comparable",
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos,
|
||||||
|
)),
|
||||||
|
(me, tgt) => Err(error::Error::new(
|
||||||
|
format!("Types differ for {}, {}", me, tgt),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the fields if this Val is a tuple. None otherwise.
|
/// Returns the fields if this Val is a tuple. None otherwise.
|
||||||
pub fn get_fields(&self) -> Option<&Vec<(Positioned<String>, Rc<Val>)>> {
|
pub fn get_fields(&self) -> Option<&Vec<(Positioned<String>, Rc<Val>)>> {
|
||||||
if let &Val::Tuple(ref fs) = self {
|
if let &Val::Tuple(ref fs) = self {
|
||||||
@ -676,6 +735,128 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_deep_equal(
|
||||||
|
&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>,
|
||||||
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
Ok(Rc::new(Val::Boolean(try!(
|
||||||
|
left.equal(right.as_ref(), pos.clone())
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_not_deep_equal(
|
||||||
|
&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>,
|
||||||
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
Ok(Rc::new(Val::Boolean(!try!(
|
||||||
|
left.equal(right.as_ref(), pos.clone())
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_gt(&self, pos: &Position, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
// first ensure that left and right are numeric vals of the same type.
|
||||||
|
if let &Val::Int(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Int(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l > r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let &Val::Float(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Float(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l > r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Box::new(error::Error::new(
|
||||||
|
format!(
|
||||||
|
"Incompatible types for numeric comparison {} with {}",
|
||||||
|
left.type_name(),
|
||||||
|
right.type_name()
|
||||||
|
),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_lt(&self, pos: &Position, left: Rc<Val>, right: Rc<Val>) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
// first ensure that left and right are numeric vals of the same type.
|
||||||
|
if let &Val::Int(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Int(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l < r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let &Val::Float(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Float(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l < r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Box::new(error::Error::new(
|
||||||
|
format!(
|
||||||
|
"Incompatible types for numeric comparison {} with {}",
|
||||||
|
left.type_name(),
|
||||||
|
right.type_name()
|
||||||
|
),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_ltequal(
|
||||||
|
&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>,
|
||||||
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
if let &Val::Int(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Int(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l <= r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let &Val::Float(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Float(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l <= r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Box::new(error::Error::new(
|
||||||
|
format!(
|
||||||
|
"Incompatible types for numeric comparison {} with {}",
|
||||||
|
left.type_name(),
|
||||||
|
right.type_name()
|
||||||
|
),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_gtequal(
|
||||||
|
&self,
|
||||||
|
pos: &Position,
|
||||||
|
left: Rc<Val>,
|
||||||
|
right: Rc<Val>,
|
||||||
|
) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
if let &Val::Int(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Int(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l >= r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let &Val::Float(ref l) = left.as_ref() {
|
||||||
|
if let &Val::Float(ref r) = right.as_ref() {
|
||||||
|
return Ok(Rc::new(Val::Boolean(l >= r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Box::new(error::Error::new(
|
||||||
|
format!(
|
||||||
|
"Incompatible types for numeric comparison {} with {}",
|
||||||
|
left.type_name(),
|
||||||
|
right.type_name()
|
||||||
|
),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
pos.clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_binary(&self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_binary(&self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let kind = &def.kind;
|
let kind = &def.kind;
|
||||||
let v = &def.left;
|
let v = &def.left;
|
||||||
@ -687,6 +868,12 @@ impl Builder {
|
|||||||
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
|
&BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Mul => self.multiply_vals(&def.pos, left, right),
|
&BinaryExprType::Mul => self.multiply_vals(&def.pos, left, right),
|
||||||
&BinaryExprType::Div => self.divide_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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,6 +1334,223 @@ 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]
|
#[test]
|
||||||
fn test_eval_simple_expr() {
|
fn test_eval_simple_expr() {
|
||||||
test_expr_to_val(
|
test_expr_to_val(
|
||||||
|
67
src/lib.rs
67
src/lib.rs
@ -97,7 +97,7 @@
|
|||||||
//! character.
|
//! character.
|
||||||
//!
|
//!
|
||||||
//! ``` ucg
|
//! ``` ucg
|
||||||
//! "foo"; // a smiple string
|
//! "foo"; // a simple string
|
||||||
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
@ -216,16 +216,75 @@
|
|||||||
//!
|
//!
|
||||||
//! #### Binary operators
|
//! #### Binary operators
|
||||||
//!
|
//!
|
||||||
//! ucg supports the following operators, +, -, *, /; Each one is type safe and infers the types from the values they operate on.
|
//! ##### Numeric operators
|
||||||
//! The operators expect both the left and right operands to be of the same type. All of the operators are valid on integers and floats.
|
//!
|
||||||
//! The + operator can additionally concatenate strings or arrays.
|
//! ucg supports the following numeric 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.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! 1 + 1; // result is 2
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ##### Concatenation
|
||||||
|
//!
|
||||||
|
//! ucg supports concatenation using the `+` operator. It is typesafe expecting both sides to be of the same type.
|
||||||
|
//! You can concatenate strings or lists but not tuples.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! "foo " + "bar" // result is "foo bar"
|
||||||
|
//! [1,2] + [3,4]; // result is [1,2,3,4]
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ##### Comparison
|
||||||
|
//!
|
||||||
|
//! ucg supports comparison using the `==`, `!=`, `>`, `<`, `>=`, `<=` operators. They are type safe and expect both
|
||||||
|
//! sides to be of the same type.
|
||||||
|
//!
|
||||||
|
//! The `>`, `<`, `>=`, and `>=` operators are only supported on numeric types.
|
||||||
|
//!
|
||||||
|
//! ``ucg
|
||||||
|
//! 1 > 2; // result is false
|
||||||
|
//! 2 < 3; // result is true
|
||||||
|
//! 10 > "9"; // This is a compile error.
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The equality operators `==` and `!=` are supported for all types and will perform deep equal comparisons on complex
|
||||||
|
//! types.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! let tpl1 = {
|
||||||
|
//! foo = "bar",
|
||||||
|
//! one = 1
|
||||||
|
//! };
|
||||||
|
//! let tpl2 = tpl1{}; // copy the tpl1 tuple
|
||||||
|
//! tpl1 == tpl2; // returns true
|
||||||
|
//! let tpl3 = tpl1{duck="quack"};
|
||||||
|
//! tpl1 == tpl3; // returns false
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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
|
||||||
|
//!
|
||||||
|
//! 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
|
//! ```ucg
|
||||||
//! 1 + 1; // result is 2
|
//! 1 + 1; // result is 2
|
||||||
//! "foo " + "bar" // result is "foo bar"
|
//! "foo " + "bar" // result is "foo bar"
|
||||||
//! [1,2] + [3,4]; // result is [1,2,3,4]
|
//! [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;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! The Equality comparison can be done on any type and will perform a deep equal comparison.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
//! #### Copy expressions
|
//! #### 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
|
//! ucg Tuples support a form of reuse with copy on write semantics. You can copy a tuple and selectively overwrite fields or add new
|
||||||
|
67
src/parse.rs
67
src/parse.rs
@ -286,6 +286,30 @@ named!(div_expression<TokenIter, Expression, error::Error>,
|
|||||||
do_binary_expr!(punct!("/"), BinaryExprType::Div)
|
do_binary_expr!(punct!("/"), BinaryExprType::Div)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(eqeq_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!("=="), BinaryExprType::Equal)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(not_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!("!="), BinaryExprType::NotEqual)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(lt_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!("<="), BinaryExprType::LTEqual)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(gt_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!(">="), BinaryExprType::GTEqual)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(gt_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!(">"), BinaryExprType::GT)
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(lt_expression<TokenIter, Expression, error::Error>,
|
||||||
|
do_binary_expr!(punct!("<"), BinaryExprType::LT)
|
||||||
|
);
|
||||||
|
|
||||||
fn expression_to_grouped_expression(e: Expression) -> ParseResult<Expression> {
|
fn expression_to_grouped_expression(e: Expression) -> ParseResult<Expression> {
|
||||||
Ok(Expression::Grouped(Box::new(e)))
|
Ok(Expression::Grouped(Box::new(e)))
|
||||||
}
|
}
|
||||||
@ -652,6 +676,12 @@ named!(expression<TokenIter, Expression, error::Error>,
|
|||||||
complete!(sub_expression) |
|
complete!(sub_expression) |
|
||||||
complete!(mul_expression) |
|
complete!(mul_expression) |
|
||||||
complete!(div_expression) |
|
complete!(div_expression) |
|
||||||
|
complete!(eqeq_expression) |
|
||||||
|
complete!(not_eqeq_expression) |
|
||||||
|
complete!(lt_eqeq_expression) |
|
||||||
|
complete!(gt_eqeq_expression) |
|
||||||
|
complete!(gt_expression) |
|
||||||
|
complete!(lt_expression) |
|
||||||
complete!(grouped_expression) |
|
complete!(grouped_expression) |
|
||||||
complete!(macro_expression) |
|
complete!(macro_expression) |
|
||||||
complete!(format_expression) |
|
complete!(format_expression) |
|
||||||
@ -1135,7 +1165,42 @@ mod test {
|
|||||||
pos: Position::new(1, 1),
|
pos: Position::new(1, 1),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
assert_parse!(
|
||||||
|
expression("1 > 1"),
|
||||||
|
Expression::Binary(BinaryOpDef {
|
||||||
|
kind: BinaryExprType::GT,
|
||||||
|
left: Value::Int(value_node!(1, 1, 1)),
|
||||||
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))),
|
||||||
|
pos: Position::new(1, 1),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_parse!(
|
||||||
|
expression("1 < 1"),
|
||||||
|
Expression::Binary(BinaryOpDef {
|
||||||
|
kind: BinaryExprType::LT,
|
||||||
|
left: Value::Int(value_node!(1, 1, 1)),
|
||||||
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))),
|
||||||
|
pos: Position::new(1, 1),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_parse!(
|
||||||
|
expression("1 <= 1"),
|
||||||
|
Expression::Binary(BinaryOpDef {
|
||||||
|
kind: BinaryExprType::LTEqual,
|
||||||
|
left: Value::Int(value_node!(1, 1, 1)),
|
||||||
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))),
|
||||||
|
pos: Position::new(1, 1),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_parse!(
|
||||||
|
expression("1 >= 1"),
|
||||||
|
Expression::Binary(BinaryOpDef {
|
||||||
|
kind: BinaryExprType::GTEqual,
|
||||||
|
left: Value::Int(value_node!(1, 1, 1)),
|
||||||
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))),
|
||||||
|
pos: Position::new(1, 1),
|
||||||
|
})
|
||||||
|
);
|
||||||
assert_parse!(
|
assert_parse!(
|
||||||
expression("1+1"),
|
expression("1+1"),
|
||||||
Expression::Binary(BinaryOpDef {
|
Expression::Binary(BinaryOpDef {
|
||||||
|
@ -181,6 +181,32 @@ named!(pcttok( Span ) -> Token,
|
|||||||
do_tag_tok!(TokenType::PUNCT, "%")
|
do_tag_tok!(TokenType::PUNCT, "%")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO(jwall): Comparison operators.
|
||||||
|
|
||||||
|
named!(eqeqtok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, "==")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(notequaltok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, "!=")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(gttok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, ">")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(gtequaltok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, ">=")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(ltequaltok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, "<=")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(lttok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::PUNCT, "<")
|
||||||
|
);
|
||||||
|
|
||||||
named!(equaltok( Span ) -> Token,
|
named!(equaltok( Span ) -> Token,
|
||||||
do_tag_tok!(TokenType::PUNCT, "=")
|
do_tag_tok!(TokenType::PUNCT, "=")
|
||||||
);
|
);
|
||||||
@ -310,6 +336,12 @@ named!(token( Span ) -> Token,
|
|||||||
comment | // Note comment must come before slashtok
|
comment | // Note comment must come before slashtok
|
||||||
slashtok |
|
slashtok |
|
||||||
pcttok |
|
pcttok |
|
||||||
|
eqeqtok |
|
||||||
|
notequaltok |
|
||||||
|
complete!(gtequaltok) |
|
||||||
|
complete!(ltequaltok) |
|
||||||
|
gttok |
|
||||||
|
lttok |
|
||||||
fatcommatok | // Note fatcommatok must come before equaltok
|
fatcommatok | // Note fatcommatok must come before equaltok
|
||||||
equaltok |
|
equaltok |
|
||||||
semicolontok |
|
semicolontok |
|
||||||
@ -670,32 +702,68 @@ mod tokenizer_test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
macro_rules! assert_token {
|
||||||
fn test_boolean() {
|
($input:expr, $typ:expr, $msg:expr) => {
|
||||||
let result = token(LocatedSpan::new("true"));
|
let result = token(LocatedSpan::new($input));
|
||||||
assert!(
|
assert!(
|
||||||
result.is_done(),
|
result.is_done(),
|
||||||
format!("result {:?} is not a boolean", result)
|
format!("result {:?} is not a {}", result, $msg)
|
||||||
);
|
);
|
||||||
if let nom::IResult::Done(_, tok) = result {
|
if let nom::IResult::Done(_, tok) = result {
|
||||||
assert_eq!(tok.fragment, "true");
|
assert_eq!(tok.fragment, $input);
|
||||||
assert_eq!(tok.typ, TokenType::BOOLEAN);
|
assert_eq!(tok.typ, $typ);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boolean() {
|
||||||
|
assert_token!("true", TokenType::BOOLEAN, "boolean");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eqeqtok() {
|
||||||
|
assert_token!("==", TokenType::PUNCT, "==");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_notequaltok() {
|
||||||
|
assert_token!("!=", TokenType::PUNCT, "!=");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gttok() {
|
||||||
|
assert_token!(">", TokenType::PUNCT, ">");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lttok() {
|
||||||
|
assert_token!("<", TokenType::PUNCT, "<");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gteqtok() {
|
||||||
|
assert_token!(">=", TokenType::PUNCT, ">=");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lteqtok() {
|
||||||
|
assert_token!("<=", TokenType::PUNCT, "<=");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize_one_of_each() {
|
fn test_tokenize_one_of_each() {
|
||||||
let result = tokenize(LocatedSpan::new(
|
let result = tokenize(LocatedSpan::new(
|
||||||
"let import macro select as => [ ] { } ; = % / * \
|
"let import macro select as => [ ] { } ; = % / * \
|
||||||
+ - . ( ) , 1 . foo \"bar\" // comment\n ; true false",
|
+ - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=",
|
||||||
));
|
));
|
||||||
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||||
let v = result.unwrap();
|
let v = result.unwrap();
|
||||||
for (i, t) in v.iter().enumerate() {
|
for (i, t) in v.iter().enumerate() {
|
||||||
println!("{}: {:?}", i, t);
|
println!("{}: {:?}", i, t);
|
||||||
}
|
}
|
||||||
assert_eq!(v.len(), 29);
|
assert_eq!(v.len(), 35);
|
||||||
assert_eq!(v[28].typ, TokenType::END);
|
assert_eq!(v[34].typ, TokenType::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user