Add Boolean types.

This commit is contained in:
Jeremy Wall 2018-03-22 20:09:38 -05:00
parent eeac1ba599
commit bfde2c5238
8 changed files with 94 additions and 4 deletions

View File

@ -87,6 +87,7 @@ impl Position {
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub enum TokenType { pub enum TokenType {
EMPTY, EMPTY,
BOOLEAN,
END, END,
WS, WS,
COMMENT, COMMENT,
@ -330,6 +331,7 @@ impl SelectorDef {
pub enum Value { pub enum Value {
// Constant Values // Constant Values
Empty(Position), Empty(Position),
Boolean(Positioned<bool>),
Int(Positioned<i64>), Int(Positioned<i64>),
Float(Positioned<f64>), Float(Positioned<f64>),
String(Positioned<String>), String(Positioned<String>),
@ -345,6 +347,7 @@ impl Value {
pub fn type_name(&self) -> String { pub fn type_name(&self) -> String {
match self { match self {
&Value::Empty(_) => "EmptyValue".to_string(), &Value::Empty(_) => "EmptyValue".to_string(),
&Value::Boolean(_) => "Boolean".to_string(),
&Value::Int(_) => "Integer".to_string(), &Value::Int(_) => "Integer".to_string(),
&Value::Float(_) => "Float".to_string(), &Value::Float(_) => "Float".to_string(),
&Value::String(_) => "String".to_string(), &Value::String(_) => "String".to_string(),
@ -375,6 +378,7 @@ impl Value {
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match self { match self {
&Value::Empty(_) => "EmptyValue".to_string(), &Value::Empty(_) => "EmptyValue".to_string(),
&Value::Boolean(ref b) => format!("{}", b.val),
&Value::Int(ref i) => format!("{}", i.val), &Value::Int(ref i) => format!("{}", i.val),
&Value::Float(ref f) => format!("{}", f.val), &Value::Float(ref f) => format!("{}", f.val),
&Value::String(ref s) => format!("{}", s.val), &Value::String(ref s) => format!("{}", s.val),
@ -389,6 +393,7 @@ impl Value {
pub fn pos(&self) -> &Position { pub fn pos(&self) -> &Position {
match self { match self {
&Value::Empty(ref pos) => pos, &Value::Empty(ref pos) => pos,
&Value::Boolean(ref b) => &b.pos,
&Value::Int(ref i) => &i.pos, &Value::Int(ref i) => &i.pos,
&Value::Float(ref f) => &f.pos, &Value::Float(ref f) => &f.pos,
&Value::String(ref s) => &s.pos, &Value::String(ref s) => &s.pos,
@ -405,6 +410,7 @@ impl Value {
self, self,
target, target,
&Value::Empty(_), &Value::Empty(_),
&Value::Boolean(_),
&Value::Int(_), &Value::Int(_),
&Value::Float(_), &Value::Float(_),
&Value::String(_), &Value::String(_),

View File

@ -73,6 +73,7 @@ type BuildResult = Result<(), Box<Error>>;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub enum Val { pub enum Val {
Empty, Empty,
Boolean(bool),
Int(i64), Int(i64),
Float(f64), Float(f64),
String(String), String(String),
@ -86,6 +87,7 @@ impl Val {
pub fn type_name(&self) -> String { pub fn type_name(&self) -> String {
match self { match self {
&Val::Empty => "EmptyValue".to_string(), &Val::Empty => "EmptyValue".to_string(),
&Val::Boolean(_) => "Boolean".to_string(),
&Val::Int(_) => "Integer".to_string(), &Val::Int(_) => "Integer".to_string(),
&Val::Float(_) => "Float".to_string(), &Val::Float(_) => "Float".to_string(),
&Val::String(_) => "String".to_string(), &Val::String(_) => "String".to_string(),
@ -101,6 +103,7 @@ impl Val {
self, self,
target, target,
&Val::Empty, &Val::Empty,
&Val::Boolean(_),
&Val::Int(_), &Val::Int(_),
&Val::Float(_), &Val::Float(_),
&Val::String(_), &Val::String(_),
@ -172,6 +175,7 @@ impl Val {
impl Display for Val { impl Display for Val {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
&Val::Boolean(b) => write!(f, "Boolean({})", b),
&Val::Empty => write!(f, "EmptyValue"), &Val::Empty => write!(f, "EmptyValue"),
&Val::Float(ref ff) => write!(f, "Float({})", ff), &Val::Float(ref ff) => write!(f, "Float({})", ff),
&Val::Int(ref i) => write!(f, "Int({})", i), &Val::Int(ref i) => write!(f, "Int({})", i),
@ -267,6 +271,7 @@ impl Builder {
fn value_to_val(&self, v: &Value) -> Result<Rc<Val>, Box<Error>> { fn value_to_val(&self, v: &Value) -> Result<Rc<Val>, Box<Error>> {
match v { match v {
&Value::Empty(_) => Ok(Rc::new(Val::Empty)), &Value::Empty(_) => Ok(Rc::new(Val::Empty)),
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
&Value::Int(ref i) => Ok(Rc::new(Val::Int(i.val))), &Value::Int(ref i) => Ok(Rc::new(Val::Int(i.val))),
&Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))), &Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))),
&Value::String(ref s) => Ok(Rc::new(Val::String(s.val.to_string()))), &Value::String(ref s) => Ok(Rc::new(Val::String(s.val.to_string()))),

View File

@ -63,6 +63,9 @@ impl EnvConverter {
// Empty is a noop. // Empty is a noop.
return Ok(()); return Ok(());
} }
&Val::Boolean(b) => {
try!(write!(w, "{} ", if b { "true" } else { "false" }));
}
&Val::Float(ref f) => { &Val::Float(ref f) => {
try!(write!(w, "{} ", f)); try!(write!(w, "{} ", f));
} }

View File

@ -66,6 +66,9 @@ impl FlagConverter {
// Empty is a noop. // Empty is a noop.
return Ok(()); return Ok(());
} }
&Val::Boolean(b) => {
try!(write!(w, "{}", if b { "true" } else { "false" }));
}
&Val::Float(ref f) => { &Val::Float(ref f) => {
try!(write!(w, "{} ", f)); try!(write!(w, "{} ", f));
} }

View File

@ -48,6 +48,7 @@ impl JsonConverter {
fn convert_value(&self, v: &Val) -> Result<serde_json::Value> { fn convert_value(&self, v: &Val) -> Result<serde_json::Value> {
let jsn_val = match v { let jsn_val = match v {
&Val::Boolean(b) => serde_json::Value::Bool(b),
&Val::Empty => serde_json::Value::Null, &Val::Empty => serde_json::Value::Null,
&Val::Float(f) => { &Val::Float(f) => {
let n = match serde_json::Number::from_f64(f) { let n = match serde_json::Number::from_f64(f) {

View File

@ -48,6 +48,8 @@
//! //!
//! The following words are reserved in ucg and can't be used as named bindings. //! The following words are reserved in ucg and can't be used as named bindings.
//! //!
//! * true
//! * false
//! * let //! * let
//! * import //! * import
//! * as //! * as
@ -60,7 +62,16 @@
//! //!
//! ### Primitive types //! ### Primitive types
//! //!
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String. //! ucg has a relatively simple syntax with a few primitive types, Null, Boolean, Int, Float, and String.
//!
//! ### Boolean
//!
//! A Boolean is either `true` or `false`.
//!
//! ```uct
//! true;
//! false;
//! ```
//! //!
//! #### Int //! #### Int
//! //!

View File

@ -138,6 +138,16 @@ named!(number<TokenIter, Value, ParseError>,
); );
// trace_macros!(false); // trace_macros!(false);
named!(boolean_value<TokenIter, Value, ParseError>,
do_parse!(
b: match_type!(BOOLEAN) >>
(Value::Boolean(Positioned{
val: b.fragment == "true",
pos: b.pos,
}))
)
);
named!( named!(
field_value<TokenIter, (Token, Expression), ParseError>, field_value<TokenIter, (Token, Expression), ParseError>,
do_parse!( do_parse!(
@ -205,6 +215,7 @@ named!(empty_value<TokenIter, Value, ParseError>,
named!(value<TokenIter, Value, ParseError>, named!(value<TokenIter, Value, ParseError>,
alt!( alt!(
boolean_value |
empty_value | empty_value |
number | number |
quoted_value | quoted_value |
@ -816,6 +827,19 @@ mod test {
assert_parse!(empty_value("NULL"), Value::Empty(Position::new(1, 1))); assert_parse!(empty_value("NULL"), Value::Empty(Position::new(1, 1)));
} }
#[test]
fn test_boolean_parsing() {
assert_parse!(
boolean_value("true"),
Value::Boolean(Positioned::new(true, 1, 1))
);
assert_parse!(
boolean_value("false"),
Value::Boolean(Positioned::new(false, 1, 1))
);
assert_error!(boolean_value("truth"));
}
#[test] #[test]
fn test_symbol_parsing() { fn test_symbol_parsing() {
assert_parse!( assert_parse!(

View File

@ -97,6 +97,21 @@ named!(digittok( Span ) -> Token,
) )
); );
named!(booleantok( Span ) -> Token,
do_parse!(
span: position!() >>
b: alt!(
tag!("true") |
tag!("false")
) >>
(Token{
typ: TokenType::BOOLEAN,
pos: Position::from(span),
fragment: b.fragment.to_string(),
})
)
);
/// do_tag_tok! is a helper macro to make building a simple tag token /// do_tag_tok! is a helper macro to make building a simple tag token
/// less code. /// less code.
macro_rules! do_tag_tok { macro_rules! do_tag_tok {
@ -299,6 +314,7 @@ named!(token( Span ) -> Token,
semicolontok | semicolontok |
leftsquarebracket | leftsquarebracket |
rightsquarebracket | rightsquarebracket |
booleantok |
lettok | lettok |
selecttok | selecttok |
macrotok | macrotok |
@ -369,6 +385,14 @@ pub fn token_clone(t: &Token) -> Result<Token, ParseError> {
/// nom macro that matches a Token by type and uses an optional conversion handler /// nom macro that matches a Token by type and uses an optional conversion handler
/// for the matched Token. /// for the matched Token.
macro_rules! match_type { macro_rules! match_type {
($i:expr, BOOLEAN => $h:expr) => {
match_type!($i, TokenType::BOOLEAN, "Not a Boolean", $h)
};
($i:expr, BOOLEAN) => {
match_type!($i, BOOLEAN => token_clone)
};
($i:expr, COMMENT => $h:expr) => { ($i:expr, COMMENT => $h:expr) => {
match_type!($i, TokenType::COMMENT, "Not a Comment", $h) match_type!($i, TokenType::COMMENT, "Not a Comment", $h)
}; };
@ -645,19 +669,32 @@ mod tokenizer_test {
} }
} }
#[test]
fn test_boolean() {
let result = token(LocatedSpan::new("true"));
assert!(
result.is_done(),
format!("result {:?} is not a boolean", result)
);
if let nom::IResult::Done(_, tok) = result {
assert_eq!(tok.fragment, "true");
assert_eq!(tok.typ, TokenType::BOOLEAN);
}
}
#[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 ;", + - . ( ) , 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(), 27); assert_eq!(v.len(), 29);
assert_eq!(v[26].typ, TokenType::END); assert_eq!(v[28].typ, TokenType::END);
} }
#[test] #[test]