From fdd8a350862262e810e2633f457db7d3f770e616 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 22 May 2018 18:02:44 -0500 Subject: [PATCH] CLEANUP: The great test module refactor. Move tests into a separate file for more manageable file organization. --- Cargo.toml | 2 +- src/ast/mod.rs | 728 ++++++++- src/ast/test.rs | 99 ++ src/ast/tree.rs | 839 ---------- src/build/compile_test.rs | 86 ++ src/{build.rs => build/mod.rs} | 898 +---------- src/build/test.rs | 772 ++++++++++ src/convert/env.rs | 2 +- src/convert/json.rs | 2 +- src/error.rs | 2 +- src/format.rs | 4 +- src/lib.rs | 8 +- src/parse.rs | 1967 ------------------------ src/parse/mod.rs | 864 +++++++++++ src/parse/test.rs | 1099 +++++++++++++ src/{tokenizer.rs => tokenizer/mod.rs} | 263 +--- src/tokenizer/test.rs | 258 ++++ 17 files changed, 3919 insertions(+), 3974 deletions(-) create mode 100644 src/ast/test.rs delete mode 100644 src/ast/tree.rs create mode 100644 src/build/compile_test.rs rename src/{build.rs => build/mod.rs} (55%) create mode 100644 src/build/test.rs delete mode 100644 src/parse.rs create mode 100644 src/parse/mod.rs create mode 100644 src/parse/test.rs rename src/{tokenizer.rs => tokenizer/mod.rs} (69%) create mode 100644 src/tokenizer/test.rs diff --git a/Cargo.toml b/Cargo.toml index 0a19d82..c1c11f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ucg" -version = "0.1.0" +version = "0.1.1" authors = ["Jeremy Wall "] description = "A configuration generation grammar." repository = "https://github.com/zaphar/ucg" diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2710913..bf6fccf 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -14,5 +14,729 @@ //! The definitions of the ucg AST and Tokens. -#[macro_use] -pub mod tree; +use std; +use std::borrow::Borrow; +use std::cmp::Eq; +use std::cmp::Ordering; +use std::cmp::PartialEq; +use std::cmp::PartialOrd; +use std::collections::HashSet; +use std::convert::Into; +use std::hash::Hash; +use std::hash::Hasher; + +macro_rules! enum_type_equality { + ( $slf:ident, $r:expr, $( $l:pat ),* ) => { + match $slf { + $( + $l => { + if let $l = $r { + true + } else { + false + } + } + )* + } + } +} + +/// Represents a line and a column position in UCG code. +/// +/// It is used for generating error messages mostly. Most all +/// parts of the UCG AST have a positioned associated with them. +#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +pub struct Position { + pub line: usize, + pub column: usize, +} + +impl Position { + /// Construct a new Position. + pub fn new(line: usize, column: usize) -> Self { + Position { + line: line, + column: column, + } + } +} + +/// Defines the types of tokens in UCG syntax. +#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +pub enum TokenType { + EMPTY, + BOOLEAN, + END, + WS, + COMMENT, + QUOTED, + DIGIT, + BAREWORD, + PUNCT, +} + +/// Defines a Token representing a building block of UCG syntax. +/// +/// Token's are passed to the parser stage to be parsed into an AST. +#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +pub struct Token { + pub typ: TokenType, + pub fragment: String, + pub pos: Position, +} + +impl Token { + /// Constructs a new Token with a type and line and column information. + pub fn new>(f: S, typ: TokenType, line: usize, col: usize) -> Self { + Self::new_with_pos(f, typ, Position::new(line, col)) + } + + // Constructs a new Token with a type and a Position. + pub fn new_with_pos>(f: S, typ: TokenType, pos: Position) -> Self { + Token { + typ: typ, + fragment: f.into(), + pos: pos, + } + } +} + +impl Borrow for Token { + fn borrow(&self) -> &str { + &self.fragment + } +} + +/// Helper macro for making a Positioned Value. +macro_rules! value_node { + ($v:expr, $p:expr) => { + Positioned::new_with_pos($v, $p) + }; + ($v:expr, $l:expr, $c:expr) => { + Positioned::new($v, $l, $c) + }; +} + +/// Helper macro for making a Token. +#[allow(unused_macros)] +macro_rules! make_tok { + (EOF => $l:expr, $c:expr) => { + Token::new("", TokenType::END, $l, $c) + }; + + (WS => $l:expr, $c:expr) => { + Token::new("", TokenType::WS, $l, $c) + }; + + (CMT => $e:expr, $l:expr, $c:expr) => { + Token::new($e, TokenType::COMMENT, $l, $c) + }; + + (QUOT => $e:expr, $l:expr, $c:expr) => { + Token::new($e, TokenType::QUOTED, $l, $c) + }; + + (PUNCT => $e:expr, $l:expr, $c:expr) => { + Token::new($e, TokenType::PUNCT, $l, $c) + }; + + (DIGIT => $e:expr, $l:expr, $c:expr) => { + Token::new($e, TokenType::DIGIT, $l, $c) + }; + + ($e:expr, $l:expr, $c:expr) => { + Token::new($e, TokenType::BAREWORD, $l, $c) + }; +} + +/// Helper macro for making expressions. +#[allow(unused_macros)] +macro_rules! make_expr { + ($e:expr) => { + make_expr!($e, 1, 1) + }; + + ($e:expr, $l:expr, $c:expr) => { + Expression::Simple(Value::Symbol(Positioned::new($e.to_string(), $l, $c))) + }; + + ($e:expr => int, $l:expr, $c:expr) => { + Expression::Simple(Value::Int(Positioned::new($e, $l, $c))) + }; +} + +/// Helper macro for making selectors. +/// +/// ``` +/// make_selector!(Token::new("tpl", 1, 1), Token::new("fld", 1, 4)); +/// +/// make_selector!(Token::new("tpl", 1, 1), vec![Token::new("fld", 1, 4)], => 1, 1); +/// +/// make_selector!(foo", ["bar"]); +/// +/// make_selector!(foo", ["bar"] => 1, 0); +/// ``` +#[allow(unused_macros)] +macro_rules! make_selector { + ( $h:expr ) => { + make_selector!($h, 1, 0) + }; + + ( $h:expr, $l:expr, $c:expr ) => { + SelectorDef::new( + SelectorList{head: Box::new($h), tail: None}, + $l, $c) + }; + + ( $h: expr, $list:expr, $l:expr, $c:expr) => { + SelectorDef::new( + SelectorList{head: Box::new($h), tail: Some($list)}, + $l, $c) + }; + + // Tokens + ( $h:expr => [ $( $item:expr ),* ] ) => { + { + make_selector!($h => [ $( $item, )* ] => 1, 1) + } + }; + + ( $h:expr => [ $( $item:expr ),* ] => $l:expr, $c:expr ) => { + { + let mut list: Vec = Vec::new(); + + $( + list.push($item); + )* + + make_selector!($h, list, $l, $c) + } + }; + + // Strings not tokens + ( $h:expr => $( $item:expr ),* ) => { + { + + let mut col = 1; + let mut list: Vec = Vec::new(); + + $( + list.push(make_tok!($item, 1, col)); + col += $item.len() + 1; + )* + + // Shut up the lint about unused code; + assert!(col != 0); + + make_selector!($h, list, 1, 1) + } + + }; + + ( $h:expr => $( $item:expr ),* => $l:expr, $c:expr ) => { + { + let mut col = $c; + let mut list: Vec = Vec::new(); + + $( + list.push(make_tok!($item, $l, col)); + col += $item.len() + 1; + )* + + // Shut up the linter about unused code; + assert!(col != 0); + + make_selector!($h, list, $l, $c) + } + }; +} + +/// An Expression with a series of symbols specifying the key +/// with which to descend into the result of the expression. +/// +/// The expression must evaluate to either a tuple or an array. The token must +/// evaluate to either a bareword Symbol or an Int. +/// +/// ```ucg +/// let foo = { bar = "a thing" }; +/// let thing = foo.bar; +/// +/// let arr = ["one", "two"]; +/// let first = arr.0; +/// +/// let berry = {best = "strawberry", unique = "acai"}.best; +/// let third = ["uno", "dos", "tres"].1; +/// ''' +#[derive(Debug, PartialEq, Clone)] +pub struct SelectorList { + pub head: Box, + pub tail: Option>, +} + +impl SelectorList { + /// Returns a stringified version of a SelectorList. + pub fn to_string(&self) -> String { + "TODO".to_string() + } +} + +/// An ordered list of Name = Value pairs. +/// +/// This is usually used as the body of a tuple in the UCG AST. +pub type FieldList = Vec<(Token, Expression)>; // Token is expected to be a symbol + +/// Encodes a selector expression in the UCG AST. +#[derive(Debug, PartialEq, Clone)] +pub struct SelectorDef { + pub pos: Position, + pub sel: SelectorList, +} + +impl SelectorDef { + /// Constructs a new SelectorDef. + pub fn new(sel: SelectorList, line: usize, col: usize) -> Self { + SelectorDef { + pos: Position::new(line, col), + sel: sel, + } + } +} + +/// Represents a Value in the UCG parsed AST. +#[derive(Debug, PartialEq, Clone)] +pub enum Value { + // Constant Values + Empty(Position), + Boolean(Positioned), + Int(Positioned), + Float(Positioned), + String(Positioned), + Symbol(Positioned), + // Complex Values + Tuple(Positioned), + List(ListDef), + Selector(SelectorDef), +} + +impl Value { + /// Returns the type name of the Value it is called on as a string. + pub fn type_name(&self) -> String { + match self { + &Value::Empty(_) => "EmptyValue".to_string(), + &Value::Boolean(_) => "Boolean".to_string(), + &Value::Int(_) => "Integer".to_string(), + &Value::Float(_) => "Float".to_string(), + &Value::String(_) => "String".to_string(), + &Value::Symbol(_) => "Symbol".to_string(), + &Value::Tuple(_) => "Tuple".to_string(), + &Value::List(_) => "List".to_string(), + &Value::Selector(_) => "Selector".to_string(), + } + } + + fn fields_to_string(v: &FieldList) -> String { + let mut buf = String::new(); + buf.push_str("{\n"); + for ref t in v.iter() { + buf.push_str("\t"); + buf.push_str(&t.0.fragment); + buf.push_str("\n"); + } + buf.push_str("}"); + return buf; + } + + fn elems_to_string(v: &Vec) -> String { + return format!("{}", v.len()); + } + + /// Returns a stringified version of the Value. + pub fn to_string(&self) -> String { + match self { + &Value::Empty(_) => "EmptyValue".to_string(), + &Value::Boolean(ref b) => format!("{}", b.val), + &Value::Int(ref i) => format!("{}", i.val), + &Value::Float(ref f) => format!("{}", f.val), + &Value::String(ref s) => format!("{}", s.val), + &Value::Symbol(ref s) => format!("{}", s.val), + &Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(&fs.val)), + &Value::List(ref def) => format!("[{}]", Self::elems_to_string(&def.elems)), + &Value::Selector(ref v) => v.sel.to_string(), + } + } + + /// Returns the position for a Value. + pub fn pos(&self) -> &Position { + match self { + &Value::Empty(ref pos) => pos, + &Value::Boolean(ref b) => &b.pos, + &Value::Int(ref i) => &i.pos, + &Value::Float(ref f) => &f.pos, + &Value::String(ref s) => &s.pos, + &Value::Symbol(ref s) => &s.pos, + &Value::Tuple(ref fs) => &fs.pos, + &Value::List(ref def) => &def.pos, + &Value::Selector(ref v) => &v.pos, + } + } + + /// Returns true if called on a Value that is the same type as itself. + pub fn type_equal(&self, target: &Self) -> bool { + enum_type_equality!( + self, + target, + &Value::Empty(_), + &Value::Boolean(_), + &Value::Int(_), + &Value::Float(_), + &Value::String(_), + &Value::Symbol(_), + &Value::Tuple(_), + &Value::List(_), + &Value::Selector(_) + ) + } +} + +/// Represents an expansion of a Macro that is expected to already have been +/// defined. +#[derive(PartialEq, Debug, Clone)] +pub struct CallDef { + pub macroref: SelectorDef, + pub arglist: Vec, + pub pos: Position, +} + +/// Encodes a select expression in the UCG AST. +#[derive(PartialEq, Debug, Clone)] +pub struct SelectDef { + pub val: Box, + pub default: Box, + pub tuple: FieldList, + pub pos: Position, +} + +// TODO(jwall): This should have a way of rendering with position information. + +/// Adds position information to any type `T`. +#[derive(Debug, Clone)] +pub struct Positioned { + pub pos: Position, + pub val: T, +} + +impl std::fmt::Display for Positioned { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}", self.val) + } +} + +impl Positioned { + /// Constructs a new Positioned with a value, line, and column information. + pub fn new(v: T, l: usize, c: usize) -> Self { + Self::new_with_pos(v, Position::new(l, c)) + } + + /// Constructs a new Positioned with a value and a Position. + pub fn new_with_pos(v: T, pos: Position) -> Self { + Positioned { pos: pos, val: v } + } +} + +impl PartialEq for Positioned { + fn eq(&self, other: &Self) -> bool { + self.val == other.val + } +} + +impl Eq for Positioned {} + +impl Ord for Positioned { + fn cmp(&self, other: &Self) -> Ordering { + self.val.cmp(&other.val) + } +} + +impl PartialOrd for Positioned { + fn partial_cmp(&self, other: &Self) -> Option { + self.val.partial_cmp(&other.val) + } +} + +impl Hash for Positioned { + fn hash(&self, state: &mut H) { + self.val.hash(state); + } +} + +impl<'a> From<&'a Token> for Positioned { + fn from(t: &'a Token) -> Positioned { + Positioned { + pos: t.pos.clone(), + val: t.fragment.to_string(), + } + } +} + +impl<'a> From<&'a Positioned> for Positioned { + fn from(t: &Positioned) -> Positioned { + Positioned { + pos: t.pos.clone(), + val: t.val.clone(), + } + } +} + +/// Encodes a macro expression in the UCG AST.. +/// +/// A macro is a pure function over a tuple. +/// MacroDefs are not closures. They can not reference +/// any values except what is defined in their arguments. +#[derive(PartialEq, Debug, Clone)] +pub struct MacroDef { + pub argdefs: Vec>, + pub fields: FieldList, + pub pos: Position, +} + +impl MacroDef { + fn symbol_is_in_args(&self, sym: &String) -> bool { + for arg in self.argdefs.iter() { + if &arg.val == sym { + return true; + } + } + return false; + } + + fn validate_value_symbols<'a>( + &self, + stack: &mut Vec<&'a Expression>, + val: &'a Value, + ) -> HashSet { + let mut bad_symbols = HashSet::new(); + if let &Value::Symbol(ref name) = val { + if !self.symbol_is_in_args(&name.val) { + bad_symbols.insert(name.val.clone()); + } + } else if let &Value::Selector(ref sel_node) = val { + stack.push(&sel_node.sel.head); + } else if let &Value::Tuple(ref tuple_node) = val { + let fields = &tuple_node.val; + for &(_, ref expr) in fields.iter() { + stack.push(expr); + } + } else if let &Value::List(ref def) = val { + for elem in def.elems.iter() { + stack.push(elem); + } + } + return bad_symbols; + } + + /// Performs typechecking of a ucg macro's arguments to ensure + /// that they are valid for the expressions in the macro. + pub fn validate_symbols(&self) -> Result<(), HashSet> { + let mut bad_symbols = HashSet::new(); + for &(_, ref expr) in self.fields.iter() { + let mut stack = Vec::new(); + stack.push(expr); + while stack.len() > 0 { + match stack.pop().unwrap() { + &Expression::Binary(ref bexpr) => { + 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); + } + &Expression::Format(ref def) => { + let exprs = &def.args; + for arg_expr in exprs.iter() { + stack.push(arg_expr); + } + } + &Expression::Select(ref def) => { + stack.push(def.default.borrow()); + stack.push(def.val.borrow()); + for &(_, ref expr) in def.tuple.iter() { + stack.push(expr); + } + } + &Expression::Copy(ref def) => { + let fields = &def.fields; + for &(_, ref expr) in fields.iter() { + stack.push(expr); + } + } + &Expression::Call(ref def) => for expr in def.arglist.iter() { + stack.push(expr); + }, + &Expression::Simple(ref val) => { + let mut syms_set = self.validate_value_symbols(&mut stack, val); + bad_symbols.extend(syms_set.drain()); + } + &Expression::Macro(_) => { + // noop + continue; + } + &Expression::ListOp(_) => { + // noop + continue; + } + } + } + } + if bad_symbols.len() > 0 { + return Err(bad_symbols); + } + return Ok(()); + } +} + +/// Specifies the types of binary operations supported in +/// UCG expression. +#[derive(Debug, PartialEq, Clone)] +pub enum BinaryExprType { + Add, + Sub, + Mul, + Div, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum CompareType { + Equal, + GT, + LT, + NotEqual, + GTEqual, + 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: Box, + pub right: Box, + pub pos: Position, +} + +/// Encodes a tuple Copy expression in the UCG AST. +#[derive(Debug, PartialEq, Clone)] +pub struct CopyDef { + pub selector: SelectorDef, + pub fields: FieldList, + pub pos: Position, +} + +/// Encodes a format expression in the UCG AST. +#[derive(Debug, PartialEq, Clone)] +pub struct FormatDef { + pub template: String, + pub args: Vec, + pub pos: Position, +} + +/// Encodes a list expression in the UCG AST. +#[derive(Debug, PartialEq, Clone)] +pub struct ListDef { + pub elems: Vec, + pub pos: Position, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ListOpType { + Map, + Filter, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct ListOpDef { + pub typ: ListOpType, + pub mac: SelectorDef, + pub field: String, + pub target: ListDef, + pub pos: Position, +} + +/// Encodes a ucg expression. Expressions compute a value from. +#[derive(Debug, PartialEq, Clone)] +pub enum Expression { + // Base Expression + Simple(Value), + + // Binary expressions + Binary(BinaryOpDef), + Compare(ComparisonDef), + + // Complex Expressions + Copy(CopyDef), + Grouped(Box), + Format(FormatDef), + Call(CallDef), + Macro(MacroDef), + Select(SelectDef), + ListOp(ListOpDef), +} + +impl Expression { + /// Returns the position of the Expression. + pub fn pos(&self) -> &Position { + 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, + &Expression::Call(ref def) => &def.pos, + &Expression::Macro(ref def) => &def.pos, + &Expression::Select(ref def) => &def.pos, + &Expression::ListOp(ref def) => &def.pos, + } + } +} + +/// Encodes a let statement in the UCG AST. +#[derive(Debug, PartialEq)] +pub struct LetDef { + pub name: Token, + pub value: Expression, +} + +/// Encodes an import statement in the UCG AST. +#[derive(Debug, PartialEq)] +pub struct ImportDef { + pub path: Token, + pub name: Token, +} + +/// Encodes a parsed statement in the UCG AST. +#[derive(Debug, PartialEq)] +pub enum Statement { + // simple expression + Expression(Expression), + + // Named bindings + Let(LetDef), + + // Import a file. + Import(ImportDef), +} + +#[cfg(test)] +pub mod test; diff --git a/src/ast/test.rs b/src/ast/test.rs new file mode 100644 index 0000000..e5028c2 --- /dev/null +++ b/src/ast/test.rs @@ -0,0 +1,99 @@ +use super::*; + +#[test] +pub fn test_macro_validation_happy_path() { + let def = MacroDef { + argdefs: vec![value_node!("foo".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("f1", 1, 1), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + ), + ], + pos: Position::new(1, 0), + }; + assert!(def.validate_symbols().unwrap() == ()); +} + +#[test] +pub fn test_macro_validation_fail() { + let def = MacroDef { + argdefs: vec![value_node!("foo".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("f1", 1, 1), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + ), + ], + pos: Position::new(1, 0), + }; + let mut expected = HashSet::new(); + expected.insert("bar".to_string()); + assert_eq!(def.validate_symbols().err().unwrap(), expected); +} + +#[test] +pub fn test_macro_validation_selector_happy_path() { + let def = MacroDef { + argdefs: vec![value_node!("foo".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("f1", 1, 1), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + ), + ], + pos: Position::new(1, 0), + }; + assert!(def.validate_symbols().unwrap() == ()); +} + +#[test] +pub fn test_macro_validation_selector_fail() { + let def = MacroDef { + argdefs: vec![value_node!("foo".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("f1", 1, 1), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + ), + ], + pos: Position::new(1, 0), + }; + let mut expected = HashSet::new(); + expected.insert("bar".to_string()); + assert_eq!(def.validate_symbols(), Err(expected)); +} diff --git a/src/ast/tree.rs b/src/ast/tree.rs deleted file mode 100644 index 4cecb05..0000000 --- a/src/ast/tree.rs +++ /dev/null @@ -1,839 +0,0 @@ -// 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. -use std; -use std::borrow::Borrow; -use std::cmp::Eq; -use std::cmp::Ordering; -use std::cmp::PartialEq; -use std::cmp::PartialOrd; -use std::collections::HashSet; -use std::convert::Into; -use std::hash::Hash; -use std::hash::Hasher; - -macro_rules! enum_type_equality { - ( $slf:ident, $r:expr, $( $l:pat ),* ) => { - match $slf { - $( - $l => { - if let $l = $r { - true - } else { - false - } - } - )* - } - } -} - -/// Represents a line and a column position in UCG code. -/// -/// It is used for generating error messages mostly. Most all -/// parts of the UCG AST have a positioned associated with them. -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] -pub struct Position { - pub line: usize, - pub column: usize, -} - -impl Position { - /// Construct a new Position. - pub fn new(line: usize, column: usize) -> Self { - Position { - line: line, - column: column, - } - } -} - -/// Defines the types of tokens in UCG syntax. -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] -pub enum TokenType { - EMPTY, - BOOLEAN, - END, - WS, - COMMENT, - QUOTED, - DIGIT, - BAREWORD, - PUNCT, -} - -/// Defines a Token representing a building block of UCG syntax. -/// -/// Token's are passed to the parser stage to be parsed into an AST. -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] -pub struct Token { - pub typ: TokenType, - pub fragment: String, - pub pos: Position, -} - -impl Token { - /// Constructs a new Token with a type and line and column information. - pub fn new>(f: S, typ: TokenType, line: usize, col: usize) -> Self { - Self::new_with_pos(f, typ, Position::new(line, col)) - } - - // Constructs a new Token with a type and a Position. - pub fn new_with_pos>(f: S, typ: TokenType, pos: Position) -> Self { - Token { - typ: typ, - fragment: f.into(), - pos: pos, - } - } -} - -impl Borrow for Token { - fn borrow(&self) -> &str { - &self.fragment - } -} - -/// Helper macro for making a Positioned Value. -macro_rules! value_node { - ($v:expr, $p:expr) => { - Positioned::new_with_pos($v, $p) - }; - ($v:expr, $l:expr, $c:expr) => { - Positioned::new($v, $l, $c) - }; -} - -/// Helper macro for making a Token. -#[allow(unused_macros)] -macro_rules! make_tok { - (EOF => $l:expr, $c:expr) => { - Token::new("", TokenType::END, $l, $c) - }; - - (WS => $l:expr, $c:expr) => { - Token::new("", TokenType::WS, $l, $c) - }; - - (CMT => $e:expr, $l:expr, $c:expr) => { - Token::new($e, TokenType::COMMENT, $l, $c) - }; - - (QUOT => $e:expr, $l:expr, $c:expr) => { - Token::new($e, TokenType::QUOTED, $l, $c) - }; - - (PUNCT => $e:expr, $l:expr, $c:expr) => { - Token::new($e, TokenType::PUNCT, $l, $c) - }; - - (DIGIT => $e:expr, $l:expr, $c:expr) => { - Token::new($e, TokenType::DIGIT, $l, $c) - }; - - ($e:expr, $l:expr, $c:expr) => { - Token::new($e, TokenType::BAREWORD, $l, $c) - }; -} - -/// Helper macro for making expressions. -#[allow(unused_macros)] -macro_rules! make_expr { - ($e:expr) => { - make_expr!($e, 1, 1) - }; - - ($e:expr, $l:expr, $c:expr) => { - Expression::Simple(Value::Symbol(Positioned::new($e.to_string(), $l, $c))) - }; - - ($e:expr => int, $l:expr, $c:expr) => { - Expression::Simple(Value::Int(Positioned::new($e, $l, $c))) - }; -} - -/// Helper macro for making selectors. -/// -/// ``` -/// make_selector!(Token::new("tpl", 1, 1), Token::new("fld", 1, 4)); -/// -/// make_selector!(Token::new("tpl", 1, 1), vec![Token::new("fld", 1, 4)], => 1, 1); -/// -/// make_selector!(foo", ["bar"]); -/// -/// make_selector!(foo", ["bar"] => 1, 0); -/// ``` -#[allow(unused_macros)] -macro_rules! make_selector { - ( $h:expr ) => { - make_selector!($h, 1, 0) - }; - - ( $h:expr, $l:expr, $c:expr ) => { - SelectorDef::new( - SelectorList{head: Box::new($h), tail: None}, - $l, $c) - }; - - ( $h: expr, $list:expr, $l:expr, $c:expr) => { - SelectorDef::new( - SelectorList{head: Box::new($h), tail: Some($list)}, - $l, $c) - }; - - // Tokens - ( $h:expr => [ $( $item:expr ),* ] ) => { - { - make_selector!($h => [ $( $item, )* ] => 1, 1) - } - }; - - ( $h:expr => [ $( $item:expr ),* ] => $l:expr, $c:expr ) => { - { - let mut list: Vec = Vec::new(); - - $( - list.push($item); - )* - - make_selector!($h, list, $l, $c) - } - }; - - // Strings not tokens - ( $h:expr => $( $item:expr ),* ) => { - { - - let mut col = 1; - let mut list: Vec = Vec::new(); - - $( - list.push(make_tok!($item, 1, col)); - col += $item.len() + 1; - )* - - // Shut up the lint about unused code; - assert!(col != 0); - - make_selector!($h, list, 1, 1) - } - - }; - - ( $h:expr => $( $item:expr ),* => $l:expr, $c:expr ) => { - { - let mut col = $c; - let mut list: Vec = Vec::new(); - - $( - list.push(make_tok!($item, $l, col)); - col += $item.len() + 1; - )* - - // Shut up the linter about unused code; - assert!(col != 0); - - make_selector!($h, list, $l, $c) - } - }; -} - -/// An Expression with a series of symbols specifying the key -/// with which to descend into the result of the expression. -/// -/// The expression must evaluate to either a tuple or an array. The token must -/// evaluate to either a bareword Symbol or an Int. -/// -/// ```ucg -/// let foo = { bar = "a thing" }; -/// let thing = foo.bar; -/// -/// let arr = ["one", "two"]; -/// let first = arr.0; -/// -/// let berry = {best = "strawberry", unique = "acai"}.best; -/// let third = ["uno", "dos", "tres"].1; -/// ''' -#[derive(Debug, PartialEq, Clone)] -pub struct SelectorList { - pub head: Box, - pub tail: Option>, -} - -impl SelectorList { - /// Returns a stringified version of a SelectorList. - pub fn to_string(&self) -> String { - "TODO".to_string() - } -} - -/// An ordered list of Name = Value pairs. -/// -/// This is usually used as the body of a tuple in the UCG AST. -pub type FieldList = Vec<(Token, Expression)>; // Token is expected to be a symbol - -/// Encodes a selector expression in the UCG AST. -#[derive(Debug, PartialEq, Clone)] -pub struct SelectorDef { - pub pos: Position, - pub sel: SelectorList, -} - -impl SelectorDef { - /// Constructs a new SelectorDef. - pub fn new(sel: SelectorList, line: usize, col: usize) -> Self { - SelectorDef { - pos: Position::new(line, col), - sel: sel, - } - } -} - -/// Represents a Value in the UCG parsed AST. -#[derive(Debug, PartialEq, Clone)] -pub enum Value { - // Constant Values - Empty(Position), - Boolean(Positioned), - Int(Positioned), - Float(Positioned), - String(Positioned), - Symbol(Positioned), - // Complex Values - Tuple(Positioned), - List(ListDef), - Selector(SelectorDef), -} - -impl Value { - /// Returns the type name of the Value it is called on as a string. - pub fn type_name(&self) -> String { - match self { - &Value::Empty(_) => "EmptyValue".to_string(), - &Value::Boolean(_) => "Boolean".to_string(), - &Value::Int(_) => "Integer".to_string(), - &Value::Float(_) => "Float".to_string(), - &Value::String(_) => "String".to_string(), - &Value::Symbol(_) => "Symbol".to_string(), - &Value::Tuple(_) => "Tuple".to_string(), - &Value::List(_) => "List".to_string(), - &Value::Selector(_) => "Selector".to_string(), - } - } - - fn fields_to_string(v: &FieldList) -> String { - let mut buf = String::new(); - buf.push_str("{\n"); - for ref t in v.iter() { - buf.push_str("\t"); - buf.push_str(&t.0.fragment); - buf.push_str("\n"); - } - buf.push_str("}"); - return buf; - } - - fn elems_to_string(v: &Vec) -> String { - return format!("{}", v.len()); - } - - /// Returns a stringified version of the Value. - pub fn to_string(&self) -> String { - match self { - &Value::Empty(_) => "EmptyValue".to_string(), - &Value::Boolean(ref b) => format!("{}", b.val), - &Value::Int(ref i) => format!("{}", i.val), - &Value::Float(ref f) => format!("{}", f.val), - &Value::String(ref s) => format!("{}", s.val), - &Value::Symbol(ref s) => format!("{}", s.val), - &Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(&fs.val)), - &Value::List(ref def) => format!("[{}]", Self::elems_to_string(&def.elems)), - &Value::Selector(ref v) => v.sel.to_string(), - } - } - - /// Returns the position for a Value. - pub fn pos(&self) -> &Position { - match self { - &Value::Empty(ref pos) => pos, - &Value::Boolean(ref b) => &b.pos, - &Value::Int(ref i) => &i.pos, - &Value::Float(ref f) => &f.pos, - &Value::String(ref s) => &s.pos, - &Value::Symbol(ref s) => &s.pos, - &Value::Tuple(ref fs) => &fs.pos, - &Value::List(ref def) => &def.pos, - &Value::Selector(ref v) => &v.pos, - } - } - - /// Returns true if called on a Value that is the same type as itself. - pub fn type_equal(&self, target: &Self) -> bool { - enum_type_equality!( - self, - target, - &Value::Empty(_), - &Value::Boolean(_), - &Value::Int(_), - &Value::Float(_), - &Value::String(_), - &Value::Symbol(_), - &Value::Tuple(_), - &Value::List(_), - &Value::Selector(_) - ) - } -} - -/// Represents an expansion of a Macro that is expected to already have been -/// defined. -#[derive(PartialEq, Debug, Clone)] -pub struct CallDef { - pub macroref: SelectorDef, - pub arglist: Vec, - pub pos: Position, -} - -/// Encodes a select expression in the UCG AST. -#[derive(PartialEq, Debug, Clone)] -pub struct SelectDef { - pub val: Box, - pub default: Box, - pub tuple: FieldList, - pub pos: Position, -} - -// TODO(jwall): This should have a way of rendering with position information. - -/// Adds position information to any type `T`. -#[derive(Debug, Clone)] -pub struct Positioned { - pub pos: Position, - pub val: T, -} - -impl std::fmt::Display for Positioned { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "{}", self.val) - } -} - -impl Positioned { - /// Constructs a new Positioned with a value, line, and column information. - pub fn new(v: T, l: usize, c: usize) -> Self { - Self::new_with_pos(v, Position::new(l, c)) - } - - /// Constructs a new Positioned with a value and a Position. - pub fn new_with_pos(v: T, pos: Position) -> Self { - Positioned { pos: pos, val: v } - } -} - -impl PartialEq for Positioned { - fn eq(&self, other: &Self) -> bool { - self.val == other.val - } -} - -impl Eq for Positioned {} - -impl Ord for Positioned { - fn cmp(&self, other: &Self) -> Ordering { - self.val.cmp(&other.val) - } -} - -impl PartialOrd for Positioned { - fn partial_cmp(&self, other: &Self) -> Option { - self.val.partial_cmp(&other.val) - } -} - -impl Hash for Positioned { - fn hash(&self, state: &mut H) { - self.val.hash(state); - } -} - -impl<'a> From<&'a Token> for Positioned { - fn from(t: &'a Token) -> Positioned { - Positioned { - pos: t.pos.clone(), - val: t.fragment.to_string(), - } - } -} - -impl<'a> From<&'a Positioned> for Positioned { - fn from(t: &Positioned) -> Positioned { - Positioned { - pos: t.pos.clone(), - val: t.val.clone(), - } - } -} - -/// Encodes a macro expression in the UCG AST.. -/// -/// A macro is a pure function over a tuple. -/// MacroDefs are not closures. They can not reference -/// any values except what is defined in their arguments. -#[derive(PartialEq, Debug, Clone)] -pub struct MacroDef { - pub argdefs: Vec>, - pub fields: FieldList, - pub pos: Position, -} - -impl MacroDef { - fn symbol_is_in_args(&self, sym: &String) -> bool { - for arg in self.argdefs.iter() { - if &arg.val == sym { - return true; - } - } - return false; - } - - fn validate_value_symbols<'a>( - &self, - stack: &mut Vec<&'a Expression>, - val: &'a Value, - ) -> HashSet { - let mut bad_symbols = HashSet::new(); - if let &Value::Symbol(ref name) = val { - if !self.symbol_is_in_args(&name.val) { - bad_symbols.insert(name.val.clone()); - } - } else if let &Value::Selector(ref sel_node) = val { - stack.push(&sel_node.sel.head); - } else if let &Value::Tuple(ref tuple_node) = val { - let fields = &tuple_node.val; - for &(_, ref expr) in fields.iter() { - stack.push(expr); - } - } else if let &Value::List(ref def) = val { - for elem in def.elems.iter() { - stack.push(elem); - } - } - return bad_symbols; - } - - /// Performs typechecking of a ucg macro's arguments to ensure - /// that they are valid for the expressions in the macro. - pub fn validate_symbols(&self) -> Result<(), HashSet> { - let mut bad_symbols = HashSet::new(); - for &(_, ref expr) in self.fields.iter() { - let mut stack = Vec::new(); - stack.push(expr); - while stack.len() > 0 { - match stack.pop().unwrap() { - &Expression::Binary(ref bexpr) => { - 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); - } - &Expression::Format(ref def) => { - let exprs = &def.args; - for arg_expr in exprs.iter() { - stack.push(arg_expr); - } - } - &Expression::Select(ref def) => { - stack.push(def.default.borrow()); - stack.push(def.val.borrow()); - for &(_, ref expr) in def.tuple.iter() { - stack.push(expr); - } - } - &Expression::Copy(ref def) => { - let fields = &def.fields; - for &(_, ref expr) in fields.iter() { - stack.push(expr); - } - } - &Expression::Call(ref def) => for expr in def.arglist.iter() { - stack.push(expr); - }, - &Expression::Simple(ref val) => { - let mut syms_set = self.validate_value_symbols(&mut stack, val); - bad_symbols.extend(syms_set.drain()); - } - &Expression::Macro(_) => { - // noop - continue; - } - &Expression::ListOp(_) => { - // noop - continue; - } - } - } - } - if bad_symbols.len() > 0 { - return Err(bad_symbols); - } - return Ok(()); - } -} - -/// Specifies the types of binary operations supported in -/// UCG expression. -#[derive(Debug, PartialEq, Clone)] -pub enum BinaryExprType { - Add, - Sub, - Mul, - Div, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum CompareType { - Equal, - GT, - LT, - NotEqual, - GTEqual, - 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: Box, - pub right: Box, - pub pos: Position, -} - -/// Encodes a tuple Copy expression in the UCG AST. -#[derive(Debug, PartialEq, Clone)] -pub struct CopyDef { - pub selector: SelectorDef, - pub fields: FieldList, - pub pos: Position, -} - -/// Encodes a format expression in the UCG AST. -#[derive(Debug, PartialEq, Clone)] -pub struct FormatDef { - pub template: String, - pub args: Vec, - pub pos: Position, -} - -/// Encodes a list expression in the UCG AST. -#[derive(Debug, PartialEq, Clone)] -pub struct ListDef { - pub elems: Vec, - pub pos: Position, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum ListOpType { - Map, - Filter, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct ListOpDef { - pub typ: ListOpType, - pub mac: SelectorDef, - pub field: String, - pub target: ListDef, - pub pos: Position, -} - -/// Encodes a ucg expression. Expressions compute a value from. -#[derive(Debug, PartialEq, Clone)] -pub enum Expression { - // Base Expression - Simple(Value), - - // Binary expressions - Binary(BinaryOpDef), - Compare(ComparisonDef), - - // Complex Expressions - Copy(CopyDef), - Grouped(Box), - Format(FormatDef), - Call(CallDef), - Macro(MacroDef), - Select(SelectDef), - ListOp(ListOpDef), -} - -impl Expression { - /// Returns the position of the Expression. - pub fn pos(&self) -> &Position { - 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, - &Expression::Call(ref def) => &def.pos, - &Expression::Macro(ref def) => &def.pos, - &Expression::Select(ref def) => &def.pos, - &Expression::ListOp(ref def) => &def.pos, - } - } -} - -/// Encodes a let statement in the UCG AST. -#[derive(Debug, PartialEq)] -pub struct LetDef { - pub name: Token, - pub value: Expression, -} - -/// Encodes an import statement in the UCG AST. -#[derive(Debug, PartialEq)] -pub struct ImportDef { - pub path: Token, - pub name: Token, -} - -/// Encodes a parsed statement in the UCG AST. -#[derive(Debug, PartialEq)] -pub enum Statement { - // simple expression - Expression(Expression), - - // Named bindings - Let(LetDef), - - // Import a file. - Import(ImportDef), -} - -#[cfg(test)] -mod ast_test { - use super::*; - - #[test] - pub fn test_macro_validation_happy_path() { - let def = MacroDef { - argdefs: vec![value_node!("foo".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("f1", 1, 1), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - ), - ], - pos: Position::new(1, 0), - }; - assert!(def.validate_symbols().unwrap() == ()); - } - - #[test] - pub fn test_macro_validation_fail() { - let def = MacroDef { - argdefs: vec![value_node!("foo".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("f1", 1, 1), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - ), - ], - pos: Position::new(1, 0), - }; - let mut expected = HashSet::new(); - expected.insert("bar".to_string()); - assert_eq!(def.validate_symbols().err().unwrap(), expected); - } - - #[test] - pub fn test_macro_validation_selector_happy_path() { - let def = MacroDef { - argdefs: vec![value_node!("foo".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("f1", 1, 1), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - ), - ], - pos: Position::new(1, 0), - }; - assert!(def.validate_symbols().unwrap() == ()); - } - - #[test] - pub fn test_macro_validation_selector_fail() { - let def = MacroDef { - argdefs: vec![value_node!("foo".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("f1", 1, 1), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - ), - ], - pos: Position::new(1, 0), - }; - let mut expected = HashSet::new(); - expected.insert("bar".to_string()); - assert_eq!(def.validate_symbols(), Err(expected)); - } -} diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs new file mode 100644 index 0000000..82434c5 --- /dev/null +++ b/src/build/compile_test.rs @@ -0,0 +1,86 @@ +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 == 4;"); + assert_build("let result = (2 * 2) + 1;", "result == 5;"); +} diff --git a/src/build.rs b/src/build/mod.rs similarity index 55% rename from src/build.rs rename to src/build/mod.rs index 8cd5daa..e93aa18 100644 --- a/src/build.rs +++ b/src/build/mod.rs @@ -25,7 +25,7 @@ use std::io::Read; use std::ops::Deref; use std::rc::Rc; -use ast::tree::*; +use ast::*; use error; use format; use parse::parse; @@ -1110,899 +1110,7 @@ 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 == 4;"); - assert_build("let result = (2 * 2) + 1;", "result == 5;"); - } -} +mod compile_test; #[cfg(test)] -mod test { - use super::{Builder, CallDef, MacroDef, SelectDef, Val}; - use ast::tree::*; - use std::rc::Rc; - - fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) { - for tpl in cases.drain(0..) { - assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1)); - } - } - - #[test] - fn test_eval_div_expr() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - 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), - }), - Val::Int(1), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected Float")] - fn test_eval_div_expr_fail() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - fn test_eval_mul_expr() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - 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), - }), - Val::Int(4), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - 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), - }), - Val::Float(4.0), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected Float")] - fn test_eval_mul_expr_fail() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - fn test_eval_subtract_expr() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Sub, - 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), - }), - Val::Int(1), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Sub, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected Float")] - fn test_eval_subtract_expr_fail() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Sub, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - fn test_eval_add_expr() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - Val::Int(2), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - Val::Float(2.0), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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, - 1 - )))), - pos: Position::new(1, 0), - }), - Val::String("foobar".to_string()), - ), - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::String(value_node!( - "foo".to_string(), - 1, - 1 - ))), - ], - pos: Position::new(1, 1), - }))), - right: Box::new(Expression::Simple(Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::String(value_node!( - "bar".to_string(), - 1, - 1 - ))), - ], - pos: Position::new(1, 1), - }))), - pos: Position::new(1, 0), - }), - Val::List(vec![ - Rc::new(Val::String("foo".to_string())), - Rc::new(Val::String("bar".to_string())), - ]), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected Float")] - fn test_eval_add_expr_fail() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - 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), - }), - Val::Float(1.0), - ), - ], - b, - ); - } - - #[test] - fn test_eval_simple_expr() { - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Int(value_node!(1, 1, 1))), - Val::Int(1), - ), - ( - Expression::Simple(Value::Float(value_node!(2.0, 1, 1))), - Val::Float(2.0), - ), - ( - Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 1))), - Val::String("foo".to_string()), - ), - ( - Expression::Simple(Value::Tuple(value_node!( - vec![ - ( - make_tok!("bar", 1, 1), - Expression::Simple(Value::Int(value_node!(1, 1, 1))), - ), - ], - 1, - 1 - ))), - Val::Tuple(vec![ - (value_node!("bar".to_string(), 1, 1), Rc::new(Val::Int(1))), - ]), - ), - ], - Builder::new(), - ); - } - - #[test] - fn test_eval_simple_lookup_expr() { - let mut b = Builder::new(); - b.out - .entry(value_node!("var1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Int(1))); - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Symbol(value_node!("var1".to_string(), 1, 1))), - Val::Int(1), - ), - ], - b, - ); - } - - #[test] - fn test_eval_simple_lookup_error() { - let mut b = Builder::new(); - b.out - .entry(value_node!("var1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Int(1))); - let expr = Expression::Simple(Value::Symbol(value_node!("var".to_string(), 1, 1))); - assert!(b.eval_expr(&expr).is_err()); - } - - #[test] - fn test_eval_selector_expr() { - let mut b = Builder::new(); - b.out - .entry(value_node!("var1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![ - ( - value_node!("lvl1".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![ - (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), - ])), - ), - ]))); - b.out - .entry(value_node!("var2".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Int(2))); - b.out - .entry(value_node!("var3".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![ - (value_node!("lvl1".to_string(), 1, 0), Rc::new(Val::Int(4))), - ]))); - - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Selector(make_selector!(make_expr!("var1")))), - Val::Tuple(vec![ - ( - value_node!("lvl1".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![ - (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), - ])), - ), - ]), - ), - ( - Expression::Simple(Value::Selector( - make_selector!(make_expr!("var1") => "lvl1"), - )), - Val::Tuple(vec![ - (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), - ]), - ), - ( - Expression::Simple(Value::Selector( - make_selector!(make_expr!("var1") => "lvl1", "lvl2"), - )), - Val::Int(3), - ), - ( - Expression::Simple(Value::Selector(make_selector!(make_expr!("var2")))), - Val::Int(2), - ), - ( - Expression::Simple(Value::Selector( - make_selector!(make_expr!("var3") => "lvl1"), - )), - Val::Int(4), - ), - ], - b, - ); - } - - #[test] - fn test_eval_selector_list_expr() { - let mut b = Builder::new(); - b.out - .entry(value_node!("var1".to_string(), 1, 1)) - .or_insert(Rc::new(Val::List(vec![ - Rc::new(Val::String("val1".to_string())), - Rc::new(Val::Tuple(vec![ - (value_node!("var2".to_string(), 1, 1), Rc::new(Val::Int(1))), - ])), - ]))); - // TODO(jwall): Assert that we can index into lists using dot syntax. - - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Selector( - make_selector!(make_expr!("var1") => "0" => 1, 1), - )), - Val::String("val1".to_string()), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Unable to find tpl1")] - fn test_expr_copy_no_such_tuple() { - let b = Builder::new(); - test_expr_to_val( - vec![ - ( - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("tpl1")), - fields: Vec::new(), - pos: Position::new(1, 0), - }), - Val::Tuple(Vec::new()), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected Tuple got Int(1)")] - fn test_expr_copy_not_a_tuple() { - let mut b = Builder::new(); - b.out - .entry(value_node!("tpl1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Int(1))); - test_expr_to_val( - vec![ - ( - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("tpl1")), - fields: Vec::new(), - pos: Position::new(1, 0), - }), - Val::Tuple(Vec::new()), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected type Integer for field fld1 but got String")] - fn test_expr_copy_field_type_error() { - let mut b = Builder::new(); - b.out - .entry(value_node!("tpl1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![ - (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), - ]))); - test_expr_to_val( - vec![ - ( - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("tpl1")), - fields: vec![ - ( - make_tok!("fld1", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ], - pos: Position::new(1, 0), - }), - Val::Tuple(vec![ - ( - value_node!("fld1".to_string(), 1, 1), - Rc::new(Val::String("2".to_string())), - ), - ]), - ), - ], - b, - ); - } - - #[test] - fn test_expr_copy() { - let mut b = Builder::new(); - b.out - .entry(value_node!("tpl1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![ - (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), - ]))); - test_expr_to_val( - vec![ - ( - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("tpl1")), - fields: vec![ - ( - make_tok!("fld2", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ], - pos: Position::new(1, 0), - }), - // Add a new field to the copy - Val::Tuple( - // NOTE(jwall): The order of these is important in order to ensure - // that the compare assertion is correct. The ordering has no - // semantics though so at some point we should probably be less restrictive. - vec![ - (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), - ( - value_node!("fld2".to_string(), 1, 1), - Rc::new(Val::String("2".to_string())), - ), - ], - ), - ), - // Overwrite a field in the copy - ( - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("tpl1")), - fields: vec![ - ( - make_tok!("fld1", 1, 1), - Expression::Simple(Value::Int(value_node!(3, 1, 1))), - ), - ( - make_tok!("fld2", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ], - pos: Position::new(1, 0), - }), - Val::Tuple(vec![ - (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(3))), - ( - value_node!("fld2".to_string(), 1, 0), - Rc::new(Val::String("2".to_string())), - ), - ]), - ), - // The source tuple is still unmodified. - ( - Expression::Simple(Value::Selector(make_selector!(make_expr!["tpl1"]))), - Val::Tuple(vec![ - (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), - ]), - ), - ], - b, - ); - } - - #[test] - fn test_macro_call() { - let mut b = Builder::new(); - b.out - .entry(value_node!("tstmac".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Macro(MacroDef { - argdefs: vec![value_node!("arg1".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Symbol(value_node!("arg1".to_string(), 1, 1))), - ), - ], - pos: Position::new(1, 0), - }))); - test_expr_to_val( - vec![ - ( - Expression::Call(CallDef { - macroref: make_selector!(make_expr!("tstmac")), - arglist: vec![ - Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), - ], - pos: Position::new(1, 0), - }), - Val::Tuple(vec![ - ( - value_node!("foo".to_string(), 1, 1), - Rc::new(Val::String("bar".to_string())), - ), - ]), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Unable to find arg1")] - fn test_macro_hermetic() { - let mut b = Builder::new(); - b.out - .entry(value_node!("arg1".to_string(), 1, 0)) - .or_insert(Rc::new(Val::String("bar".to_string()))); - b.out - .entry(value_node!("tstmac".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Macro(MacroDef { - argdefs: vec![value_node!("arg2".to_string(), 1, 0)], - fields: vec![ - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Symbol(value_node!("arg1".to_string(), 1, 1))), - ), - ], - pos: Position::new(1, 0), - }))); - test_expr_to_val( - vec![ - ( - Expression::Call(CallDef { - macroref: make_selector!(make_expr!("tstmac")), - arglist: vec![ - Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), - ], - pos: Position::new(1, 1), - }), - Val::Tuple(vec![ - ( - value_node!("foo".to_string(), 1, 0), - Rc::new(Val::String("bar".to_string())), - ), - ]), - ), - ], - b, - ); - } - - #[test] - fn test_select_expr() { - let mut b = Builder::new(); - b.out - .entry(value_node!("foo".to_string(), 1, 0)) - .or_insert(Rc::new(Val::String("bar".to_string()))); - b.out - .entry(value_node!("baz".to_string(), 1, 0)) - .or_insert(Rc::new(Val::String("boo".to_string()))); - test_expr_to_val( - vec![ - ( - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Symbol(value_node!( - "foo".to_string(), - 1, - 1 - )))), - default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - tuple: vec![ - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ( - make_tok!("bar", 1, 1), - Expression::Simple(Value::Int(value_node!(2, 1, 1))), - ), - ], - pos: Position::new(1, 0), - }), - Val::Int(2), - ), - ( - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Symbol(value_node!( - "baz".to_string(), - 1, - 1 - )))), - default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - tuple: vec![ - ( - make_tok!("bar", 1, 1), - Expression::Simple(Value::Int(value_node!(2, 1, 1))), - ), - ( - make_tok!("quux", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ], - pos: Position::new(1, 0), - }), - // If the field doesn't exist then we get the default. - Val::Int(1), - ), - ], - b, - ); - } - - #[test] - #[should_panic(expected = "Expected String but got Integer in Select expression")] - fn test_select_expr_not_a_string() { - let mut b = Builder::new(); - b.out - .entry(value_node!("foo".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Int(4))); - test_expr_to_val( - vec![ - ( - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Symbol(value_node!( - "foo".to_string(), - 1, - 1 - )))), - default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - tuple: vec![ - ( - make_tok!("bar", 1, 1), - Expression::Simple(Value::Int(value_node!(2, 1, 1))), - ), - ( - make_tok!("quux", 1, 1), - Expression::Simple(Value::String(value_node!( - "2".to_string(), - 1, - 1 - ))), - ), - ], - pos: Position::new(1, 0), - }), - Val::Int(2), - ), - ], - b, - ); - } - - #[test] - fn test_let_statement() { - let mut b = Builder::new(); - let stmt = Statement::Let(LetDef { - name: make_tok!("foo", 1, 1), - value: Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), - }); - b.build_stmt(&stmt).unwrap(); - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Symbol(value_node!("foo".to_string(), 1, 1))), - Val::String("bar".to_string()), - ), - ], - b, - ); - } - - #[test] - fn test_build_file_string() { - let mut b = Builder::new(); - b.build_file_string("let foo = 1;".to_string()).unwrap(); - let key = value_node!("foo".to_string(), 1, 0); - assert!(b.out.contains_key(&key)); - } - - #[test] - fn test_asset_symbol_lookups() { - let mut b = Builder::new(); - b.assets - .entry(value_node!("foo".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![ - ( - value_node!("bar".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![ - (value_node!("quux".to_string(), 1, 0), Rc::new(Val::Int(1))), - ])), - ), - ]))); - test_expr_to_val( - vec![ - ( - Expression::Simple(Value::Symbol(value_node!("foo".to_string(), 1, 1))), - Val::Tuple(vec![ - ( - value_node!("bar".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![ - (value_node!("quux".to_string(), 1, 0), Rc::new(Val::Int(1))), - ])), - ), - ]), - ), - ], - b, - ); - } -} +mod test; diff --git a/src/build/test.rs b/src/build/test.rs new file mode 100644 index 0000000..02a04fd --- /dev/null +++ b/src/build/test.rs @@ -0,0 +1,772 @@ +use super::{Builder, CallDef, MacroDef, SelectDef, Val}; +use ast::*; +use std::rc::Rc; + +fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) { + for tpl in cases.drain(0..) { + assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1)); + } +} + +#[test] +fn test_eval_div_expr() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + 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), + }), + Val::Int(1), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected Float")] +fn test_eval_div_expr_fail() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +fn test_eval_mul_expr() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + 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), + }), + Val::Int(4), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + 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), + }), + Val::Float(4.0), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected Float")] +fn test_eval_mul_expr_fail() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +fn test_eval_subtract_expr() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Sub, + 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), + }), + Val::Int(1), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Sub, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected Float")] +fn test_eval_subtract_expr_fail() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Sub, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +fn test_eval_add_expr() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + Val::Int(2), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + Val::Float(2.0), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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, + 1 + )))), + pos: Position::new(1, 0), + }), + Val::String("foobar".to_string()), + ), + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 1))), + ], + pos: Position::new(1, 1), + }))), + right: Box::new(Expression::Simple(Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), + ], + pos: Position::new(1, 1), + }))), + pos: Position::new(1, 0), + }), + Val::List(vec![ + Rc::new(Val::String("foo".to_string())), + Rc::new(Val::String("bar".to_string())), + ]), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected Float")] +fn test_eval_add_expr_fail() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + 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), + }), + Val::Float(1.0), + ), + ], + b, + ); +} + +#[test] +fn test_eval_simple_expr() { + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Int(value_node!(1, 1, 1))), + Val::Int(1), + ), + ( + Expression::Simple(Value::Float(value_node!(2.0, 1, 1))), + Val::Float(2.0), + ), + ( + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 1))), + Val::String("foo".to_string()), + ), + ( + Expression::Simple(Value::Tuple(value_node!( + vec![ + ( + make_tok!("bar", 1, 1), + Expression::Simple(Value::Int(value_node!(1, 1, 1))), + ), + ], + 1, + 1 + ))), + Val::Tuple(vec![ + (value_node!("bar".to_string(), 1, 1), Rc::new(Val::Int(1))), + ]), + ), + ], + Builder::new(), + ); +} + +#[test] +fn test_eval_simple_lookup_expr() { + let mut b = Builder::new(); + b.out + .entry(value_node!("var1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Int(1))); + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Symbol(value_node!("var1".to_string(), 1, 1))), + Val::Int(1), + ), + ], + b, + ); +} + +#[test] +fn test_eval_simple_lookup_error() { + let mut b = Builder::new(); + b.out + .entry(value_node!("var1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Int(1))); + let expr = Expression::Simple(Value::Symbol(value_node!("var".to_string(), 1, 1))); + assert!(b.eval_expr(&expr).is_err()); +} + +#[test] +fn test_eval_selector_expr() { + let mut b = Builder::new(); + b.out + .entry(value_node!("var1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Tuple(vec![ + ( + value_node!("lvl1".to_string(), 1, 0), + Rc::new(Val::Tuple(vec![ + (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), + ])), + ), + ]))); + b.out + .entry(value_node!("var2".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Int(2))); + b.out + .entry(value_node!("var3".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Tuple(vec![ + (value_node!("lvl1".to_string(), 1, 0), Rc::new(Val::Int(4))), + ]))); + + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Selector(make_selector!(make_expr!("var1")))), + Val::Tuple(vec![ + ( + value_node!("lvl1".to_string(), 1, 0), + Rc::new(Val::Tuple(vec![ + (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), + ])), + ), + ]), + ), + ( + Expression::Simple(Value::Selector( + make_selector!(make_expr!("var1") => "lvl1"), + )), + Val::Tuple(vec![ + (value_node!("lvl2".to_string(), 1, 0), Rc::new(Val::Int(3))), + ]), + ), + ( + Expression::Simple(Value::Selector( + make_selector!(make_expr!("var1") => "lvl1", "lvl2"), + )), + Val::Int(3), + ), + ( + Expression::Simple(Value::Selector(make_selector!(make_expr!("var2")))), + Val::Int(2), + ), + ( + Expression::Simple(Value::Selector( + make_selector!(make_expr!("var3") => "lvl1"), + )), + Val::Int(4), + ), + ], + b, + ); +} + +#[test] +fn test_eval_selector_list_expr() { + let mut b = Builder::new(); + b.out + .entry(value_node!("var1".to_string(), 1, 1)) + .or_insert(Rc::new(Val::List(vec![ + Rc::new(Val::String("val1".to_string())), + Rc::new(Val::Tuple(vec![ + (value_node!("var2".to_string(), 1, 1), Rc::new(Val::Int(1))), + ])), + ]))); + // TODO(jwall): Assert that we can index into lists using dot syntax. + + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Selector( + make_selector!(make_expr!("var1") => "0" => 1, 1), + )), + Val::String("val1".to_string()), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Unable to find tpl1")] +fn test_expr_copy_no_such_tuple() { + let b = Builder::new(); + test_expr_to_val( + vec![ + ( + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("tpl1")), + fields: Vec::new(), + pos: Position::new(1, 0), + }), + Val::Tuple(Vec::new()), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected Tuple got Int(1)")] +fn test_expr_copy_not_a_tuple() { + let mut b = Builder::new(); + b.out + .entry(value_node!("tpl1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Int(1))); + test_expr_to_val( + vec![ + ( + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("tpl1")), + fields: Vec::new(), + pos: Position::new(1, 0), + }), + Val::Tuple(Vec::new()), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected type Integer for field fld1 but got String")] +fn test_expr_copy_field_type_error() { + let mut b = Builder::new(); + b.out + .entry(value_node!("tpl1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Tuple(vec![ + (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), + ]))); + test_expr_to_val( + vec![ + ( + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("tpl1")), + fields: vec![ + ( + make_tok!("fld1", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + Val::Tuple(vec![ + ( + value_node!("fld1".to_string(), 1, 1), + Rc::new(Val::String("2".to_string())), + ), + ]), + ), + ], + b, + ); +} + +#[test] +fn test_expr_copy() { + let mut b = Builder::new(); + b.out + .entry(value_node!("tpl1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Tuple(vec![ + (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), + ]))); + test_expr_to_val( + vec![ + ( + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("tpl1")), + fields: vec![ + ( + make_tok!("fld2", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + // Add a new field to the copy + Val::Tuple( + // NOTE(jwall): The order of these is important in order to ensure + // that the compare assertion is correct. The ordering has no + // semantics though so at some point we should probably be less restrictive. + vec![ + (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), + ( + value_node!("fld2".to_string(), 1, 1), + Rc::new(Val::String("2".to_string())), + ), + ], + ), + ), + // Overwrite a field in the copy + ( + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("tpl1")), + fields: vec![ + ( + make_tok!("fld1", 1, 1), + Expression::Simple(Value::Int(value_node!(3, 1, 1))), + ), + ( + make_tok!("fld2", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + Val::Tuple(vec![ + (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(3))), + ( + value_node!("fld2".to_string(), 1, 0), + Rc::new(Val::String("2".to_string())), + ), + ]), + ), + // The source tuple is still unmodified. + ( + Expression::Simple(Value::Selector(make_selector!(make_expr!["tpl1"]))), + Val::Tuple(vec![ + (value_node!("fld1".to_string(), 1, 0), Rc::new(Val::Int(1))), + ]), + ), + ], + b, + ); +} + +#[test] +fn test_macro_call() { + let mut b = Builder::new(); + b.out + .entry(value_node!("tstmac".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Macro(MacroDef { + argdefs: vec![value_node!("arg1".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Symbol(value_node!("arg1".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }))); + test_expr_to_val( + vec![ + ( + Expression::Call(CallDef { + macroref: make_selector!(make_expr!("tstmac")), + arglist: vec![ + Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), + ], + pos: Position::new(1, 0), + }), + Val::Tuple(vec![ + ( + value_node!("foo".to_string(), 1, 1), + Rc::new(Val::String("bar".to_string())), + ), + ]), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Unable to find arg1")] +fn test_macro_hermetic() { + let mut b = Builder::new(); + b.out + .entry(value_node!("arg1".to_string(), 1, 0)) + .or_insert(Rc::new(Val::String("bar".to_string()))); + b.out + .entry(value_node!("tstmac".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Macro(MacroDef { + argdefs: vec![value_node!("arg2".to_string(), 1, 0)], + fields: vec![ + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Symbol(value_node!("arg1".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }))); + test_expr_to_val( + vec![ + ( + Expression::Call(CallDef { + macroref: make_selector!(make_expr!("tstmac")), + arglist: vec![ + Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), + ], + pos: Position::new(1, 1), + }), + Val::Tuple(vec![ + ( + value_node!("foo".to_string(), 1, 0), + Rc::new(Val::String("bar".to_string())), + ), + ]), + ), + ], + b, + ); +} + +#[test] +fn test_select_expr() { + let mut b = Builder::new(); + b.out + .entry(value_node!("foo".to_string(), 1, 0)) + .or_insert(Rc::new(Val::String("bar".to_string()))); + b.out + .entry(value_node!("baz".to_string(), 1, 0)) + .or_insert(Rc::new(Val::String("boo".to_string()))); + test_expr_to_val( + vec![ + ( + Expression::Select(SelectDef { + val: Box::new(Expression::Simple(Value::Symbol(value_node!( + "foo".to_string(), + 1, + 1 + )))), + default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + tuple: vec![ + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ( + make_tok!("bar", 1, 1), + Expression::Simple(Value::Int(value_node!(2, 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + Val::Int(2), + ), + ( + Expression::Select(SelectDef { + val: Box::new(Expression::Simple(Value::Symbol(value_node!( + "baz".to_string(), + 1, + 1 + )))), + default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + tuple: vec![ + ( + make_tok!("bar", 1, 1), + Expression::Simple(Value::Int(value_node!(2, 1, 1))), + ), + ( + make_tok!("quux", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + // If the field doesn't exist then we get the default. + Val::Int(1), + ), + ], + b, + ); +} + +#[test] +#[should_panic(expected = "Expected String but got Integer in Select expression")] +fn test_select_expr_not_a_string() { + let mut b = Builder::new(); + b.out + .entry(value_node!("foo".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Int(4))); + test_expr_to_val( + vec![ + ( + Expression::Select(SelectDef { + val: Box::new(Expression::Simple(Value::Symbol(value_node!( + "foo".to_string(), + 1, + 1 + )))), + default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + tuple: vec![ + ( + make_tok!("bar", 1, 1), + Expression::Simple(Value::Int(value_node!(2, 1, 1))), + ), + ( + make_tok!("quux", 1, 1), + Expression::Simple(Value::String(value_node!("2".to_string(), 1, 1))), + ), + ], + pos: Position::new(1, 0), + }), + Val::Int(2), + ), + ], + b, + ); +} + +#[test] +fn test_let_statement() { + let mut b = Builder::new(); + let stmt = Statement::Let(LetDef { + name: make_tok!("foo", 1, 1), + value: Expression::Simple(Value::String(value_node!("bar".to_string(), 1, 1))), + }); + b.build_stmt(&stmt).unwrap(); + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Symbol(value_node!("foo".to_string(), 1, 1))), + Val::String("bar".to_string()), + ), + ], + b, + ); +} + +#[test] +fn test_build_file_string() { + let mut b = Builder::new(); + b.build_file_string("let foo = 1;".to_string()).unwrap(); + let key = value_node!("foo".to_string(), 1, 0); + assert!(b.out.contains_key(&key)); +} + +#[test] +fn test_asset_symbol_lookups() { + let mut b = Builder::new(); + b.assets + .entry(value_node!("foo".to_string(), 1, 0)) + .or_insert(Rc::new(Val::Tuple(vec![ + ( + value_node!("bar".to_string(), 1, 0), + Rc::new(Val::Tuple(vec![ + (value_node!("quux".to_string(), 1, 0), Rc::new(Val::Int(1))), + ])), + ), + ]))); + test_expr_to_val( + vec![ + ( + Expression::Simple(Value::Symbol(value_node!("foo".to_string(), 1, 1))), + Val::Tuple(vec![ + ( + value_node!("bar".to_string(), 1, 0), + Rc::new(Val::Tuple(vec![ + (value_node!("quux".to_string(), 1, 0), Rc::new(Val::Int(1))), + ])), + ), + ]), + ), + ], + b, + ); +} diff --git a/src/convert/env.rs b/src/convert/env.rs index 2733e8c..3fc36b0 100644 --- a/src/convert/env.rs +++ b/src/convert/env.rs @@ -17,7 +17,7 @@ use std::io::Result; use std::io::Write; use std::rc::Rc; -use ast::tree::*; +use ast::*; use build::Val; use convert::traits::Converter; diff --git a/src/convert/json.rs b/src/convert/json.rs index 16be2f9..0c89df4 100644 --- a/src/convert/json.rs +++ b/src/convert/json.rs @@ -36,7 +36,7 @@ impl JsonConverter { fn convert_tuple( &self, - items: &Vec<(ast::tree::Positioned, Rc)>, + items: &Vec<(ast::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 b6ab6a5..462f312 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ use std::error; use std::fmt; -use ast::tree::*; +use ast::*; use nom; diff --git a/src/format.rs b/src/format.rs index bff58c7..4f8f627 100644 --- a/src/format.rs +++ b/src/format.rs @@ -16,7 +16,7 @@ use std::clone::Clone; use std::error::Error; -use ast::tree::*; +use ast::*; use error; /// Implements the logic for format strings in UCG format expressions. @@ -77,7 +77,7 @@ impl + Clone> Formatter { #[cfg(test)] mod test { use super::Formatter; - use ast::tree::Position; + use ast::Position; #[test] fn test_format_happy_path() { diff --git a/src/lib.rs b/src/lib.rs index 5e405f3..9b223d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,7 +271,7 @@ //! ##### Operator Precedence //! //! UCG binary operators follow the typical operator precedence for math. `*` and `/` are higher precendence than -//! `+` and `-` which are higher precedence than any of the comparison operators. +//! `+` and `-` which are higher precedence than any of the comparison operators. //! //! #### Copy expressions //! @@ -409,9 +409,9 @@ pub mod parse; mod format; -pub use ast::tree::Expression; -pub use ast::tree::Statement; -pub use ast::tree::Value; +pub use ast::Expression; +pub use ast::Statement; +pub use ast::Value; pub use build::Builder; pub use build::Val; diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index d99562a..0000000 --- a/src/parse.rs +++ /dev/null @@ -1,1967 +0,0 @@ -// 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 Parsing stage of the ucg compiler. -use std::borrow::Borrow; -use std::str::FromStr; - -use nom; -use nom::IResult; -use nom::InputLength; -use nom_locate::LocatedSpan; - -use ast::tree::*; -use error; -use tokenizer::*; - -type NomResult<'a, O> = nom::IResult, O, error::Error>; - -type ParseResult = Result; - -fn symbol_to_value(s: &Token) -> ParseResult { - Ok(Value::Symbol(value_node!( - s.fragment.to_string(), - s.pos.clone() - ))) -} - -// symbol is a bare unquoted field. -named!(symbol, - match_type!(BAREWORD => symbol_to_value) -); - -fn str_to_value(s: &Token) -> ParseResult { - Ok(Value::String(value_node!( - s.fragment.to_string(), - s.pos.clone() - ))) -} - -// quoted_value is a quoted string. -named!(quoted_value, - match_type!(STR => str_to_value) -); - -// Helper function to make the return types work for down below. -fn triple_to_number(v: (Option, Option, Option)) -> ParseResult { - let (pref, mut pref_pos) = match v.0 { - None => ("", Position::new(0, 0)), - Some(ref bs) => (bs.fragment.borrow(), bs.pos.clone()), - }; - - let has_dot = v.1.is_some(); - - if v.0.is_some() && !has_dot && v.2.is_none() { - let i = match FromStr::from_str(pref) { - Ok(i) => i, - Err(_) => { - return Err(error::Error::new( - format!("Not an integer! {}", pref), - error::ErrorType::UnexpectedToken, - pref_pos, - )) - } - }; - return Ok(Value::Int(value_node!(i, pref_pos))); - } - - if v.0.is_none() && has_dot { - pref_pos = v.1.unwrap().pos; - } - - let (maybepos, suf) = match v.2 { - None => (None, "".to_string()), - Some(bs) => (Some(bs.pos), bs.fragment), - }; - - let to_parse = pref.to_string() + "." + &suf; - // TODO(jwall): if there is an error we should report where that error occured. - let f = match FromStr::from_str(&to_parse) { - Ok(f) => f, - Err(_) => { - return Err(error::Error::new( - format!("Not a float! {}", to_parse), - error::ErrorType::UnexpectedToken, - maybepos.unwrap(), - )) - } - }; - return Ok(Value::Float(value_node!(f, pref_pos))); -} - -// trace_macros!(true); - -// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters -// alot. We need to process alternatives in order of decreasing -// specificity. Unfortunately this means we are required to go in a -// decreasing size order which messes with alt!'s completion logic. To -// work around this we have to force Incomplete to be Error so that -// alt! will try the next in the series instead of aborting. -// -// *IMPORTANT* -// It also means this combinator is risky when used with partial -// inputs. So handle with care. -named!(number, - map_res!(alt!( - complete!(do_parse!( // 1.0 - prefix: match_type!(DIGIT) >> - has_dot: punct!(".") >> - suffix: match_type!(DIGIT) >> - (Some(prefix.clone()), Some(has_dot.clone()), Some(suffix.clone())) - )) | - complete!(do_parse!( // 1. - prefix: match_type!(DIGIT) >> - has_dot: punct!(".") >> - (Some(prefix.clone()), Some(has_dot.clone()), None) - )) | - complete!(do_parse!( // .1 - has_dot: punct!(".") >> - suffix: match_type!(DIGIT) >> - (None, Some(has_dot.clone()), Some(suffix.clone())) - )) | - do_parse!( // 1 - prefix: match_type!(DIGIT) >> -// The peek!(not!(..)) make this whole combinator slightly -// safer for partial inputs. - (Some(prefix.clone()), None, None) - )), - triple_to_number - ) -); -// trace_macros!(false); - -named!(boolean_value, - do_parse!( - b: match_type!(BOOLEAN) >> - (Value::Boolean(Positioned{ - val: b.fragment == "true", - pos: b.pos, - })) - ) -); - -named!( - field_value, - do_parse!( - field: match_type!(BAREWORD) >> - punct!("=") >> - value: expression >> - (field, value) - ) -); - -// Helper function to make the return types work for down below. -fn vec_to_tuple(t: (Position, Option)) -> ParseResult { - Ok(Value::Tuple(value_node!( - t.1.unwrap_or(Vec::new()), - t.0.line as usize, - t.0.column as usize - ))) -} - -named!(field_list, - separated_list!(punct!(","), field_value) -); - -named!( - #[doc="Capture a tuple of named fields with values. {=,...}"], - tuple, - map_res!( - do_parse!( - pos: pos >> - punct!("{") >> - v: field_list >> - punct!("}") >> - (pos, Some(v)) - ), - vec_to_tuple - ) -); - -fn tuple_to_list>(t: (Sp, Vec)) -> ParseResult { - return Ok(Value::List(ListDef { - elems: t.1, - pos: t.0.into(), - })); -} - -named!(list_value, - map_res!( - do_parse!( - start: punct!("[") >> - elements: separated_list!(punct!(","), expression) >> - punct!("]") >> - (start.pos, elements) - ), - tuple_to_list - ) -); - -named!(empty_value, - do_parse!( - pos: pos >> - match_type!(EMPTY) >> - (Value::Empty(pos)) - ) -); - -named!(value, - alt!( - boolean_value | - empty_value | - number | - quoted_value | - list_value | - tuple | - selector_value ) - ); - -fn value_to_expression(v: Value) -> ParseResult { - Ok(Expression::Simple(v)) -} - -named!(simple_expression, - map_res!( - value, - value_to_expression - ) -); - -fn tuple_to_binary_expression( - tpl: (Position, BinaryExprType, Expression, Expression), -) -> ParseResult { - Ok(Expression::Binary(BinaryOpDef { - kind: tpl.1, - left: Box::new(tpl.2), - right: Box::new(tpl.3), - pos: Position::new(tpl.0.line as usize, tpl.0.column as usize), - })) -} - -/// do_binary_expr implements precedence based parsing where the more tightly bound parsers -/// are passed in as lowerrule parsers. We default to grouped_expression and simple_expression as -/// the most tightly bound expressions. -macro_rules! do_binary_expr { - ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr) => { - do_binary_expr!($i, $oprule!($($args)*), $typ, alt!(grouped_expression | simple_expression)) - }; - - ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident) => { - do_binary_expr!($i, $oprule!($($args)*), $typ, call!($lowerrule)) - }; - - ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident!( $($lowerargs:tt)* )) => { - map_res!($i, - do_parse!( - pos: pos >> - left: $lowerrule!($($lowerargs)*) >> - $oprule!($($args)*) >> - right: $lowerrule!($($lowerargs)*) >> - (pos, $typ, left, right) - ), - tuple_to_binary_expression - ) - }; -} - -named!(math_expression, - alt!(sum_expression | product_expression) -); - -named!(sum_expression, - alt!(add_expression | sub_expression) -); - -// trace_macros!(true); -named!(add_expression, - do_binary_expr!(punct!("+"), BinaryExprType::Add, alt!(product_expression | simple_expression | grouped_expression)) -); -// trace_macros!(false); - -named!(sub_expression, - do_binary_expr!(punct!("-"), BinaryExprType::Sub, alt!(product_expression | simple_expression | grouped_expression)) -); - -named!(product_expression, - alt!(mul_expression | div_expression) -); - -named!(mul_expression, - do_binary_expr!(punct!("*"), BinaryExprType::Mul) -); - -named!(div_expression, - do_binary_expr!(punct!("/"), BinaryExprType::Div) -); - -// TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence? -fn tuple_to_compare_expression( - tpl: (Position, CompareType, Expression, Expression), -) -> ParseResult { - Ok(Expression::Compare(ComparisonDef { - kind: tpl.1, - left: Box::new(tpl.2), - right: Box::new(tpl.3), - pos: Position::new(tpl.0.line as usize, tpl.0.column as usize), - })) -} - -macro_rules! do_compare_expr { - ($i:expr, $subrule:ident!( $($args:tt)* ), $typ:expr) => { - map_res!($i, - do_parse!( - pos: pos >> - left: alt!(simple_expression | grouped_expression | math_expression) >> - $subrule!($($args)*) >> - right: expression >> - (pos, $typ, left, right) - ), - tuple_to_compare_expression - ) - }; -} - -named!(eqeq_expression, - do_compare_expr!(punct!("=="), CompareType::Equal) -); - -named!(not_eqeq_expression, - do_compare_expr!(punct!("!="), CompareType::NotEqual) -); - -named!(lt_eqeq_expression, - do_compare_expr!(punct!("<="), CompareType::LTEqual) -); - -named!(gt_eqeq_expression, - do_compare_expr!(punct!(">="), CompareType::GTEqual) -); - -named!(gt_expression, - do_compare_expr!(punct!(">"), CompareType::GT) -); - -named!(lt_expression, - do_compare_expr!(punct!("<"), CompareType::LT) -); - -fn expression_to_grouped_expression(e: Expression) -> ParseResult { - Ok(Expression::Grouped(Box::new(e))) -} - -named!(grouped_expression, - map_res!( - preceded!(punct!("("), terminated!(expression, punct!(")"))), - expression_to_grouped_expression - ) -); - -fn symbol_or_expression(input: TokenIter) -> NomResult { - let sym = do_parse!(input, sym: symbol >> (sym)); - - match sym { - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - IResult::Error(_) => { - // TODO(jwall): Still missing some. But we need to avoid recursion - return grouped_expression(input); - } - IResult::Done(rest, val) => { - return IResult::Done(rest, Expression::Simple(val)); - } - } -} - -fn selector_list(input: TokenIter) -> NomResult { - let (rest, head) = match symbol_or_expression(input) { - IResult::Done(rest, val) => (rest, val), - IResult::Error(e) => { - return IResult::Error(e); - } - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - }; - - let (rest, is_dot) = match punct!(rest, ".") { - IResult::Done(rest, tok) => (rest, Some(tok)), - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - IResult::Error(_) => (rest, None), - }; - - let (rest, list) = if is_dot.is_some() { - let (rest, list) = match separated_list!( - rest, - punct!("."), - alt!(match_type!(BAREWORD) | match_type!(DIGIT)) - ) { - IResult::Done(rest, val) => (rest, val), - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - IResult::Error(e) => { - return IResult::Error(e); - } - }; - - if list.is_empty() { - return IResult::Error(nom::ErrorKind::Custom(error::Error::new( - "(.) with no selector fields after".to_string(), - error::ErrorType::IncompleteParsing, - is_dot.unwrap().pos, - ))); - } else { - (rest, Some(list)) - } - } else { - (rest, None) - }; - - let sel_list = SelectorList { - head: Box::new(head), - tail: list, - }; - - return IResult::Done(rest, sel_list); -} - -fn tuple_to_copy(t: (SelectorDef, FieldList)) -> ParseResult { - let pos = t.0.pos.clone(); - Ok(Expression::Copy(CopyDef { - selector: t.0, - fields: t.1, - pos: pos, - })) -} - -named!(copy_expression, - map_res!( - do_parse!( - pos: pos >> - selector: selector_list >> - punct!("{") >> - fields: field_list >> - punct!("}") >> - (SelectorDef::new(selector, pos.line, pos.column as usize), fields) - ), - tuple_to_copy - ) -); - -fn tuple_to_macro(mut t: (Position, Vec, Value)) -> ParseResult { - match t.2 { - Value::Tuple(v) => Ok(Expression::Macro(MacroDef { - argdefs: t.1 - .drain(0..) - .map(|s| Positioned { - pos: s.pos().clone(), - val: s.to_string(), - }) - .collect(), - fields: v.val, - pos: t.0, - })), - // TODO(jwall): Show a better version of the unexpected parsed value. - val => Err(error::Error::new( - format!("Expected Tuple Got {:?}", val), - error::ErrorType::UnexpectedToken, - t.0, - )), - } -} - -named!(arglist, error::Error>, separated_list!(punct!(","), symbol)); - -named!(macro_expression, - map_res!( - do_parse!( - pos: pos >> - word!("macro") >> - punct!("(") >> - arglist: arglist >> - punct!(")") >> - punct!("=>") >> - map: tuple >> - (pos, arglist, map) - ), - tuple_to_macro - ) -); - -fn tuple_to_select(t: (Position, Expression, Expression, Value)) -> ParseResult { - match t.3 { - Value::Tuple(v) => Ok(Expression::Select(SelectDef { - val: Box::new(t.1), - default: Box::new(t.2), - tuple: v.val, - pos: t.0, - })), - val => Err(error::Error::new( - format!("Expected Tuple Got {:?}", val), - error::ErrorType::UnexpectedToken, - t.0, - )), - } -} - -named!(select_expression, - map_res!( - do_parse!( - start: word!("select") >> - val: terminated!(expression, punct!(",")) >> - default: terminated!(expression, punct!(",")) >> - map: tuple >> - (start.pos.clone(), val, default, map) - ), - tuple_to_select - ) -); - -fn tuple_to_format(t: (Token, Vec)) -> ParseResult { - Ok(Expression::Format(FormatDef { - template: t.0.fragment.to_string(), - args: t.1, - pos: t.0.pos, - })) -} - -named!(format_expression, - map_res!( - do_parse!( - tmpl: match_type!(STR) >> - punct!("%") >> - punct!("(") >> - args: separated_list!(punct!(","), expression) >> - punct!(")") >> - (tmpl, args) - ), - tuple_to_format - ) -); - -fn tuple_to_call(t: (Position, Value, Vec)) -> ParseResult { - if let Value::Selector(def) = t.1 { - Ok(Expression::Call(CallDef { - macroref: def, - arglist: t.2, - pos: Position::new(t.0.line as usize, t.0.column as usize), - })) - } else { - Err(error::Error::new( - format!("Expected Selector Got {:?}", t.0), - error::ErrorType::UnexpectedToken, - Position::new(t.0.line as usize, t.0.column as usize), - )) - } -} - -fn vec_to_selector_value(t: (Position, SelectorList)) -> ParseResult { - Ok(Value::Selector(SelectorDef::new( - t.1, - t.0.line as usize, - t.0.column as usize, - ))) -} - -named!(selector_value, - map_res!( - do_parse!( - sl: selector_list >> - (sl.head.pos().clone(), sl) - ), - vec_to_selector_value - ) -); - -named!(call_expression, - map_res!( - do_parse!( - macroname: selector_value >> - punct!("(") >> - args: separated_list!(punct!(","), expression) >> - punct!(")") >> - (macroname.pos().clone(), macroname, args) - ), - tuple_to_call - ) -); - -fn symbol_or_list(input: TokenIter) -> NomResult { - let sym = do_parse!(input, sym: symbol >> (sym)); - - match sym { - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - IResult::Error(_) => { - // TODO(jwall): Still missing some. But we need to avoid recursion - match list_value(input) { - IResult::Incomplete(i) => { - return IResult::Incomplete(i); - } - IResult::Error(e) => { - return IResult::Error(e); - } - IResult::Done(i, val) => { - return IResult::Done(i, val); - } - } - } - IResult::Done(rest, val) => { - return IResult::Done(rest, val); - } - } -} - -fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult { - let pos = tpl.0; - let t = if &tpl.1.fragment == "map" { - ListOpType::Map - } else if &tpl.1.fragment == "filter" { - ListOpType::Filter - } else { - return Err(error::Error::new( - format!( - "Expected one of 'map' or 'filter' but got '{}'", - tpl.1.fragment - ), - error::ErrorType::UnexpectedToken, - pos, - )); - }; - let macroname = tpl.2; - let list = tpl.3; - if let Value::Selector(mut def) = macroname { - // First of all we need to assert that this is a selector of at least - // two sections. - let fieldname: String = match &mut def.sel.tail { - &mut None => { - return Err(error::Error::new( - format!("Missing a result field for the macro"), - error::ErrorType::IncompleteParsing, - pos, - )); - } - &mut Some(ref mut tl) => { - if tl.len() < 1 { - return Err(error::Error::new( - format!("Missing a result field for the macro"), - error::ErrorType::IncompleteParsing, - def.pos.clone(), - )); - } - let fname = tl.pop(); - fname.unwrap().fragment - } - }; - if let Value::List(ldef) = list { - return Ok(Expression::ListOp(ListOpDef { - typ: t, - mac: def, - field: fieldname, - target: ldef, - pos: pos, - })); - } - // TODO(jwall): We should print a pretter message than debug formatting here. - return Err(error::Error::new( - format!("Expected a list but got {:?}", list), - error::ErrorType::UnexpectedToken, - pos, - )); - } - return Err(error::Error::new( - format!("Expected a selector but got {:?}", macroname), - error::ErrorType::UnexpectedToken, - pos, - )); -} - -named!(list_op_expression, - map_res!( - do_parse!( - pos: pos >> - optype: alt!(word!("map") | word!("filter")) >> - macroname: selector_value >> - list: symbol_or_list >> - (pos, optype, macroname, list) - ), - tuple_to_list_op - ) -); - -// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters -// alot. We need to process alternatives in order of decreasing -// specificity. Unfortunately this means we are required to go in a -// decreasing size order which messes with alt!'s completion logic. To -// work around this we have to force Incomplete to be Error so that -// alt! will try the next in the series instead of aborting. -// -// *IMPORTANT* -// It also means this combinator is risky when used with partial -// inputs. So handle with care. -named!(expression, - do_parse!( - expr: alt!( - complete!(list_op_expression) | - complete!(math_expression) | - complete!(eqeq_expression) | - complete!(not_eqeq_expression) | - complete!(lt_eqeq_expression) | - complete!(gt_eqeq_expression) | - complete!(gt_expression) | - complete!(lt_expression) | - complete!(macro_expression) | - complete!(format_expression) | - complete!(select_expression) | - complete!(call_expression) | - complete!(copy_expression) | - complete!(grouped_expression) | - simple_expression - ) >> - (expr) - ) -); - -fn expression_to_statement(v: Expression) -> ParseResult { - Ok(Statement::Expression(v)) -} - -named!(expression_statement, - map_res!( - terminated!(expression, punct!(";")), - expression_to_statement - ) -); - -fn tuple_to_let(t: (Token, Expression)) -> ParseResult { - Ok(Statement::Let(LetDef { - name: t.0, - value: t.1, - })) -} - -named!(let_statement, - map_res!( - do_parse!( - word!("let") >> - name: match_type!(BAREWORD) >> - punct!("=") >> - val: expression >> - punct!(";") >> - (name, val) - ), - tuple_to_let - ) -); - -fn tuple_to_import(t: (Token, Token)) -> ParseResult { - Ok(Statement::Import(ImportDef { - path: t.0, - name: t.1, - })) -} - -named!(import_statement, - map_res!( - do_parse!( - word!("import") >> - path: match_type!(STR) >> - word!("as") >> - name: match_type!(BAREWORD) >> - punct!(";") >> - (path, name) - ), - tuple_to_import - ) -); - -named!(statement, - do_parse!( - stmt: alt_complete!( - import_statement | - let_statement | - expression_statement - ) >> - (stmt) - ) -); - -/// Parses a LocatedSpan into a list of Statements or an error::Error. -pub fn parse(input: LocatedSpan<&str>) -> Result, error::Error> { - match tokenize(input) { - Ok(tokenized) => { - let mut out = Vec::new(); - let mut i_ = TokenIter { - source: tokenized.as_slice(), - }; - loop { - let i = i_.clone(); - if i[0].typ == TokenType::END { - break; - } - match statement(i) { - IResult::Error(nom::ErrorKind::Custom(e)) => { - return Err(e); - } - IResult::Error(e) => { - return Err(error::Error::new_with_errorkind( - format!("Statement Parse error: {:?} current token: {:?}", e, i_[0]), - error::ErrorType::ParseError, - Position { - line: i_[0].pos.line, - column: i_[0].pos.column, - }, - e, - )); - } - IResult::Incomplete(ei) => { - return Err(error::Error::new( - format!("Unexpected end of parsing input: {:?}", ei), - error::ErrorType::IncompleteParsing, - Position { - line: i_[0].pos.line, - column: i_[0].pos.column, - }, - )); - } - IResult::Done(rest, stmt) => { - out.push(stmt); - i_ = rest; - if i_.input_len() == 0 { - break; - } - } - } - } - return Ok(out); - } - Err(e) => { - return Err(error::Error::new( - format!("Tokenization Error {:?}", e.1), - error::ErrorType::ParseError, - e.0, - )); - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use tokenizer::{tokenize, TokenIter}; - - use nom::IResult; - use nom_locate::LocatedSpan; - - macro_rules! assert_parse { - ($parsemac:ident($i:expr), $out:expr) => { - assert_parse!($i, $parsemac, $out) - }; - ($i:expr, $f:expr, $out:expr) => {{ - let input = LocatedSpan::new($i); - match tokenize(input) { - Err(e) => assert!(false, format!("Tokenizer Error: {:?}", e)), - Ok(val) => match $f(TokenIter { - source: val.as_slice(), - }) { - IResult::Done(_, result) => assert_eq!(result, $out), - other => assert!(false, format!("Expected Done got {:?}", other)), - }, - } - };}; - } - - macro_rules! assert_error { - ($parsemac:ident($i:expr)) => { - assert_error!($i, $parsemac) - }; - ($i:expr, $f:expr) => {{ - let input = LocatedSpan::new($i); - match tokenize(input) { - Err(_) => assert!(true), - Ok(val) => { - let result = $f(TokenIter { - source: val.as_slice(), - }); - assert!(result.is_err(), format!("Not an error: {:?}", result)) - } - } - }}; - } - - #[test] - fn test_null_parsing() { - 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] - fn test_symbol_parsing() { - assert_parse!( - symbol("foo"), - Value::Symbol(value_node!("foo".to_string(), 1, 1)) - ); - assert_parse!( - symbol("foo-bar"), - Value::Symbol(value_node!("foo-bar".to_string(), 1, 1)) - ); - assert_parse!( - symbol("foo_bar"), - Value::Symbol(value_node!("foo_bar".to_string(), 1, 1)) - ); - } - - #[test] - fn test_selector_parsing() { - assert_error!(selector_value("foo.")); - assert_parse!( - selector_value("foo.bar "), - Value::Selector(make_selector!(make_expr!("foo".to_string(), 1, 1) => [ - make_tok!("bar", 1, 5)] => - 1, 1)) - ); - assert_parse!( - selector_value("foo.0 "), - Value::Selector(make_selector!(make_expr!("foo".to_string(), 1, 1) => [ - make_tok!(DIGIT => "0", 1, 5)] => - 1, 1)) - ); - assert_parse!( - selector_value("foo.bar;"), - Value::Selector(make_selector!(make_expr!("foo", 1, 1) => - [ - make_tok!("bar", 1, 5) - ] => - 1, 1)) - ); - assert_parse!( - selector_value("({foo=1}).foo "), - Value::Selector( - make_selector!(Expression::Grouped(Box::new(Expression::Simple( - Value::Tuple(value_node!( - vec![(make_tok!("foo", 1, 3), Expression::Simple(Value::Int(Positioned::new(1, 1, 7))))], - 1, 3)) - ))) => [ make_tok!("foo", 1, 11) ] => 1, 2) - ) - ); - } - - #[test] - fn test_statement_parse() { - let stmt = "import \"foo\" as foo;"; - assert_parse!( - statement(stmt), - Statement::Import(ImportDef { - path: make_tok!(QUOT => "foo", 1,8), - name: make_tok!("foo", 1, 17), - }) - ); - - assert_error!(import_statement("import \"foo\"")); - - assert_parse!( - statement("let foo = 1.0 ;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), - }) - ); - - assert_parse!( - statement("let foo = 1 + 1 * 2;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), - right: Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))), - pos: Position::new(1, 15), - })), - pos: Position::new(1, 11), - }), - }) - ); - - assert_parse!( - statement("let foo = (1 + 1) * 2;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - left: Box::new(Expression::Grouped(Box::new(Expression::Binary( - BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 12)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 16)))), - pos: Position::new(1, 12), - }, - )))), - right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 21)))), - pos: Position::new(1, 11), - }), - }) - ); - - assert_parse!( - statement("let foo = 1 * 1 + 2;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))), - pos: Position::new(1, 11), - })), - right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))), - pos: Position::new(1, 11), - }), - }) - ); - - assert_parse!( - statement("// comment\nlet foo = 1.0 ;"), - Statement::Let(LetDef { - name: make_tok!("foo", 2, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 2, 11))), - }) - ); - - assert_parse!( - statement("1.0;"), - Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) - ); - } - - #[test] - fn test_import_statement_parse() { - assert_error!(import_statement("import")); - assert_error!(import_statement("import \"foo\"")); - assert_error!(import_statement("import \"foo\" as")); - assert_error!(import_statement("import \"foo\" as foo")); - - let import_stmt = "import \"foo\" as foo;"; - assert_parse!( - import_statement(import_stmt), - Statement::Import(ImportDef { - path: make_tok!(QUOT => "foo", 1, 8), - name: make_tok!("foo", 1, 17), - }) - ); - } - - #[test] - fn test_let_statement_parse() { - assert_error!(let_statement("foo")); - assert_error!(let_statement("let \"foo\"")); - assert_error!(let_statement("let 1")); - assert_error!(let_statement("let")); - assert_error!(let_statement("let foo")); - assert_error!(let_statement("let foo =")); - assert_error!(let_statement("let foo = ")); - assert_error!(let_statement("let foo = 1")); - - assert_parse!( - let_statement("let foo = 1.0 ;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), - }) - ); - - assert_parse!( - let_statement("let foo = // comment\n1.0 ;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 2, 1))), - }) - ); - - assert_parse!( - let_statement("let foo = 1.0 // comment\n;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), - }) - ); - - assert_parse!( - let_statement("let foo= 1.0;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 1, 10))), - }) - ); - - assert_parse!( - let_statement("let foo =1.0;"), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 5), - value: Expression::Simple(Value::Float(value_node!(1.0, 1, 10))), - }) - ); - } - - #[test] - fn test_expression_statement_parse() { - assert_error!(expression_statement("foo")); - assert_parse!( - expression_statement("1.0;"), - Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) - ); - assert_parse!( - expression_statement("1.0 ;"), - Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) - ); - assert_parse!( - expression_statement(" 1.0;"), - Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 2)))) - ); - assert_parse!( - expression_statement("foo;"), - Statement::Expression(Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 1), - 1, - 1 - )))) - ); - assert_parse!( - expression_statement("foo ;"), - Statement::Expression(Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 2), - 1, - 1 - )))) - ); - assert_parse!( - expression_statement(" foo;"), - Statement::Expression(Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 2), - 1, - 2 - )))) - ); - assert_parse!( - expression_statement("\"foo\";"), - Statement::Expression(Expression::Simple(Value::String(value_node!( - "foo".to_string(), - 1, - 1 - )))) - ); - assert_parse!( - expression_statement("\"foo\" ;"), - Statement::Expression(Expression::Simple(Value::String(value_node!( - "foo".to_string(), - 1, - 1 - )))) - ); - assert_parse!( - expression_statement(" \"foo\";"), - Statement::Expression(Expression::Simple(Value::String(value_node!( - "foo".to_string(), - 1, - 2 - )))) - ); - } - - #[test] - fn test_expression_parse() { - assert_parse!( - expression("NULL"), - Expression::Simple(Value::Empty(Position::new(1, 1))) - ); - assert_parse!( - expression("\"foo\""), - Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 1))) - ); - assert_parse!( - expression("1"), - Expression::Simple(Value::Int(value_node!(1, 1, 1))) - ); - assert_parse!( - expression("foo "), - Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 1), - 1, - 1 - ))) - ); - assert_parse!( - expression("foo.bar "), - Expression::Simple(Value::Selector(make_selector!(make_expr!("foo", 1, 1) => - [ make_tok!("bar", 1, 5) ] => - 1, 1))) - ); - assert_parse!( - math_expression("1 + 1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(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::Sub, - left: Box::new(Expression::Simple(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::Mul, - left: Box::new(Expression::Simple(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::Div, - left: Box::new(Expression::Simple(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::Grouped(Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), - pos: Position::new(1, 2), - }))) - ); - assert_parse!( - expression("1 / 1 + 1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))), - pos: Position::new(1, 1), - })), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 9)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("(1 + 1) * 1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - left: Box::new(Expression::Grouped(Box::new(Expression::Binary( - BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), - pos: Position::new(1, 2), - } - )))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("1 > 1"), - Expression::Compare(ComparisonDef { - kind: CompareType::GT, - left: Box::new(Expression::Simple(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::Compare(ComparisonDef { - kind: CompareType::LT, - left: Box::new(Expression::Simple(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::Compare(ComparisonDef { - kind: CompareType::LTEqual, - left: Box::new(Expression::Simple(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::Compare(ComparisonDef { - kind: CompareType::GTEqual, - left: Box::new(Expression::Simple(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::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("1-1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Sub, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("1*1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Mul, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("1/1"), - Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Div, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("macro (arg1, arg2) => { foo = arg1 }"), - Expression::Macro(MacroDef { - argdefs: vec![ - value_node!("arg1".to_string(), 1, 8), - value_node!("arg2".to_string(), 1, 14), - ], - fields: vec![ - ( - make_tok!("foo", 1, 25), - Expression::Simple(Value::Selector(make_selector!( - make_expr!("arg1", 1, 31), - 1, - 31 - ))), - ), - ], - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("select foo, 1, { foo = 2 }"), - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 8), - 1, - 8 - )))), - default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 13)))), - tuple: vec![ - ( - make_tok!("foo", 1, 18), - Expression::Simple(Value::Int(value_node!(2, 1, 24))), - ), - ], - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("foo.bar (1, \"foo\")"), - Expression::Call(CallDef { - macroref: make_selector!(make_expr!("foo", 1, 1) => - [ make_tok!("bar", 1, 5) ] => - 1, 1), - arglist: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 10))), - Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 13))), - ], - pos: Position::new(1, 1), - }) - ); - assert_parse!( - expression("(1 + 1)"), - Expression::Grouped(Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), - pos: Position::new(1, 2), // FIXME(jwall): grouped expressions appear to be getting positioned wrong - }))) - ); - assert_parse!( - expression("[1, 1]"), - Expression::Simple(Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 2))), - Expression::Simple(Value::Int(value_node!(1, 1, 5))), - ], - pos: Position::new(1, 1), - })) - ); - } - - #[test] - fn test_format_parse() { - assert_error!(format_expression("\"foo")); - assert_error!(format_expression("\"foo\"")); - assert_error!(format_expression("\"foo\" %")); - assert_error!(format_expression("\"foo\" % (, 2")); - - assert_parse!( - format_expression("\"foo @ @\" % (1, 2)"), - Expression::Format(FormatDef { - template: "foo @ @".to_string(), - args: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 14))), - Expression::Simple(Value::Int(value_node!(2, 1, 17))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - format_expression("\"foo @ @\"%(1, 2)"), - Expression::Format(FormatDef { - template: "foo @ @".to_string(), - args: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 12))), - Expression::Simple(Value::Int(value_node!(2, 1, 15))), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_call_parse() { - assert_error!(call_expression("foo")); - assert_error!(call_expression("foo (")); - assert_error!(call_expression("foo (1")); - assert_error!(call_expression("foo (1,")); - assert_error!(call_expression("foo (1,2")); - - assert_parse!( - call_expression("foo (1, \"foo\")"), - Expression::Call(CallDef { - macroref: make_selector!(make_expr!("foo", 1, 1), 1, 1), - arglist: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 6))), - Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 9))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - call_expression("foo.bar (1, \"foo\")"), - Expression::Call(CallDef { - macroref: make_selector!(make_expr!("foo") => [ make_tok!("bar", 1, 5) ] => 1, 1), - arglist: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 10))), - Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 13))), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_select_parse() { - assert_error!(select_expression("select")); - assert_error!(select_expression("select foo")); - assert_error!(select_expression("select foo, 1")); - assert_error!(select_expression("select foo, 1, {")); - - assert_parse!( - select_expression("select foo, 1, { foo = 2 }"), - Expression::Select(SelectDef { - val: Box::new(Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 8), - 1, - 8 - )))), - default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 13)))), - tuple: vec![ - ( - make_tok!("foo", 1, 18), - Expression::Simple(Value::Int(value_node!(2, 1, 24))), - ), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_macro_expression_parsing() { - assert_error!(macro_expression("foo")); - assert_error!(macro_expression("macro \"foo\"")); - assert_error!(macro_expression("macro 1")); - assert_error!(macro_expression("macro")); - assert_error!(macro_expression("macro (")); - assert_error!(macro_expression("macro (arg")); - assert_error!(macro_expression("macro (arg, arg2")); - assert_error!(macro_expression("macro (arg1, arg2) =>")); - assert_error!(macro_expression("macro (arg1, arg2) => {")); - assert_error!(macro_expression("macro (arg1, arg2) => { foo")); - assert_error!(macro_expression("macro (arg1, arg2) => { foo =")); - - assert_parse!( - macro_expression("macro (arg1, arg2) => {foo=1,bar=2}"), - Expression::Macro(MacroDef { - argdefs: vec![ - value_node!("arg1".to_string(), 1, 8), - value_node!("arg2".to_string(), 1, 14), - ], - fields: vec![ - ( - make_tok!("foo", 1, 24), - Expression::Simple(Value::Int(value_node!(1, 1, 28))), - ), - ( - make_tok!("bar", 1, 30), - Expression::Simple(Value::Int(value_node!(2, 1, 34))), - ), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_copy_parse() { - assert_error!(copy_expression("{}")); - assert_error!(copy_expression("foo")); - assert_error!(copy_expression("foo{")); - - assert_parse!( - copy_expression("foo{}"), - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("foo", 1, 1), 1, 1), - fields: Vec::new(), - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - copy_expression("foo{bar=1}"), - Expression::Copy(CopyDef { - selector: make_selector!(make_expr!("foo", 1, 1), 1, 1), - fields: vec![ - ( - make_tok!("bar", 1, 5), - Expression::Simple(Value::Int(value_node!(1, 1, 9))), - ), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_grouped_expression_parse() { - assert_error!(grouped_expression("foo")); - assert_error!(grouped_expression("(foo")); - assert_parse!( - grouped_expression("(foo)"), - Expression::Grouped(Box::new(Expression::Simple(Value::Selector( - make_selector!(make_expr!("foo", 1, 2), 1, 2) - )))) - ); - assert_parse!( - grouped_expression("(1 + 1)"), - Expression::Grouped(Box::new(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), - pos: Position::new(1, 2), - }))) - ); - } - - #[test] - fn test_list_value_parse() { - assert_error!(list_value("foo")); - assert_error!(list_value("[foo")); - assert_error!(list_value("// commen\n[foo")); - - assert_parse!( - list_value("[foo]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Selector(make_selector!( - make_expr!("foo", 1, 2), - 1, - 2 - ))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - list_value("[1, 1]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 2))), - Expression::Simple(Value::Int(value_node!(1, 1, 5))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - list_value("// comment\n[1, 1]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 2, 2))), - Expression::Simple(Value::Int(value_node!(1, 2, 5))), - ], - pos: Position::new(2, 1), - }) - ); - - assert_parse!( - list_value("[// comment\n1, 1]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 2, 2))), - Expression::Simple(Value::Int(value_node!(1, 2, 5))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - list_value("[1, // comment\n1]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 2))), - Expression::Simple(Value::Int(value_node!(1, 2, 1))), - ], - pos: Position::new(1, 1), - }) - ); - - assert_parse!( - list_value("[1, 1 // comment\n]"), - Value::List(ListDef { - elems: vec![ - Expression::Simple(Value::Int(value_node!(1, 1, 2))), - Expression::Simple(Value::Int(value_node!(1, 1, 5))), - ], - pos: Position::new(1, 1), - }) - ); - } - - #[test] - fn test_tuple_parse() { - assert_error!(tuple("{")); - assert_error!(tuple("{ foo")); - assert_error!(tuple("{ foo =")); - assert_error!(tuple("{ foo = 1")); - assert_error!(tuple("{ foo = 1,")); - assert_error!(tuple("{ foo = 1, bar =")); - assert_error!(tuple("// comment\n{ foo = 1, bar =")); - - assert_parse!(tuple("{ }"), Value::Tuple(value_node!(vec![], 1, 1))); - - assert_parse!( - tuple("{ foo = 1 }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 1, 3), - Expression::Simple(Value::Int(value_node!(1, 1, 9))), - ), - ], - 1, - 1 - )) - ); - - assert_parse!( - tuple("// comment\n{ foo = 1 }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 2, 3), - Expression::Simple(Value::Int(value_node!(1, 2, 9))), - ), - ], - 1, - 1 - )) - ); - - assert_parse!( - tuple("{// comment\n foo = 1 }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 2, 2), - Expression::Simple(Value::Int(value_node!(1, 2, 8))), - ), - ], - 1, - 1 - )) - ); - - assert_parse!( - tuple("{ foo = 1// comment\n }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 1, 3), - Expression::Simple(Value::Int(value_node!(1, 1, 9))), - ), - ], - 1, - 1 - )) - ); - - assert_parse!( - tuple("{ foo = 1, bar = \"1\" }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 1, 3), - Expression::Simple(Value::Int(value_node!(1, 1, 9))), - ), - ( - make_tok!("bar", 1, 12), - Expression::Simple(Value::String(value_node!( - "1".to_string(), - Position::new(1, 18) - ))), - ), - ], - 1, - 1 - )) - ); - assert_parse!( - tuple("{ foo = 1, // comment\nbar = \"1\" }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 1, 3), - Expression::Simple(Value::Int(value_node!(1, 1, 9))), - ), - ( - make_tok!("bar", 2, 1), - Expression::Simple(Value::String(value_node!( - "1".to_string(), - Position::new(2, 7) - ))), - ), - ], - 1, - 1 - )) - ); - assert_parse!( - tuple("{ foo = 1, bar = {} }"), - Value::Tuple(value_node!( - vec![ - ( - make_tok!("foo", 1, 3), - Expression::Simple(Value::Int(value_node!(1, Position::new(1, 9)))), - ), - ( - make_tok!("bar", 1, 12), - Expression::Simple(Value::Tuple(value_node!( - Vec::new(), - Position::new(1, 17) - ))), - ), - ], - 1, - 1 - )) - ); - } - - #[test] - fn test_field_list_parse() { - let mut f_list = "foo = 1, quux = 2;"; - assert_parse!( - field_list(f_list), - vec![ - (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), - (make_tok!("quux", 1, 10), make_expr!(2 => int, 1, 17)), - ] - ); - - f_list = "foo = 1, // comment\nquux = 2;"; - assert_parse!( - field_list(f_list), - vec![ - (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), - (make_tok!("quux", 2, 1), make_expr!(2 => int, 2, 8)), - ] - ); - - f_list = "foo = 1,\n// comment\nquux = 2;"; - assert_parse!( - field_list(f_list), - vec![ - (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), - (make_tok!("quux", 3, 1), make_expr!(2 => int, 3, 8)), - ] - ); - } - - #[test] - fn test_field_value_parse() { - assert_error!(field_value("foo")); - assert_error!(field_value("// comment\nfoo")); - assert_error!(field_value("foo =")); - - assert_parse!( - field_value("foo = 1"), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Int(value_node!(1, 1, 7))) - ) - ); - assert_parse!( - field_value("foo = 1 // foo comment\n"), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Int(value_node!(1, 1, 7))) - ) - ); - assert_parse!( - field_value("foo // foo comment\n = 1"), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Int(value_node!(1, 2, 4))) - ) - ); - assert_parse!( - field_value("// foo comment\nfoo = 1"), - ( - make_tok!("foo", 2, 1), - Expression::Simple(Value::Int(value_node!(1, 2, 7))) - ) - ); - assert_parse!( - field_value("foo = \"1\""), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::String(value_node!("1".to_string(), 1, 7))) - ) - ); - assert_parse!( - field_value("foo = bar "), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Selector(make_selector!( - make_expr!("bar", 1, 7), - 1, - 7 - ))) - ) - ); - assert_parse!( - field_value("foo = bar.baz "), - ( - make_tok!("foo", 1, 1), - Expression::Simple(Value::Selector( - make_selector!(make_expr!("bar", 1, 7) => [ make_tok!("baz", 1, 11) ] => 1, 7), - )) - ) - ); - } - - #[test] - fn test_number_parsing() { - assert_error!(number(".")); - assert_error!(number(". ")); - assert_parse!(number("1.0"), Value::Float(value_node!(1.0, 1, 1))); - assert_parse!(number("1."), Value::Float(value_node!(1.0, 1, 1))); - assert_parse!(number("1"), Value::Int(value_node!(1, 1, 1))); - assert_parse!(number(".1"), Value::Float(value_node!(0.1, 1, 1))); - } - - #[test] - fn test_parse() { - let bad_input = LocatedSpan::new("import mylib as lib;"); - let bad_result = parse(bad_input); - assert!(bad_result.is_err()); - - // Valid parsing tree - let input = LocatedSpan::new("import \"mylib\" as lib;let foo = 1;1+1;"); - let result = parse(input); - assert!(result.is_ok(), format!("Expected Ok, Got {:?}", result)); - let tpl = result.unwrap(); - assert_eq!( - tpl, - vec![ - Statement::Import(ImportDef { - path: make_tok!(QUOT => "mylib", 1, 8), - name: make_tok!("lib", 1, 19), - }), - Statement::Let(LetDef { - name: make_tok!("foo", 1, 27), - value: Expression::Simple(Value::Int(value_node!(1, 1, 33))), - }), - Statement::Expression(Expression::Binary(BinaryOpDef { - kind: BinaryExprType::Add, - left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 35)))), - right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 37)))), - pos: Position::new(1, 35), - })), - ] - ); - } -} diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 0000000..af85b49 --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,864 @@ +// 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 Parsing stage of the ucg compiler. +use std::borrow::Borrow; +use std::str::FromStr; + +use nom; +use nom::IResult; +use nom::InputLength; +use nom_locate::LocatedSpan; + +use ast::*; +use error; +use tokenizer::*; + +type NomResult<'a, O> = nom::IResult, O, error::Error>; + +type ParseResult = Result; + +fn symbol_to_value(s: &Token) -> ParseResult { + Ok(Value::Symbol(value_node!( + s.fragment.to_string(), + s.pos.clone() + ))) +} + +// symbol is a bare unquoted field. +named!(symbol, + match_type!(BAREWORD => symbol_to_value) +); + +fn str_to_value(s: &Token) -> ParseResult { + Ok(Value::String(value_node!( + s.fragment.to_string(), + s.pos.clone() + ))) +} + +// quoted_value is a quoted string. +named!(quoted_value, + match_type!(STR => str_to_value) +); + +// Helper function to make the return types work for down below. +fn triple_to_number(v: (Option, Option, Option)) -> ParseResult { + let (pref, mut pref_pos) = match v.0 { + None => ("", Position::new(0, 0)), + Some(ref bs) => (bs.fragment.borrow(), bs.pos.clone()), + }; + + let has_dot = v.1.is_some(); + + if v.0.is_some() && !has_dot && v.2.is_none() { + let i = match FromStr::from_str(pref) { + Ok(i) => i, + Err(_) => { + return Err(error::Error::new( + format!("Not an integer! {}", pref), + error::ErrorType::UnexpectedToken, + pref_pos, + )) + } + }; + return Ok(Value::Int(value_node!(i, pref_pos))); + } + + if v.0.is_none() && has_dot { + pref_pos = v.1.unwrap().pos; + } + + let (maybepos, suf) = match v.2 { + None => (None, "".to_string()), + Some(bs) => (Some(bs.pos), bs.fragment), + }; + + let to_parse = pref.to_string() + "." + &suf; + // TODO(jwall): if there is an error we should report where that error occured. + let f = match FromStr::from_str(&to_parse) { + Ok(f) => f, + Err(_) => { + return Err(error::Error::new( + format!("Not a float! {}", to_parse), + error::ErrorType::UnexpectedToken, + maybepos.unwrap(), + )) + } + }; + return Ok(Value::Float(value_node!(f, pref_pos))); +} + +// trace_macros!(true); + +// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters +// alot. We need to process alternatives in order of decreasing +// specificity. Unfortunately this means we are required to go in a +// decreasing size order which messes with alt!'s completion logic. To +// work around this we have to force Incomplete to be Error so that +// alt! will try the next in the series instead of aborting. +// +// *IMPORTANT* +// It also means this combinator is risky when used with partial +// inputs. So handle with care. +named!(number, + map_res!(alt!( + complete!(do_parse!( // 1.0 + prefix: match_type!(DIGIT) >> + has_dot: punct!(".") >> + suffix: match_type!(DIGIT) >> + (Some(prefix.clone()), Some(has_dot.clone()), Some(suffix.clone())) + )) | + complete!(do_parse!( // 1. + prefix: match_type!(DIGIT) >> + has_dot: punct!(".") >> + (Some(prefix.clone()), Some(has_dot.clone()), None) + )) | + complete!(do_parse!( // .1 + has_dot: punct!(".") >> + suffix: match_type!(DIGIT) >> + (None, Some(has_dot.clone()), Some(suffix.clone())) + )) | + do_parse!( // 1 + prefix: match_type!(DIGIT) >> +// The peek!(not!(..)) make this whole combinator slightly +// safer for partial inputs. + (Some(prefix.clone()), None, None) + )), + triple_to_number + ) +); +// trace_macros!(false); + +named!(boolean_value, + do_parse!( + b: match_type!(BOOLEAN) >> + (Value::Boolean(Positioned{ + val: b.fragment == "true", + pos: b.pos, + })) + ) +); + +named!( + field_value, + do_parse!( + field: match_type!(BAREWORD) >> + punct!("=") >> + value: expression >> + (field, value) + ) +); + +// Helper function to make the return types work for down below. +fn vec_to_tuple(t: (Position, Option)) -> ParseResult { + Ok(Value::Tuple(value_node!( + t.1.unwrap_or(Vec::new()), + t.0.line as usize, + t.0.column as usize + ))) +} + +named!(field_list, + separated_list!(punct!(","), field_value) +); + +named!( + #[doc="Capture a tuple of named fields with values. {=,...}"], + tuple, + map_res!( + do_parse!( + pos: pos >> + punct!("{") >> + v: field_list >> + punct!("}") >> + (pos, Some(v)) + ), + vec_to_tuple + ) +); + +fn tuple_to_list>(t: (Sp, Vec)) -> ParseResult { + return Ok(Value::List(ListDef { + elems: t.1, + pos: t.0.into(), + })); +} + +named!(list_value, + map_res!( + do_parse!( + start: punct!("[") >> + elements: separated_list!(punct!(","), expression) >> + punct!("]") >> + (start.pos, elements) + ), + tuple_to_list + ) +); + +named!(empty_value, + do_parse!( + pos: pos >> + match_type!(EMPTY) >> + (Value::Empty(pos)) + ) +); + +named!(value, + alt!( + boolean_value | + empty_value | + number | + quoted_value | + list_value | + tuple | + selector_value ) + ); + +fn value_to_expression(v: Value) -> ParseResult { + Ok(Expression::Simple(v)) +} + +named!(simple_expression, + map_res!( + value, + value_to_expression + ) +); + +fn tuple_to_binary_expression( + tpl: (Position, BinaryExprType, Expression, Expression), +) -> ParseResult { + Ok(Expression::Binary(BinaryOpDef { + kind: tpl.1, + left: Box::new(tpl.2), + right: Box::new(tpl.3), + pos: Position::new(tpl.0.line as usize, tpl.0.column as usize), + })) +} + +/// do_binary_expr implements precedence based parsing where the more tightly bound parsers +/// are passed in as lowerrule parsers. We default to grouped_expression and simple_expression as +/// the most tightly bound expressions. +macro_rules! do_binary_expr { + ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr) => { + do_binary_expr!($i, $oprule!($($args)*), $typ, alt!(grouped_expression | simple_expression)) + }; + + ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident) => { + do_binary_expr!($i, $oprule!($($args)*), $typ, call!($lowerrule)) + }; + + ($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident!( $($lowerargs:tt)* )) => { + map_res!($i, + do_parse!( + pos: pos >> + left: $lowerrule!($($lowerargs)*) >> + $oprule!($($args)*) >> + right: $lowerrule!($($lowerargs)*) >> + (pos, $typ, left, right) + ), + tuple_to_binary_expression + ) + }; +} + +named!(math_expression, + alt!(sum_expression | product_expression) +); + +named!(sum_expression, + alt!(add_expression | sub_expression) +); + +// trace_macros!(true); +named!(add_expression, + do_binary_expr!(punct!("+"), BinaryExprType::Add, alt!(product_expression | simple_expression | grouped_expression)) +); +// trace_macros!(false); + +named!(sub_expression, + do_binary_expr!(punct!("-"), BinaryExprType::Sub, alt!(product_expression | simple_expression | grouped_expression)) +); + +named!(product_expression, + alt!(mul_expression | div_expression) +); + +named!(mul_expression, + do_binary_expr!(punct!("*"), BinaryExprType::Mul) +); + +named!(div_expression, + do_binary_expr!(punct!("/"), BinaryExprType::Div) +); + +// TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence? +fn tuple_to_compare_expression( + tpl: (Position, CompareType, Expression, Expression), +) -> ParseResult { + Ok(Expression::Compare(ComparisonDef { + kind: tpl.1, + left: Box::new(tpl.2), + right: Box::new(tpl.3), + pos: Position::new(tpl.0.line as usize, tpl.0.column as usize), + })) +} + +macro_rules! do_compare_expr { + ($i:expr, $subrule:ident!( $($args:tt)* ), $typ:expr) => { + map_res!($i, + do_parse!( + pos: pos >> + left: alt!(simple_expression | grouped_expression | math_expression) >> + $subrule!($($args)*) >> + right: expression >> + (pos, $typ, left, right) + ), + tuple_to_compare_expression + ) + }; +} + +named!(eqeq_expression, + do_compare_expr!(punct!("=="), CompareType::Equal) +); + +named!(not_eqeq_expression, + do_compare_expr!(punct!("!="), CompareType::NotEqual) +); + +named!(lt_eqeq_expression, + do_compare_expr!(punct!("<="), CompareType::LTEqual) +); + +named!(gt_eqeq_expression, + do_compare_expr!(punct!(">="), CompareType::GTEqual) +); + +named!(gt_expression, + do_compare_expr!(punct!(">"), CompareType::GT) +); + +named!(lt_expression, + do_compare_expr!(punct!("<"), CompareType::LT) +); + +fn expression_to_grouped_expression(e: Expression) -> ParseResult { + Ok(Expression::Grouped(Box::new(e))) +} + +named!(grouped_expression, + map_res!( + preceded!(punct!("("), terminated!(expression, punct!(")"))), + expression_to_grouped_expression + ) +); + +fn symbol_or_expression(input: TokenIter) -> NomResult { + let sym = do_parse!(input, sym: symbol >> (sym)); + + match sym { + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + IResult::Error(_) => { + // TODO(jwall): Still missing some. But we need to avoid recursion + return grouped_expression(input); + } + IResult::Done(rest, val) => { + return IResult::Done(rest, Expression::Simple(val)); + } + } +} + +fn selector_list(input: TokenIter) -> NomResult { + let (rest, head) = match symbol_or_expression(input) { + IResult::Done(rest, val) => (rest, val), + IResult::Error(e) => { + return IResult::Error(e); + } + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + }; + + let (rest, is_dot) = match punct!(rest, ".") { + IResult::Done(rest, tok) => (rest, Some(tok)), + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + IResult::Error(_) => (rest, None), + }; + + let (rest, list) = if is_dot.is_some() { + let (rest, list) = match separated_list!( + rest, + punct!("."), + alt!(match_type!(BAREWORD) | match_type!(DIGIT)) + ) { + IResult::Done(rest, val) => (rest, val), + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + IResult::Error(e) => { + return IResult::Error(e); + } + }; + + if list.is_empty() { + return IResult::Error(nom::ErrorKind::Custom(error::Error::new( + "(.) with no selector fields after".to_string(), + error::ErrorType::IncompleteParsing, + is_dot.unwrap().pos, + ))); + } else { + (rest, Some(list)) + } + } else { + (rest, None) + }; + + let sel_list = SelectorList { + head: Box::new(head), + tail: list, + }; + + return IResult::Done(rest, sel_list); +} + +fn tuple_to_copy(t: (SelectorDef, FieldList)) -> ParseResult { + let pos = t.0.pos.clone(); + Ok(Expression::Copy(CopyDef { + selector: t.0, + fields: t.1, + pos: pos, + })) +} + +named!(copy_expression, + map_res!( + do_parse!( + pos: pos >> + selector: selector_list >> + punct!("{") >> + fields: field_list >> + punct!("}") >> + (SelectorDef::new(selector, pos.line, pos.column as usize), fields) + ), + tuple_to_copy + ) +); + +fn tuple_to_macro(mut t: (Position, Vec, Value)) -> ParseResult { + match t.2 { + Value::Tuple(v) => Ok(Expression::Macro(MacroDef { + argdefs: t.1 + .drain(0..) + .map(|s| Positioned { + pos: s.pos().clone(), + val: s.to_string(), + }) + .collect(), + fields: v.val, + pos: t.0, + })), + // TODO(jwall): Show a better version of the unexpected parsed value. + val => Err(error::Error::new( + format!("Expected Tuple Got {:?}", val), + error::ErrorType::UnexpectedToken, + t.0, + )), + } +} + +named!(arglist, error::Error>, separated_list!(punct!(","), symbol)); + +named!(macro_expression, + map_res!( + do_parse!( + pos: pos >> + word!("macro") >> + punct!("(") >> + arglist: arglist >> + punct!(")") >> + punct!("=>") >> + map: tuple >> + (pos, arglist, map) + ), + tuple_to_macro + ) +); + +fn tuple_to_select(t: (Position, Expression, Expression, Value)) -> ParseResult { + match t.3 { + Value::Tuple(v) => Ok(Expression::Select(SelectDef { + val: Box::new(t.1), + default: Box::new(t.2), + tuple: v.val, + pos: t.0, + })), + val => Err(error::Error::new( + format!("Expected Tuple Got {:?}", val), + error::ErrorType::UnexpectedToken, + t.0, + )), + } +} + +named!(select_expression, + map_res!( + do_parse!( + start: word!("select") >> + val: terminated!(expression, punct!(",")) >> + default: terminated!(expression, punct!(",")) >> + map: tuple >> + (start.pos.clone(), val, default, map) + ), + tuple_to_select + ) +); + +fn tuple_to_format(t: (Token, Vec)) -> ParseResult { + Ok(Expression::Format(FormatDef { + template: t.0.fragment.to_string(), + args: t.1, + pos: t.0.pos, + })) +} + +named!(format_expression, + map_res!( + do_parse!( + tmpl: match_type!(STR) >> + punct!("%") >> + punct!("(") >> + args: separated_list!(punct!(","), expression) >> + punct!(")") >> + (tmpl, args) + ), + tuple_to_format + ) +); + +fn tuple_to_call(t: (Position, Value, Vec)) -> ParseResult { + if let Value::Selector(def) = t.1 { + Ok(Expression::Call(CallDef { + macroref: def, + arglist: t.2, + pos: Position::new(t.0.line as usize, t.0.column as usize), + })) + } else { + Err(error::Error::new( + format!("Expected Selector Got {:?}", t.0), + error::ErrorType::UnexpectedToken, + Position::new(t.0.line as usize, t.0.column as usize), + )) + } +} + +fn vec_to_selector_value(t: (Position, SelectorList)) -> ParseResult { + Ok(Value::Selector(SelectorDef::new( + t.1, + t.0.line as usize, + t.0.column as usize, + ))) +} + +named!(selector_value, + map_res!( + do_parse!( + sl: selector_list >> + (sl.head.pos().clone(), sl) + ), + vec_to_selector_value + ) +); + +named!(call_expression, + map_res!( + do_parse!( + macroname: selector_value >> + punct!("(") >> + args: separated_list!(punct!(","), expression) >> + punct!(")") >> + (macroname.pos().clone(), macroname, args) + ), + tuple_to_call + ) +); + +fn symbol_or_list(input: TokenIter) -> NomResult { + let sym = do_parse!(input, sym: symbol >> (sym)); + + match sym { + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + IResult::Error(_) => { + // TODO(jwall): Still missing some. But we need to avoid recursion + match list_value(input) { + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + } + IResult::Error(e) => { + return IResult::Error(e); + } + IResult::Done(i, val) => { + return IResult::Done(i, val); + } + } + } + IResult::Done(rest, val) => { + return IResult::Done(rest, val); + } + } +} + +fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult { + let pos = tpl.0; + let t = if &tpl.1.fragment == "map" { + ListOpType::Map + } else if &tpl.1.fragment == "filter" { + ListOpType::Filter + } else { + return Err(error::Error::new( + format!( + "Expected one of 'map' or 'filter' but got '{}'", + tpl.1.fragment + ), + error::ErrorType::UnexpectedToken, + pos, + )); + }; + let macroname = tpl.2; + let list = tpl.3; + if let Value::Selector(mut def) = macroname { + // First of all we need to assert that this is a selector of at least + // two sections. + let fieldname: String = match &mut def.sel.tail { + &mut None => { + return Err(error::Error::new( + format!("Missing a result field for the macro"), + error::ErrorType::IncompleteParsing, + pos, + )); + } + &mut Some(ref mut tl) => { + if tl.len() < 1 { + return Err(error::Error::new( + format!("Missing a result field for the macro"), + error::ErrorType::IncompleteParsing, + def.pos.clone(), + )); + } + let fname = tl.pop(); + fname.unwrap().fragment + } + }; + if let Value::List(ldef) = list { + return Ok(Expression::ListOp(ListOpDef { + typ: t, + mac: def, + field: fieldname, + target: ldef, + pos: pos, + })); + } + // TODO(jwall): We should print a pretter message than debug formatting here. + return Err(error::Error::new( + format!("Expected a list but got {:?}", list), + error::ErrorType::UnexpectedToken, + pos, + )); + } + return Err(error::Error::new( + format!("Expected a selector but got {:?}", macroname), + error::ErrorType::UnexpectedToken, + pos, + )); +} + +named!(list_op_expression, + map_res!( + do_parse!( + pos: pos >> + optype: alt!(word!("map") | word!("filter")) >> + macroname: selector_value >> + list: symbol_or_list >> + (pos, optype, macroname, list) + ), + tuple_to_list_op + ) +); + +// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters +// a lot. We need to process alternatives in order of decreasing +// specificity. Unfortunately this means we are required to go in a +// decreasing size order which messes with alt!'s completion logic. To +// work around this we have to force Incomplete to be Error so that +// alt! will try the next in the series instead of aborting. +// +// *IMPORTANT* +// It also means this combinator is risky when used with partial +// inputs. So handle with care. +named!(expression, + do_parse!( + expr: alt!( + complete!(list_op_expression) | + complete!(math_expression) | + complete!(eqeq_expression) | + complete!(not_eqeq_expression) | + complete!(lt_eqeq_expression) | + complete!(gt_eqeq_expression) | + complete!(gt_expression) | + complete!(lt_expression) | + complete!(macro_expression) | + complete!(format_expression) | + complete!(select_expression) | + complete!(call_expression) | + complete!(copy_expression) | + complete!(grouped_expression) | + simple_expression + ) >> + (expr) + ) +); + +fn expression_to_statement(v: Expression) -> ParseResult { + Ok(Statement::Expression(v)) +} + +named!(expression_statement, + map_res!( + terminated!(expression, punct!(";")), + expression_to_statement + ) +); + +fn tuple_to_let(t: (Token, Expression)) -> ParseResult { + Ok(Statement::Let(LetDef { + name: t.0, + value: t.1, + })) +} + +named!(let_statement, + map_res!( + do_parse!( + word!("let") >> + name: match_type!(BAREWORD) >> + punct!("=") >> + val: expression >> + punct!(";") >> + (name, val) + ), + tuple_to_let + ) +); + +fn tuple_to_import(t: (Token, Token)) -> ParseResult { + Ok(Statement::Import(ImportDef { + path: t.0, + name: t.1, + })) +} + +named!(import_statement, + map_res!( + do_parse!( + word!("import") >> + path: match_type!(STR) >> + word!("as") >> + name: match_type!(BAREWORD) >> + punct!(";") >> + (path, name) + ), + tuple_to_import + ) +); + +named!(statement, + do_parse!( + stmt: alt_complete!( + import_statement | + let_statement | + expression_statement + ) >> + (stmt) + ) +); + +/// Parses a LocatedSpan into a list of Statements or an error::Error. +pub fn parse(input: LocatedSpan<&str>) -> Result, error::Error> { + match tokenize(input) { + Ok(tokenized) => { + let mut out = Vec::new(); + let mut i_ = TokenIter { + source: tokenized.as_slice(), + }; + loop { + let i = i_.clone(); + if i[0].typ == TokenType::END { + break; + } + match statement(i) { + IResult::Error(nom::ErrorKind::Custom(e)) => { + return Err(e); + } + IResult::Error(e) => { + return Err(error::Error::new_with_errorkind( + format!("Statement Parse error: {:?} current token: {:?}", e, i_[0]), + error::ErrorType::ParseError, + Position { + line: i_[0].pos.line, + column: i_[0].pos.column, + }, + e, + )); + } + IResult::Incomplete(ei) => { + return Err(error::Error::new( + format!("Unexpected end of parsing input: {:?}", ei), + error::ErrorType::IncompleteParsing, + Position { + line: i_[0].pos.line, + column: i_[0].pos.column, + }, + )); + } + IResult::Done(rest, stmt) => { + out.push(stmt); + i_ = rest; + if i_.input_len() == 0 { + break; + } + } + } + } + return Ok(out); + } + Err(e) => { + return Err(error::Error::new( + format!("Tokenization Error {:?}", e.1), + error::ErrorType::ParseError, + e.0, + )); + } + } +} + +#[cfg(test)] +mod test; diff --git a/src/parse/test.rs b/src/parse/test.rs new file mode 100644 index 0000000..6a99fc2 --- /dev/null +++ b/src/parse/test.rs @@ -0,0 +1,1099 @@ +use super::*; +use tokenizer::{tokenize, TokenIter}; + +use nom::IResult; +use nom_locate::LocatedSpan; + +macro_rules! assert_parse { + ($parsemac:ident($i:expr), $out:expr) => { + assert_parse!($i, $parsemac, $out) + }; + ($i:expr, $f:expr, $out:expr) => {{ + let input = LocatedSpan::new($i); + match tokenize(input) { + Err(e) => assert!(false, format!("Tokenizer Error: {:?}", e)), + Ok(val) => match $f(TokenIter { + source: val.as_slice(), + }) { + IResult::Done(_, result) => assert_eq!(result, $out), + other => assert!(false, format!("Expected Done got {:?}", other)), + }, + } + };}; +} + +macro_rules! assert_error { + ($parsemac:ident($i:expr)) => { + assert_error!($i, $parsemac) + }; + ($i:expr, $f:expr) => {{ + let input = LocatedSpan::new($i); + match tokenize(input) { + Err(_) => assert!(true), + Ok(val) => { + let result = $f(TokenIter { + source: val.as_slice(), + }); + assert!(result.is_err(), format!("Not an error: {:?}", result)) + } + } + }}; +} + +#[test] +fn test_null_parsing() { + 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] +fn test_symbol_parsing() { + assert_parse!( + symbol("foo"), + Value::Symbol(value_node!("foo".to_string(), 1, 1)) + ); + assert_parse!( + symbol("foo-bar"), + Value::Symbol(value_node!("foo-bar".to_string(), 1, 1)) + ); + assert_parse!( + symbol("foo_bar"), + Value::Symbol(value_node!("foo_bar".to_string(), 1, 1)) + ); +} + +#[test] +fn test_selector_parsing() { + assert_error!(selector_value("foo.")); + assert_parse!( + selector_value("foo.bar "), + Value::Selector(make_selector!(make_expr!("foo".to_string(), 1, 1) => [ + make_tok!("bar", 1, 5)] => + 1, 1)) + ); + assert_parse!( + selector_value("foo.0 "), + Value::Selector(make_selector!(make_expr!("foo".to_string(), 1, 1) => [ + make_tok!(DIGIT => "0", 1, 5)] => + 1, 1)) + ); + assert_parse!( + selector_value("foo.bar;"), + Value::Selector(make_selector!(make_expr!("foo", 1, 1) => + [ + make_tok!("bar", 1, 5) + ] => + 1, 1)) + ); + assert_parse!( + selector_value("({foo=1}).foo "), + Value::Selector( + make_selector!(Expression::Grouped(Box::new(Expression::Simple( + Value::Tuple(value_node!( + vec![(make_tok!("foo", 1, 3), Expression::Simple(Value::Int(Positioned::new(1, 1, 7))))], + 1, 3)) + ))) => [ make_tok!("foo", 1, 11) ] => 1, 2) + ) + ); +} + +#[test] +fn test_statement_parse() { + let stmt = "import \"foo\" as foo;"; + assert_parse!( + statement(stmt), + Statement::Import(ImportDef { + path: make_tok!(QUOT => "foo", 1,8), + name: make_tok!("foo", 1, 17), + }) + ); + + assert_error!(import_statement("import \"foo\"")); + + assert_parse!( + statement("let foo = 1.0 ;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), + }) + ); + + assert_parse!( + statement("let foo = 1 + 1 * 2;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), + right: Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))), + pos: Position::new(1, 15), + })), + pos: Position::new(1, 11), + }), + }) + ); + + assert_parse!( + statement("let foo = (1 + 1) * 2;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + left: Box::new(Expression::Grouped(Box::new(Expression::Binary( + BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 12)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 16)))), + pos: Position::new(1, 12), + }, + )))), + right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 21)))), + pos: Position::new(1, 11), + }), + }) + ); + + assert_parse!( + statement("let foo = 1 * 1 + 2;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 15)))), + pos: Position::new(1, 11), + })), + right: Box::new(Expression::Simple(Value::Int(value_node!(2, 1, 19)))), + pos: Position::new(1, 11), + }), + }) + ); + + assert_parse!( + statement("// comment\nlet foo = 1.0 ;"), + Statement::Let(LetDef { + name: make_tok!("foo", 2, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 2, 11))), + }) + ); + + assert_parse!( + statement("1.0;"), + Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) + ); +} + +#[test] +fn test_import_statement_parse() { + assert_error!(import_statement("import")); + assert_error!(import_statement("import \"foo\"")); + assert_error!(import_statement("import \"foo\" as")); + assert_error!(import_statement("import \"foo\" as foo")); + + let import_stmt = "import \"foo\" as foo;"; + assert_parse!( + import_statement(import_stmt), + Statement::Import(ImportDef { + path: make_tok!(QUOT => "foo", 1, 8), + name: make_tok!("foo", 1, 17), + }) + ); +} + +#[test] +fn test_let_statement_parse() { + assert_error!(let_statement("foo")); + assert_error!(let_statement("let \"foo\"")); + assert_error!(let_statement("let 1")); + assert_error!(let_statement("let")); + assert_error!(let_statement("let foo")); + assert_error!(let_statement("let foo =")); + assert_error!(let_statement("let foo = ")); + assert_error!(let_statement("let foo = 1")); + + assert_parse!( + let_statement("let foo = 1.0 ;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), + }) + ); + + assert_parse!( + let_statement("let foo = // comment\n1.0 ;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 2, 1))), + }) + ); + + assert_parse!( + let_statement("let foo = 1.0 // comment\n;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 1, 11))), + }) + ); + + assert_parse!( + let_statement("let foo= 1.0;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 1, 10))), + }) + ); + + assert_parse!( + let_statement("let foo =1.0;"), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 5), + value: Expression::Simple(Value::Float(value_node!(1.0, 1, 10))), + }) + ); +} + +#[test] +fn test_expression_statement_parse() { + assert_error!(expression_statement("foo")); + assert_parse!( + expression_statement("1.0;"), + Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) + ); + assert_parse!( + expression_statement("1.0 ;"), + Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 1)))) + ); + assert_parse!( + expression_statement(" 1.0;"), + Statement::Expression(Expression::Simple(Value::Float(value_node!(1.0, 1, 2)))) + ); + assert_parse!( + expression_statement("foo;"), + Statement::Expression(Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 1), + 1, + 1 + )))) + ); + assert_parse!( + expression_statement("foo ;"), + Statement::Expression(Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 2), + 1, + 1 + )))) + ); + assert_parse!( + expression_statement(" foo;"), + Statement::Expression(Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 2), + 1, + 2 + )))) + ); + assert_parse!( + expression_statement("\"foo\";"), + Statement::Expression(Expression::Simple(Value::String(value_node!( + "foo".to_string(), + 1, + 1 + )))) + ); + assert_parse!( + expression_statement("\"foo\" ;"), + Statement::Expression(Expression::Simple(Value::String(value_node!( + "foo".to_string(), + 1, + 1 + )))) + ); + assert_parse!( + expression_statement(" \"foo\";"), + Statement::Expression(Expression::Simple(Value::String(value_node!( + "foo".to_string(), + 1, + 2 + )))) + ); +} + +#[test] +fn test_expression_parse() { + assert_parse!( + expression("NULL"), + Expression::Simple(Value::Empty(Position::new(1, 1))) + ); + assert_parse!( + expression("\"foo\""), + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 1))) + ); + assert_parse!( + expression("1"), + Expression::Simple(Value::Int(value_node!(1, 1, 1))) + ); + assert_parse!( + expression("foo "), + Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 1), + 1, + 1 + ))) + ); + assert_parse!( + expression("foo.bar "), + Expression::Simple(Value::Selector(make_selector!(make_expr!("foo", 1, 1) => + [ make_tok!("bar", 1, 5) ] => + 1, 1))) + ); + assert_parse!( + math_expression("1 + 1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(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::Sub, + left: Box::new(Expression::Simple(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::Mul, + left: Box::new(Expression::Simple(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::Div, + left: Box::new(Expression::Simple(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::Grouped(Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), + pos: Position::new(1, 2), + }))) + ); + assert_parse!( + expression("1 / 1 + 1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 5)))), + pos: Position::new(1, 1), + })), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 9)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("(1 + 1) * 1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + left: Box::new(Expression::Grouped(Box::new(Expression::Binary( + BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), + pos: Position::new(1, 2), + } + )))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 11)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("1 > 1"), + Expression::Compare(ComparisonDef { + kind: CompareType::GT, + left: Box::new(Expression::Simple(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::Compare(ComparisonDef { + kind: CompareType::LT, + left: Box::new(Expression::Simple(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::Compare(ComparisonDef { + kind: CompareType::LTEqual, + left: Box::new(Expression::Simple(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::Compare(ComparisonDef { + kind: CompareType::GTEqual, + left: Box::new(Expression::Simple(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::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("1-1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Sub, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("1*1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Mul, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("1/1"), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Div, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 3)))), + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("macro (arg1, arg2) => { foo = arg1 }"), + Expression::Macro(MacroDef { + argdefs: vec![ + value_node!("arg1".to_string(), 1, 8), + value_node!("arg2".to_string(), 1, 14), + ], + fields: vec![ + ( + make_tok!("foo", 1, 25), + Expression::Simple(Value::Selector(make_selector!( + make_expr!("arg1", 1, 31), + 1, + 31 + ))), + ), + ], + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("select foo, 1, { foo = 2 }"), + Expression::Select(SelectDef { + val: Box::new(Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 8), + 1, + 8 + )))), + default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 13)))), + tuple: vec![ + ( + make_tok!("foo", 1, 18), + Expression::Simple(Value::Int(value_node!(2, 1, 24))), + ), + ], + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("foo.bar (1, \"foo\")"), + Expression::Call(CallDef { + macroref: make_selector!(make_expr!("foo", 1, 1) => + [ make_tok!("bar", 1, 5) ] => + 1, 1), + arglist: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 10))), + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 13))), + ], + pos: Position::new(1, 1), + }) + ); + assert_parse!( + expression("(1 + 1)"), + Expression::Grouped(Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), + pos: Position::new(1, 2), // FIXME(jwall): grouped expressions appear to be getting positioned wrong + }))) + ); + assert_parse!( + expression("[1, 1]"), + Expression::Simple(Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 2))), + Expression::Simple(Value::Int(value_node!(1, 1, 5))), + ], + pos: Position::new(1, 1), + })) + ); +} + +#[test] +fn test_format_parse() { + assert_error!(format_expression("\"foo")); + assert_error!(format_expression("\"foo\"")); + assert_error!(format_expression("\"foo\" %")); + assert_error!(format_expression("\"foo\" % (, 2")); + + assert_parse!( + format_expression("\"foo @ @\" % (1, 2)"), + Expression::Format(FormatDef { + template: "foo @ @".to_string(), + args: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 14))), + Expression::Simple(Value::Int(value_node!(2, 1, 17))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + format_expression("\"foo @ @\"%(1, 2)"), + Expression::Format(FormatDef { + template: "foo @ @".to_string(), + args: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 12))), + Expression::Simple(Value::Int(value_node!(2, 1, 15))), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_call_parse() { + assert_error!(call_expression("foo")); + assert_error!(call_expression("foo (")); + assert_error!(call_expression("foo (1")); + assert_error!(call_expression("foo (1,")); + assert_error!(call_expression("foo (1,2")); + + assert_parse!( + call_expression("foo (1, \"foo\")"), + Expression::Call(CallDef { + macroref: make_selector!(make_expr!("foo", 1, 1), 1, 1), + arglist: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 6))), + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 9))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + call_expression("foo.bar (1, \"foo\")"), + Expression::Call(CallDef { + macroref: make_selector!(make_expr!("foo") => [ make_tok!("bar", 1, 5) ] => 1, 1), + arglist: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 10))), + Expression::Simple(Value::String(value_node!("foo".to_string(), 1, 13))), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_select_parse() { + assert_error!(select_expression("select")); + assert_error!(select_expression("select foo")); + assert_error!(select_expression("select foo, 1")); + assert_error!(select_expression("select foo, 1, {")); + + assert_parse!( + select_expression("select foo, 1, { foo = 2 }"), + Expression::Select(SelectDef { + val: Box::new(Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 8), + 1, + 8 + )))), + default: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 13)))), + tuple: vec![ + ( + make_tok!("foo", 1, 18), + Expression::Simple(Value::Int(value_node!(2, 1, 24))), + ), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_macro_expression_parsing() { + assert_error!(macro_expression("foo")); + assert_error!(macro_expression("macro \"foo\"")); + assert_error!(macro_expression("macro 1")); + assert_error!(macro_expression("macro")); + assert_error!(macro_expression("macro (")); + assert_error!(macro_expression("macro (arg")); + assert_error!(macro_expression("macro (arg, arg2")); + assert_error!(macro_expression("macro (arg1, arg2) =>")); + assert_error!(macro_expression("macro (arg1, arg2) => {")); + assert_error!(macro_expression("macro (arg1, arg2) => { foo")); + assert_error!(macro_expression("macro (arg1, arg2) => { foo =")); + + assert_parse!( + macro_expression("macro (arg1, arg2) => {foo=1,bar=2}"), + Expression::Macro(MacroDef { + argdefs: vec![ + value_node!("arg1".to_string(), 1, 8), + value_node!("arg2".to_string(), 1, 14), + ], + fields: vec![ + ( + make_tok!("foo", 1, 24), + Expression::Simple(Value::Int(value_node!(1, 1, 28))), + ), + ( + make_tok!("bar", 1, 30), + Expression::Simple(Value::Int(value_node!(2, 1, 34))), + ), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_copy_parse() { + assert_error!(copy_expression("{}")); + assert_error!(copy_expression("foo")); + assert_error!(copy_expression("foo{")); + + assert_parse!( + copy_expression("foo{}"), + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("foo", 1, 1), 1, 1), + fields: Vec::new(), + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + copy_expression("foo{bar=1}"), + Expression::Copy(CopyDef { + selector: make_selector!(make_expr!("foo", 1, 1), 1, 1), + fields: vec![ + ( + make_tok!("bar", 1, 5), + Expression::Simple(Value::Int(value_node!(1, 1, 9))), + ), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_grouped_expression_parse() { + assert_error!(grouped_expression("foo")); + assert_error!(grouped_expression("(foo")); + assert_parse!( + grouped_expression("(foo)"), + Expression::Grouped(Box::new(Expression::Simple(Value::Selector( + make_selector!(make_expr!("foo", 1, 2), 1, 2) + )))) + ); + assert_parse!( + grouped_expression("(1 + 1)"), + Expression::Grouped(Box::new(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 2)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 6)))), + pos: Position::new(1, 2), + }))) + ); +} + +#[test] +fn test_list_value_parse() { + assert_error!(list_value("foo")); + assert_error!(list_value("[foo")); + assert_error!(list_value("// commen\n[foo")); + + assert_parse!( + list_value("[foo]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Selector(make_selector!( + make_expr!("foo", 1, 2), + 1, + 2 + ))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + list_value("[1, 1]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 2))), + Expression::Simple(Value::Int(value_node!(1, 1, 5))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + list_value("// comment\n[1, 1]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 2, 2))), + Expression::Simple(Value::Int(value_node!(1, 2, 5))), + ], + pos: Position::new(2, 1), + }) + ); + + assert_parse!( + list_value("[// comment\n1, 1]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 2, 2))), + Expression::Simple(Value::Int(value_node!(1, 2, 5))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + list_value("[1, // comment\n1]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 2))), + Expression::Simple(Value::Int(value_node!(1, 2, 1))), + ], + pos: Position::new(1, 1), + }) + ); + + assert_parse!( + list_value("[1, 1 // comment\n]"), + Value::List(ListDef { + elems: vec![ + Expression::Simple(Value::Int(value_node!(1, 1, 2))), + Expression::Simple(Value::Int(value_node!(1, 1, 5))), + ], + pos: Position::new(1, 1), + }) + ); +} + +#[test] +fn test_tuple_parse() { + assert_error!(tuple("{")); + assert_error!(tuple("{ foo")); + assert_error!(tuple("{ foo =")); + assert_error!(tuple("{ foo = 1")); + assert_error!(tuple("{ foo = 1,")); + assert_error!(tuple("{ foo = 1, bar =")); + assert_error!(tuple("// comment\n{ foo = 1, bar =")); + + assert_parse!(tuple("{ }"), Value::Tuple(value_node!(vec![], 1, 1))); + + assert_parse!( + tuple("{ foo = 1 }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 1, 3), + Expression::Simple(Value::Int(value_node!(1, 1, 9))), + ), + ], + 1, + 1 + )) + ); + + assert_parse!( + tuple("// comment\n{ foo = 1 }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 2, 3), + Expression::Simple(Value::Int(value_node!(1, 2, 9))), + ), + ], + 1, + 1 + )) + ); + + assert_parse!( + tuple("{// comment\n foo = 1 }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 2, 2), + Expression::Simple(Value::Int(value_node!(1, 2, 8))), + ), + ], + 1, + 1 + )) + ); + + assert_parse!( + tuple("{ foo = 1// comment\n }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 1, 3), + Expression::Simple(Value::Int(value_node!(1, 1, 9))), + ), + ], + 1, + 1 + )) + ); + + assert_parse!( + tuple("{ foo = 1, bar = \"1\" }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 1, 3), + Expression::Simple(Value::Int(value_node!(1, 1, 9))), + ), + ( + make_tok!("bar", 1, 12), + Expression::Simple(Value::String(value_node!( + "1".to_string(), + Position::new(1, 18) + ))), + ), + ], + 1, + 1 + )) + ); + assert_parse!( + tuple("{ foo = 1, // comment\nbar = \"1\" }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 1, 3), + Expression::Simple(Value::Int(value_node!(1, 1, 9))), + ), + ( + make_tok!("bar", 2, 1), + Expression::Simple(Value::String(value_node!( + "1".to_string(), + Position::new(2, 7) + ))), + ), + ], + 1, + 1 + )) + ); + assert_parse!( + tuple("{ foo = 1, bar = {} }"), + Value::Tuple(value_node!( + vec![ + ( + make_tok!("foo", 1, 3), + Expression::Simple(Value::Int(value_node!(1, Position::new(1, 9)))), + ), + ( + make_tok!("bar", 1, 12), + Expression::Simple(Value::Tuple(value_node!(Vec::new(), Position::new(1, 17)))), + ), + ], + 1, + 1 + )) + ); +} + +#[test] +fn test_field_list_parse() { + let mut f_list = "foo = 1, quux = 2;"; + assert_parse!( + field_list(f_list), + vec![ + (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), + (make_tok!("quux", 1, 10), make_expr!(2 => int, 1, 17)), + ] + ); + + f_list = "foo = 1, // comment\nquux = 2;"; + assert_parse!( + field_list(f_list), + vec![ + (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), + (make_tok!("quux", 2, 1), make_expr!(2 => int, 2, 8)), + ] + ); + + f_list = "foo = 1,\n// comment\nquux = 2;"; + assert_parse!( + field_list(f_list), + vec![ + (make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)), + (make_tok!("quux", 3, 1), make_expr!(2 => int, 3, 8)), + ] + ); +} + +#[test] +fn test_field_value_parse() { + assert_error!(field_value("foo")); + assert_error!(field_value("// comment\nfoo")); + assert_error!(field_value("foo =")); + + assert_parse!( + field_value("foo = 1"), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Int(value_node!(1, 1, 7))) + ) + ); + assert_parse!( + field_value("foo = 1 // foo comment\n"), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Int(value_node!(1, 1, 7))) + ) + ); + assert_parse!( + field_value("foo // foo comment\n = 1"), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Int(value_node!(1, 2, 4))) + ) + ); + assert_parse!( + field_value("// foo comment\nfoo = 1"), + ( + make_tok!("foo", 2, 1), + Expression::Simple(Value::Int(value_node!(1, 2, 7))) + ) + ); + assert_parse!( + field_value("foo = \"1\""), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::String(value_node!("1".to_string(), 1, 7))) + ) + ); + assert_parse!( + field_value("foo = bar "), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Selector(make_selector!( + make_expr!("bar", 1, 7), + 1, + 7 + ))) + ) + ); + assert_parse!( + field_value("foo = bar.baz "), + ( + make_tok!("foo", 1, 1), + Expression::Simple(Value::Selector( + make_selector!(make_expr!("bar", 1, 7) => [ make_tok!("baz", 1, 11) ] => 1, 7), + )) + ) + ); +} + +#[test] +fn test_number_parsing() { + assert_error!(number(".")); + assert_error!(number(". ")); + assert_parse!(number("1.0"), Value::Float(value_node!(1.0, 1, 1))); + assert_parse!(number("1."), Value::Float(value_node!(1.0, 1, 1))); + assert_parse!(number("1"), Value::Int(value_node!(1, 1, 1))); + assert_parse!(number(".1"), Value::Float(value_node!(0.1, 1, 1))); +} + +#[test] +fn test_parse() { + let bad_input = LocatedSpan::new("import mylib as lib;"); + let bad_result = parse(bad_input); + assert!(bad_result.is_err()); + + // Valid parsing tree + let input = LocatedSpan::new("import \"mylib\" as lib;let foo = 1;1+1;"); + let result = parse(input); + assert!(result.is_ok(), format!("Expected Ok, Got {:?}", result)); + let tpl = result.unwrap(); + assert_eq!( + tpl, + vec![ + Statement::Import(ImportDef { + path: make_tok!(QUOT => "mylib", 1, 8), + name: make_tok!("lib", 1, 19), + }), + Statement::Let(LetDef { + name: make_tok!("foo", 1, 27), + value: Expression::Simple(Value::Int(value_node!(1, 1, 33))), + }), + Statement::Expression(Expression::Binary(BinaryOpDef { + kind: BinaryExprType::Add, + left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 35)))), + right: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 37)))), + pos: Position::new(1, 35), + })), + ] + ); +} diff --git a/src/tokenizer.rs b/src/tokenizer/mod.rs similarity index 69% rename from src/tokenizer.rs rename to src/tokenizer/mod.rs index 5b33baf..ae0a609 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. //! The tokenization stage of the ucg compiler. -use ast::tree::*; +use ast::*; use error; use nom; use nom::{InputIter, InputLength, Slice}; @@ -659,263 +659,4 @@ impl<'a> InputIter for TokenIter<'a> { } #[cfg(test)] -mod tokenizer_test { - use super::*; - use nom; - use nom_locate::LocatedSpan; - - #[test] - fn test_empty_token() { - let result = emptytok(LocatedSpan::new("NULL")); - assert!(result.is_done(), format!("result {:?} is not done", result)); - if let nom::IResult::Done(_, tok) = result { - assert_eq!(tok.fragment, "NULL"); - assert_eq!(tok.typ, TokenType::EMPTY); - } - } - - #[test] - fn test_escape_quoted() { - let result = escapequoted(LocatedSpan::new("foo \\\"bar\"")); - assert!(result.is_done(), format!("result {:?} is not ok", result)); - if let nom::IResult::Done(rest, frag) = result { - assert_eq!(frag, "foo \"bar"); - assert_eq!(rest.fragment, "\""); - } - } - - #[test] - fn test_string_with_escaping() { - let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\"")); - assert!(result.is_done(), format!("result {:?} is not ok", result)); - if let nom::IResult::Done(_, tok) = result { - assert_eq!(tok.fragment, "foo \\ \"bar".to_string()); - } - } - - #[test] - fn test_tokenize_bareword_with_dash() { - let result = tokenize(LocatedSpan::new("foo-bar ")); - assert!(result.is_ok(), format!("result {:?} is not ok", result)); - if let Ok(toks) = result { - assert_eq!(toks.len(), 2); - assert_eq!(toks[0].fragment, "foo-bar"); - } - } - - macro_rules! assert_token { - ($input:expr, $typ:expr, $msg:expr) => { - let result = token(LocatedSpan::new($input)); - assert!( - result.is_done(), - format!("result {:?} is not a {}", result, $msg) - ); - if let nom::IResult::Done(_, tok) = result { - assert_eq!(tok.fragment, $input); - 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] - fn test_tokenize_one_of_each() { - let result = tokenize(LocatedSpan::new( - "let import macro select as => [ ] { } ; = % / * \ - + - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=", - )); - assert!(result.is_ok(), format!("result {:?} is not ok", result)); - let v = result.unwrap(); - for (i, t) in v.iter().enumerate() { - println!("{}: {:?}", i, t); - } - assert_eq!(v.len(), 35); - assert_eq!(v[34].typ, TokenType::END); - } - - #[test] - fn test_parse_has_end() { - let result = tokenize(LocatedSpan::new("foo")); - assert!(result.is_ok()); - let v = result.unwrap(); - assert_eq!(v.len(), 2); - assert_eq!(v[1].typ, TokenType::END); - } - - #[test] - fn test_parse_comment() { - assert!(comment(LocatedSpan::new("// comment\n")).is_done()); - assert!(comment(LocatedSpan::new("// comment")).is_done()); - assert_eq!( - comment(LocatedSpan::new("// comment\n")), - nom::IResult::Done( - LocatedSpan { - fragment: "", - offset: 11, - line: 2, - }, - Token { - typ: TokenType::COMMENT, - fragment: " comment".to_string(), - pos: Position { line: 1, column: 1 }, - } - ) - ); - assert!(comment(LocatedSpan::new("// comment\r\n")).is_done()); - assert_eq!( - comment(LocatedSpan::new("// comment\r\n")), - nom::IResult::Done( - LocatedSpan { - fragment: "", - offset: 12, - line: 2, - }, - Token { - typ: TokenType::COMMENT, - fragment: " comment".to_string(), - pos: Position { column: 1, line: 1 }, - } - ) - ); - assert!(comment(LocatedSpan::new("// comment\r\n ")).is_done()); - assert_eq!( - comment(LocatedSpan::new("// comment\r\n ")), - nom::IResult::Done( - LocatedSpan { - fragment: " ", - offset: 12, - line: 2, - }, - Token { - typ: TokenType::COMMENT, - fragment: " comment".to_string(), - pos: Position { column: 1, line: 1 }, - } - ) - ); - assert!(comment(LocatedSpan::new("// comment")).is_done()); - } - - #[test] - fn test_match_word() { - let input = vec![ - Token { - fragment: "foo".to_string(), - typ: TokenType::BAREWORD, - pos: Position { line: 1, column: 1 }, - }, - ]; - let result = word!( - TokenIter { - source: input.as_slice(), - }, - "foo" - ); - match result { - nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), - res => assert!(false, format!("Fail: {:?}", res)), - } - } - - #[test] - fn test_match_word_empty_input() { - let input = vec![ - Token { - fragment: "".to_string(), - typ: TokenType::END, - pos: Position { line: 1, column: 1 }, - }, - ]; - let result = word!( - TokenIter { - source: input.as_slice(), - }, - "foo" - ); - match result { - nom::IResult::Done(_, _) => assert!(false, "Should have been an error but was Done"), - nom::IResult::Incomplete(_) => { - assert!(false, "Should have been an error but was Incomplete") - } - nom::IResult::Error(_) => { - // noop - } - } - } - - #[test] - fn test_match_punct() { - let input = vec![ - Token { - fragment: "!".to_string(), - typ: TokenType::PUNCT, - pos: Position { line: 1, column: 1 }, - }, - ]; - let result = punct!( - TokenIter { - source: input.as_slice(), - }, - "!" - ); - match result { - nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), - res => assert!(false, format!("Fail: {:?}", res)), - } - } - - #[test] - fn test_match_type() { - let input = vec![ - Token { - fragment: "foo".to_string(), - typ: TokenType::BAREWORD, - pos: Position { line: 1, column: 1 }, - }, - ]; - let result = match_type!( - TokenIter { - source: input.as_slice(), - }, - BAREWORD - ); - match result { - nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), - res => assert!(false, format!("Fail: {:?}", res)), - } - } -} +mod test; diff --git a/src/tokenizer/test.rs b/src/tokenizer/test.rs new file mode 100644 index 0000000..3c0183c --- /dev/null +++ b/src/tokenizer/test.rs @@ -0,0 +1,258 @@ +use super::*; +use nom; +use nom_locate::LocatedSpan; + +#[test] +fn test_empty_token() { + let result = emptytok(LocatedSpan::new("NULL")); + assert!(result.is_done(), format!("result {:?} is not done", result)); + if let nom::IResult::Done(_, tok) = result { + assert_eq!(tok.fragment, "NULL"); + assert_eq!(tok.typ, TokenType::EMPTY); + } +} + +#[test] +fn test_escape_quoted() { + let result = escapequoted(LocatedSpan::new("foo \\\"bar\"")); + assert!(result.is_done(), format!("result {:?} is not ok", result)); + if let nom::IResult::Done(rest, frag) = result { + assert_eq!(frag, "foo \"bar"); + assert_eq!(rest.fragment, "\""); + } +} + +#[test] +fn test_string_with_escaping() { + let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\"")); + assert!(result.is_done(), format!("result {:?} is not ok", result)); + if let nom::IResult::Done(_, tok) = result { + assert_eq!(tok.fragment, "foo \\ \"bar".to_string()); + } +} + +#[test] +fn test_tokenize_bareword_with_dash() { + let result = tokenize(LocatedSpan::new("foo-bar ")); + assert!(result.is_ok(), format!("result {:?} is not ok", result)); + if let Ok(toks) = result { + assert_eq!(toks.len(), 2); + assert_eq!(toks[0].fragment, "foo-bar"); + } +} + +macro_rules! assert_token { + ($input:expr, $typ:expr, $msg:expr) => { + let result = token(LocatedSpan::new($input)); + assert!( + result.is_done(), + format!("result {:?} is not a {}", result, $msg) + ); + if let nom::IResult::Done(_, tok) = result { + assert_eq!(tok.fragment, $input); + 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] +fn test_tokenize_one_of_each() { + let result = tokenize(LocatedSpan::new( + "let import macro select as => [ ] { } ; = % / * \ + + - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=", + )); + assert!(result.is_ok(), format!("result {:?} is not ok", result)); + let v = result.unwrap(); + for (i, t) in v.iter().enumerate() { + println!("{}: {:?}", i, t); + } + assert_eq!(v.len(), 35); + assert_eq!(v[34].typ, TokenType::END); +} + +#[test] +fn test_parse_has_end() { + let result = tokenize(LocatedSpan::new("foo")); + assert!(result.is_ok()); + let v = result.unwrap(); + assert_eq!(v.len(), 2); + assert_eq!(v[1].typ, TokenType::END); +} + +#[test] +fn test_parse_comment() { + assert!(comment(LocatedSpan::new("// comment\n")).is_done()); + assert!(comment(LocatedSpan::new("// comment")).is_done()); + assert_eq!( + comment(LocatedSpan::new("// comment\n")), + nom::IResult::Done( + LocatedSpan { + fragment: "", + offset: 11, + line: 2, + }, + Token { + typ: TokenType::COMMENT, + fragment: " comment".to_string(), + pos: Position { line: 1, column: 1 }, + } + ) + ); + assert!(comment(LocatedSpan::new("// comment\r\n")).is_done()); + assert_eq!( + comment(LocatedSpan::new("// comment\r\n")), + nom::IResult::Done( + LocatedSpan { + fragment: "", + offset: 12, + line: 2, + }, + Token { + typ: TokenType::COMMENT, + fragment: " comment".to_string(), + pos: Position { column: 1, line: 1 }, + } + ) + ); + assert!(comment(LocatedSpan::new("// comment\r\n ")).is_done()); + assert_eq!( + comment(LocatedSpan::new("// comment\r\n ")), + nom::IResult::Done( + LocatedSpan { + fragment: " ", + offset: 12, + line: 2, + }, + Token { + typ: TokenType::COMMENT, + fragment: " comment".to_string(), + pos: Position { column: 1, line: 1 }, + } + ) + ); + assert!(comment(LocatedSpan::new("// comment")).is_done()); +} + +#[test] +fn test_match_word() { + let input = vec![ + Token { + fragment: "foo".to_string(), + typ: TokenType::BAREWORD, + pos: Position { line: 1, column: 1 }, + }, + ]; + let result = word!( + TokenIter { + source: input.as_slice(), + }, + "foo" + ); + match result { + nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), + res => assert!(false, format!("Fail: {:?}", res)), + } +} + +#[test] +fn test_match_word_empty_input() { + let input = vec![ + Token { + fragment: "".to_string(), + typ: TokenType::END, + pos: Position { line: 1, column: 1 }, + }, + ]; + let result = word!( + TokenIter { + source: input.as_slice(), + }, + "foo" + ); + match result { + nom::IResult::Done(_, _) => assert!(false, "Should have been an error but was Done"), + nom::IResult::Incomplete(_) => { + assert!(false, "Should have been an error but was Incomplete") + } + nom::IResult::Error(_) => { + // noop + } + } +} + +#[test] +fn test_match_punct() { + let input = vec![ + Token { + fragment: "!".to_string(), + typ: TokenType::PUNCT, + pos: Position { line: 1, column: 1 }, + }, + ]; + let result = punct!( + TokenIter { + source: input.as_slice(), + }, + "!" + ); + match result { + nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), + res => assert!(false, format!("Fail: {:?}", res)), + } +} + +#[test] +fn test_match_type() { + let input = vec![ + Token { + fragment: "foo".to_string(), + typ: TokenType::BAREWORD, + pos: Position { line: 1, column: 1 }, + }, + ]; + let result = match_type!( + TokenIter { + source: input.as_slice(), + }, + BAREWORD + ); + match result { + nom::IResult::Done(_, tok) => assert_eq!(tok, input[0]), + res => assert!(false, format!("Fail: {:?}", res)), + } +}