2017-07-11 20:29:54 -05:00
|
|
|
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2017-10-02 21:32:06 -05:00
|
|
|
use std::str::FromStr;
|
|
|
|
use std::error::Error;
|
2017-11-06 21:06:30 -06:00
|
|
|
use std::borrow::Borrow;
|
2017-10-02 21:32:06 -05:00
|
|
|
|
|
|
|
use ast::*;
|
2017-11-05 15:26:52 -06:00
|
|
|
use tokenizer::*;
|
2017-09-23 11:19:45 -05:00
|
|
|
|
2017-06-07 20:14:40 -05:00
|
|
|
quick_error! {
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
|
|
pub enum ParseError {
|
|
|
|
UnexpectedToken(expected: String, actual: String) {
|
|
|
|
description("Unexpected Token")
|
|
|
|
display("Unexpected Token Expected {} Got {}", expected, actual)
|
|
|
|
}
|
2017-11-05 15:26:52 -06:00
|
|
|
EmptyExpression(msg: String ) {
|
|
|
|
description("EmptyExpression")
|
|
|
|
display("Unexpected EmptyExpression {}", msg)
|
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-23 11:19:45 -05:00
|
|
|
|
2017-08-08 21:02:54 -05:00
|
|
|
// TODO(jwall): Error Reporting with Line and Column information.
|
|
|
|
|
2017-08-12 14:48:28 -05:00
|
|
|
type ParseResult<O> = Result<O, Box<Error>>;
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn symbol_to_value(s: Token) -> ParseResult<Value> {
|
2017-11-06 21:06:30 -06:00
|
|
|
Ok(Value::Symbol(value_node!(s.fragment.to_string(), s.pos)))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// symbol is a bare unquoted field.
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(symbol( Span ) -> Value, map_res!(barewordtok, symbol_to_value));
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn str_to_value(s: Token) -> ParseResult<Value> {
|
2017-11-06 21:06:30 -06:00
|
|
|
Ok(Value::String(value_node!(s.fragment.to_string(), s.pos)))
|
2017-06-09 18:25:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// quoted_value is a quoted string.
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(quoted_value( Span ) -> Value,
|
|
|
|
map_res!(strtok, str_to_value)
|
2017-06-09 18:25:51 -05:00
|
|
|
);
|
|
|
|
|
2017-06-07 20:14:40 -05:00
|
|
|
// Helper function to make the return types work for down below.
|
2017-11-05 15:26:52 -06:00
|
|
|
fn triple_to_number(v: (Option<Token>, Option<Token>, Option<Token>)) -> ParseResult<Value> {
|
2017-11-06 21:06:30 -06:00
|
|
|
let (pref, mut pref_pos) = match v.0 {
|
2017-11-08 18:55:10 -06:00
|
|
|
None => {
|
|
|
|
("",
|
|
|
|
Position {
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
|
|
|
})
|
|
|
|
}
|
2017-11-06 21:06:30 -06:00
|
|
|
Some(ref bs) => (bs.fragment.borrow(), bs.pos.clone()),
|
2017-06-07 20:14:40 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let has_dot = v.1.is_some();
|
|
|
|
|
|
|
|
if v.0.is_some() && !has_dot && v.2.is_none() {
|
2017-11-06 21:06:30 -06:00
|
|
|
return Ok(Value::Int(value_node!(try!(FromStr::from_str(pref)), pref_pos)));
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
if v.0.is_none() && has_dot {
|
|
|
|
pref_pos = v.1.unwrap().pos;
|
|
|
|
}
|
2017-11-08 18:55:10 -06:00
|
|
|
|
2017-06-07 20:14:40 -05:00
|
|
|
let suf = match v.2 {
|
|
|
|
None => "",
|
2017-11-05 15:26:52 -06:00
|
|
|
Some(ref bs) => &bs.fragment,
|
2017-06-07 20:14:40 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let to_parse = pref.to_string() + "." + suf;
|
2017-11-06 21:06:30 -06:00
|
|
|
// TODO(jwall): if there is an error we should report where that error occured.
|
2017-06-07 20:14:40 -05:00
|
|
|
let f = try!(FromStr::from_str(&to_parse));
|
2017-11-06 21:06:30 -06:00
|
|
|
return Ok(Value::Float(value_node!(f, pref_pos)));
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(number( Span ) -> Value,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(alt!(
|
|
|
|
complete!(do_parse!( // 1.0
|
2017-11-05 15:26:52 -06:00
|
|
|
prefix: digittok >>
|
|
|
|
has_dot: dottok >>
|
|
|
|
suffix: digittok >>
|
|
|
|
peek!(not!(digittok)) >>
|
2017-06-07 20:14:40 -05:00
|
|
|
(Some(prefix), Some(has_dot), Some(suffix))
|
|
|
|
)) |
|
|
|
|
complete!(do_parse!( // 1.
|
2017-11-05 15:26:52 -06:00
|
|
|
prefix: digittok >>
|
|
|
|
has_dot: dottok >>
|
|
|
|
peek!(not!(digittok)) >>
|
2017-06-07 20:14:40 -05:00
|
|
|
(Some(prefix), Some(has_dot), None)
|
|
|
|
)) |
|
|
|
|
complete!(do_parse!( // .1
|
2017-11-05 15:26:52 -06:00
|
|
|
has_dot: dottok >>
|
|
|
|
suffix: digittok >>
|
|
|
|
peek!(not!(digittok)) >>
|
2017-06-07 20:14:40 -05:00
|
|
|
(None, Some(has_dot), Some(suffix))
|
|
|
|
)) |
|
|
|
|
do_parse!( // 1
|
2017-11-05 15:26:52 -06:00
|
|
|
prefix: digittok >>
|
2017-06-07 20:14:40 -05:00
|
|
|
// The peek!(not!(..)) make this whole combinator slightly
|
|
|
|
// safer for partial inputs.
|
2017-11-05 15:26:52 -06:00
|
|
|
peek!(not!(digittok)) >>
|
2017-06-07 20:14:40 -05:00
|
|
|
(Some(prefix), None, None)
|
|
|
|
)),
|
|
|
|
triple_to_number
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
named!(
|
|
|
|
#[doc="Capture a field and value pair composed of `<symbol> = <value>,`"],
|
2017-11-05 15:26:52 -06:00
|
|
|
field_value( Span ) -> (Token, Expression),
|
2017-06-07 20:14:40 -05:00
|
|
|
do_parse!(
|
2017-11-05 15:26:52 -06:00
|
|
|
field: barewordtok >>
|
|
|
|
ws!(equaltok) >>
|
2017-06-07 20:14:40 -05:00
|
|
|
value: expression >>
|
|
|
|
(field, value)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Helper function to make the return types work for down below.
|
2017-11-06 21:06:30 -06:00
|
|
|
fn vec_to_tuple(t: (Span, FieldList)) -> ParseResult<Value> {
|
|
|
|
Ok(Value::Tuple(value_node!(t.1, Position{line: t.0.line as usize, column: t.0.offset as usize})))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(field_list( Span ) -> FieldList,
|
|
|
|
separated_list!(commatok, ws!(field_value)));
|
2017-06-07 20:14:40 -05:00
|
|
|
|
|
|
|
named!(
|
|
|
|
#[doc="Capture a tuple of named fields with values. {<field>=<value>,...}"],
|
2017-11-05 15:26:52 -06:00
|
|
|
tuple( Span ) -> Value,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
2017-11-06 21:06:30 -06:00
|
|
|
do_parse!(
|
|
|
|
pos: position!() >>
|
|
|
|
v: delimited!(lbracetok,
|
2017-06-07 20:14:40 -05:00
|
|
|
ws!(field_list),
|
2017-11-06 21:06:30 -06:00
|
|
|
rbracetok) >>
|
|
|
|
(pos, v)
|
|
|
|
),
|
2017-06-07 20:14:40 -05:00
|
|
|
vec_to_tuple
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(value( Span ) -> Value, alt!(number | quoted_value | symbol | tuple));
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-09-18 18:32:47 -05:00
|
|
|
fn value_to_expression(v: Value) -> ParseResult<Expression> {
|
2017-06-07 20:14:40 -05:00
|
|
|
Ok(Expression::Simple(v))
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(simple_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
|
|
|
value,
|
|
|
|
value_to_expression
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-08 18:55:10 -06:00
|
|
|
fn tuple_to_binary_expression(tpl: (Span, BinaryExprType, Value, Expression))
|
|
|
|
-> ParseResult<Expression> {
|
2017-11-05 15:26:52 -06:00
|
|
|
Ok(Expression::Binary(BinaryOpDef {
|
2017-11-06 21:06:30 -06:00
|
|
|
kind: tpl.1,
|
|
|
|
left: tpl.2,
|
|
|
|
right: Box::new(tpl.3),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position {
|
2017-11-08 18:55:10 -06:00
|
|
|
line: tpl.0.line as usize,
|
|
|
|
column: tpl.0.offset as usize,
|
2017-11-08 20:06:16 -06:00
|
|
|
},
|
2017-09-21 00:06:43 -05:00
|
|
|
}))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
macro_rules! do_binary_expr {
|
|
|
|
($i:expr, $fn:expr, $typ:expr) => {
|
|
|
|
// NOTE(jwall): Nom macros do magic with their inputs. They in fact
|
|
|
|
// rewrite your macro argumets for you. Which means we require this $i
|
|
|
|
// paramater even though we don't explicitely pass it below. I don't
|
|
|
|
// particularly like this but I'm living with it for now.
|
|
|
|
map_res!(
|
|
|
|
$i, do_parse!(
|
2017-11-06 21:06:30 -06:00
|
|
|
pos: position!() >>
|
2017-11-05 15:26:52 -06:00
|
|
|
left: value >>
|
|
|
|
ws!($fn) >>
|
|
|
|
right: expression >>
|
2017-11-06 21:06:30 -06:00
|
|
|
(pos, $typ, left, right)
|
2017-11-05 15:26:52 -06:00
|
|
|
),
|
|
|
|
tuple_to_binary_expression
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
named!(add_expression( Span ) -> Expression,
|
|
|
|
do_binary_expr!(plustok, BinaryExprType::Add)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(sub_expression( Span ) -> Expression,
|
|
|
|
do_binary_expr!(dashtok, BinaryExprType::Sub)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(mul_expression( Span ) -> Expression,
|
|
|
|
do_binary_expr!(startok, BinaryExprType::Mul)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(div_expression( Span ) -> Expression,
|
|
|
|
do_binary_expr!(slashtok, BinaryExprType::Div)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
|
|
|
|
2017-07-19 18:42:31 -05:00
|
|
|
fn expression_to_grouped_expression(e: Expression) -> ParseResult<Expression> {
|
2017-06-07 20:14:40 -05:00
|
|
|
Ok(Expression::Grouped(Box::new(e)))
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(grouped_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
2017-11-05 15:26:52 -06:00
|
|
|
preceded!(lparentok, terminated!(expression, rparentok)),
|
2017-06-07 20:14:40 -05:00
|
|
|
expression_to_grouped_expression
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn assert_nonempty_list<T>(v: Vec<T>) -> ParseResult<Vec<T>> {
|
|
|
|
if v.is_empty() {
|
2017-11-08 18:55:10 -06:00
|
|
|
return Err(Box::new(ParseError::EmptyExpression("Selectors can't be empty.".to_string())));
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
return Ok(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
named!(selector_list( Span ) -> SelectorList,
|
|
|
|
map_res!(
|
|
|
|
separated_list!(dottok, barewordtok),
|
|
|
|
assert_nonempty_list
|
|
|
|
)
|
|
|
|
);
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
fn tuple_to_copy(t: (Span, SelectorList, FieldList)) -> ParseResult<Expression> {
|
2017-11-05 15:26:52 -06:00
|
|
|
Ok(Expression::Copy(CopyDef {
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: t.1,
|
|
|
|
fields: t.2,
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position {
|
2017-11-08 18:55:10 -06:00
|
|
|
line: t.0.line as usize,
|
|
|
|
column: t.0.offset as usize,
|
2017-11-08 20:06:16 -06:00
|
|
|
},
|
2017-11-05 15:26:52 -06:00
|
|
|
}))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(copy_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
|
|
|
do_parse!(
|
2017-11-06 21:06:30 -06:00
|
|
|
pos: position!() >>
|
2017-06-07 20:14:40 -05:00
|
|
|
selector: selector_list >>
|
2017-11-06 21:06:30 -06:00
|
|
|
lbracetok >>
|
|
|
|
fields: ws!(field_list) >>
|
|
|
|
rbracetok >>
|
|
|
|
(pos, selector, fields)
|
2017-06-07 20:14:40 -05:00
|
|
|
),
|
|
|
|
tuple_to_copy
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
fn tuple_to_macro(mut t: (Span, Vec<Value>, Value)) -> ParseResult<Expression> {
|
|
|
|
match t.2 {
|
2017-06-07 20:14:40 -05:00
|
|
|
Value::Tuple(v) => {
|
2017-08-12 14:48:28 -05:00
|
|
|
Ok(Expression::Macro(MacroDef {
|
2017-11-06 21:06:30 -06:00
|
|
|
argdefs: t.1
|
2017-11-05 15:26:52 -06:00
|
|
|
.drain(0..)
|
|
|
|
.map(|s| {
|
|
|
|
Positioned {
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: s.pos().clone(),
|
2017-11-05 15:26:52 -06:00
|
|
|
val: s.to_string(),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
2017-09-18 20:12:34 -05:00
|
|
|
fields: v.val,
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position {
|
2017-11-08 18:55:10 -06:00
|
|
|
line: t.0.line as usize,
|
|
|
|
column: t.0.offset as usize,
|
2017-11-08 20:06:16 -06:00
|
|
|
},
|
2017-08-12 14:48:28 -05:00
|
|
|
}))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
// TODO(jwall): Show a better version of the unexpected parsed value.
|
|
|
|
val => {
|
|
|
|
Err(Box::new(ParseError::UnexpectedToken("{ .. }".to_string(), format!("{:?}", val))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(arglist( Span ) -> Vec<Value>, separated_list!(ws!(commatok), symbol));
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(macro_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
|
|
|
do_parse!(
|
2017-11-06 21:06:30 -06:00
|
|
|
pos: position!() >>
|
|
|
|
macrotok >>
|
|
|
|
ws!(lparentok) >>
|
|
|
|
arglist: ws!(arglist) >>
|
|
|
|
rparentok >>
|
|
|
|
ws!(fatcommatok) >>
|
|
|
|
map: tuple >>
|
|
|
|
(pos, arglist, map)
|
2017-06-07 20:14:40 -05:00
|
|
|
),
|
2017-07-12 20:55:03 -05:00
|
|
|
tuple_to_macro
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
fn tuple_to_select(t: (Span, Expression, Expression, Value)) -> ParseResult<Expression> {
|
|
|
|
match t.3 {
|
2017-06-07 20:14:40 -05:00
|
|
|
Value::Tuple(v) => {
|
2017-11-05 15:26:52 -06:00
|
|
|
Ok(Expression::Select(SelectDef {
|
2017-11-06 21:06:30 -06:00
|
|
|
val: Box::new(t.1),
|
|
|
|
default: Box::new(t.2),
|
2017-09-18 20:12:34 -05:00
|
|
|
tuple: v.val,
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position {
|
2017-11-08 18:55:10 -06:00
|
|
|
line: t.0.line as usize,
|
|
|
|
column: t.0.offset as usize,
|
2017-11-08 20:06:16 -06:00
|
|
|
},
|
2017-08-12 14:48:28 -05:00
|
|
|
}))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
// TODO(jwall): Show a better version of the unexpected parsed value.
|
|
|
|
val => {
|
|
|
|
Err(Box::new(ParseError::UnexpectedToken("{ .. }".to_string(), format!("{:?}", val))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(select_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
2017-11-05 15:26:52 -06:00
|
|
|
do_parse!(
|
2017-11-06 21:06:30 -06:00
|
|
|
pos: position!() >>
|
2017-11-05 15:26:52 -06:00
|
|
|
selecttok >>
|
2017-11-06 21:06:30 -06:00
|
|
|
val: ws!(terminated!(expression, commatok)) >>
|
|
|
|
default: ws!(terminated!(expression, commatok)) >>
|
|
|
|
map: ws!(tuple) >>
|
|
|
|
(pos, val, default, map)
|
2017-11-05 15:26:52 -06:00
|
|
|
),
|
2017-06-07 20:14:40 -05:00
|
|
|
tuple_to_select
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn tuple_to_format(t: (Token, Vec<Expression>)) -> ParseResult<Expression> {
|
|
|
|
Ok(Expression::Format(FormatDef {
|
|
|
|
template: t.0.fragment.to_string(),
|
|
|
|
args: t.1,
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: t.0.pos,
|
2017-11-05 15:26:52 -06:00
|
|
|
}))
|
2017-08-08 21:02:54 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(format_expression( Span ) -> Expression,
|
2017-08-08 21:02:54 -05:00
|
|
|
map_res!(
|
|
|
|
do_parse!(
|
2017-11-05 15:26:52 -06:00
|
|
|
tmpl: ws!(strtok) >>
|
|
|
|
ws!(pcttok) >>
|
|
|
|
lparentok >>
|
|
|
|
args: ws!(separated_list!(ws!(commatok), expression)) >>
|
|
|
|
rparentok >>
|
2017-08-08 21:02:54 -05:00
|
|
|
(tmpl, args)
|
|
|
|
),
|
|
|
|
tuple_to_format
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
fn tuple_to_call(t: (Span, Value, Vec<Expression>)) -> ParseResult<Expression> {
|
|
|
|
if let Value::Selector(sl) = t.1 {
|
2017-11-05 15:26:52 -06:00
|
|
|
Ok(Expression::Call(CallDef {
|
2017-09-18 20:12:34 -05:00
|
|
|
macroref: sl.val,
|
2017-11-06 21:06:30 -06:00
|
|
|
arglist: t.2,
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position {
|
2017-11-08 18:55:10 -06:00
|
|
|
line: t.0.line as usize,
|
|
|
|
column: t.0.offset as usize,
|
2017-11-08 20:06:16 -06:00
|
|
|
},
|
2017-08-12 14:48:28 -05:00
|
|
|
}))
|
2017-06-07 20:14:40 -05:00
|
|
|
} else {
|
|
|
|
Err(Box::new(ParseError::UnexpectedToken("Selector".to_string(), format!("{:?}", t.0))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:06:30 -06:00
|
|
|
fn vec_to_selector_value(t: (Span, SelectorList)) -> ParseResult<Value> {
|
|
|
|
Ok(Value::Selector(value_node!(t.1, Position{line: t.0.line as usize, column: t.0.offset as usize})))
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(selector_value( Span ) -> Value,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
2017-11-06 21:06:30 -06:00
|
|
|
do_parse!(
|
|
|
|
pos: position!() >>
|
|
|
|
sl: ws!(selector_list) >>
|
|
|
|
(pos, sl)
|
|
|
|
),
|
2017-06-10 11:09:24 -05:00
|
|
|
vec_to_selector_value
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(call_expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
map_res!(
|
|
|
|
do_parse!(
|
2017-11-06 21:06:30 -06:00
|
|
|
pos: position!() >>
|
2017-07-12 20:55:03 -05:00
|
|
|
macroname: selector_value >>
|
2017-11-06 21:06:30 -06:00
|
|
|
lparentok >>
|
|
|
|
args: ws!(separated_list!(ws!(commatok), expression)) >>
|
|
|
|
rparentok >>
|
|
|
|
(pos, macroname, args)
|
2017-06-07 20:14:40 -05:00
|
|
|
),
|
|
|
|
tuple_to_call
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// 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.
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(expression( Span ) -> Expression,
|
2017-06-07 20:14:40 -05:00
|
|
|
alt!(
|
|
|
|
complete!(add_expression) |
|
|
|
|
complete!(sub_expression) |
|
|
|
|
complete!(mul_expression) |
|
|
|
|
complete!(div_expression) |
|
|
|
|
complete!(grouped_expression) |
|
2017-07-12 20:55:03 -05:00
|
|
|
complete!(macro_expression) |
|
2017-08-08 21:02:54 -05:00
|
|
|
complete!(format_expression) |
|
2017-06-07 20:14:40 -05:00
|
|
|
complete!(select_expression) |
|
|
|
|
complete!(call_expression) |
|
|
|
|
complete!(copy_expression) |
|
|
|
|
ws!(simple_expression)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
fn expression_to_statement(v: Expression) -> ParseResult<Statement> {
|
|
|
|
Ok(Statement::Expression(v))
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(expression_statement( Span ) -> Statement,
|
2017-06-10 11:09:24 -05:00
|
|
|
map_res!(
|
2017-11-05 15:26:52 -06:00
|
|
|
terminated!(ws!(expression), semicolontok),
|
2017-06-10 11:09:24 -05:00
|
|
|
expression_to_statement
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn tuple_to_let(t: (Token, Expression)) -> ParseResult<Statement> {
|
2017-06-10 11:09:24 -05:00
|
|
|
Ok(Statement::Let {
|
2017-11-05 15:26:52 -06:00
|
|
|
name: t.0,
|
2017-06-10 11:09:24 -05:00
|
|
|
value: t.1,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(let_statement( Span ) -> Statement,
|
2017-06-10 11:09:24 -05:00
|
|
|
map_res!(
|
|
|
|
terminated!(do_parse!(
|
2017-11-05 15:26:52 -06:00
|
|
|
lettok >>
|
|
|
|
name: ws!(barewordtok) >>
|
|
|
|
equaltok >>
|
2017-06-10 11:09:24 -05:00
|
|
|
val: ws!(expression) >>
|
|
|
|
(name, val)
|
2017-11-05 15:26:52 -06:00
|
|
|
), semicolontok),
|
2017-06-10 11:09:24 -05:00
|
|
|
tuple_to_let
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
fn tuple_to_import(t: (Token, Token)) -> ParseResult<Statement> {
|
2017-06-10 11:09:24 -05:00
|
|
|
Ok(Statement::Import {
|
2017-11-05 15:26:52 -06:00
|
|
|
name: t.0,
|
|
|
|
path: t.1.fragment.to_string(),
|
2017-06-10 11:09:24 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(import_statement( Span ) -> Statement,
|
2017-06-10 11:09:24 -05:00
|
|
|
map_res!(
|
|
|
|
terminated!(do_parse!(
|
2017-11-05 15:26:52 -06:00
|
|
|
importtok >>
|
|
|
|
path: ws!(strtok) >>
|
|
|
|
astok >>
|
|
|
|
name: ws!(barewordtok) >>
|
2017-06-10 11:09:24 -05:00
|
|
|
(name, path)
|
2017-11-05 15:26:52 -06:00
|
|
|
), semicolontok),
|
2017-06-10 11:09:24 -05:00
|
|
|
tuple_to_import
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(statement( Span ) -> Statement,
|
2017-06-10 11:09:24 -05:00
|
|
|
alt_complete!(
|
|
|
|
import_statement |
|
|
|
|
let_statement |
|
|
|
|
expression_statement
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
named!(pub parse( Span ) -> Vec<Statement>, many1!(ws!(statement)));
|
2017-06-10 11:09:24 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2017-08-12 14:48:28 -05:00
|
|
|
use super::{Statement, Expression, Value, MacroDef, SelectDef, CallDef};
|
2017-08-08 21:02:54 -05:00
|
|
|
use super::{number, symbol, parse, field_value, tuple, grouped_expression};
|
2017-09-21 17:39:13 -05:00
|
|
|
use super::{copy_expression, macro_expression, select_expression};
|
2017-08-08 21:02:54 -05:00
|
|
|
use super::{format_expression, call_expression, expression};
|
2017-06-10 11:09:24 -05:00
|
|
|
use super::{expression_statement, let_statement, import_statement, statement};
|
2017-08-12 14:48:28 -05:00
|
|
|
use ast::*;
|
2017-11-05 15:26:52 -06:00
|
|
|
use nom_locate::LocatedSpan;
|
2017-06-10 11:09:24 -05:00
|
|
|
|
2017-09-18 20:12:34 -05:00
|
|
|
use nom::IResult;
|
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_statement_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
let mut stmt = "import \"foo\" as foo;";
|
|
|
|
let input = LocatedSpan::new(stmt);
|
|
|
|
assert_eq!(statement(input),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
offset: stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
fragment: "",
|
|
|
|
},
|
|
|
|
Statement::Import{
|
|
|
|
path: "foo".to_string(),
|
|
|
|
name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 17,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
2017-11-05 15:26:52 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
assert!(statement(LocatedSpan::new("import foo")).is_err() );
|
|
|
|
|
|
|
|
stmt = "let foo = 1.0 ;";
|
|
|
|
let input = LocatedSpan::new(stmt);
|
|
|
|
assert_eq!(statement(input),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
offset: stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
fragment: "",
|
|
|
|
},
|
|
|
|
Statement::Let{
|
|
|
|
name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position {
|
|
|
|
line: 1,
|
|
|
|
column: 5,
|
|
|
|
},
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 11})))
|
2017-11-05 15:26:52 -06:00
|
|
|
}));
|
|
|
|
stmt = "1.0;";
|
|
|
|
let input = LocatedSpan::new(stmt);
|
|
|
|
assert_eq!(statement(input),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
offset: stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
fragment: "",
|
|
|
|
},
|
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 1}))))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_import_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(import_statement(LocatedSpan::new("import")).is_incomplete());
|
|
|
|
assert!(import_statement(LocatedSpan::new("import \"foo\"")).is_incomplete());
|
|
|
|
assert!(import_statement(LocatedSpan::new("import \"foo\" as")).is_incomplete());
|
|
|
|
assert!(import_statement(LocatedSpan::new("import \"foo\" as foo")).is_incomplete());
|
|
|
|
|
|
|
|
let import_stmt = "import \"foo\" as foo;";
|
|
|
|
assert_eq!(import_statement(LocatedSpan::new(import_stmt)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
line: 1,
|
|
|
|
offset: import_stmt.len(),
|
|
|
|
},
|
|
|
|
Statement::Import{
|
|
|
|
path: "foo".to_string(),
|
|
|
|
name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 17,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_let_statement_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(let_statement(LocatedSpan::new("foo")).is_err() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let \"foo\"")).is_err() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let 1")).is_err() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let")).is_incomplete() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let foo")).is_incomplete() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let foo =")).is_incomplete() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let foo = ")).is_incomplete() );
|
|
|
|
assert!(let_statement(LocatedSpan::new("let foo = 1")).is_incomplete() );
|
|
|
|
|
|
|
|
let mut let_stmt = "let foo = 1.0 ;";
|
|
|
|
assert_eq!(let_statement(LocatedSpan::new(let_stmt)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: let_stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Statement::Let{name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 5,
|
|
|
|
},
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 11})))
|
2017-11-05 15:26:52 -06:00
|
|
|
}));
|
2017-11-08 18:55:10 -06:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
let_stmt = "let foo= 1.0;";
|
|
|
|
assert_eq!(let_statement(LocatedSpan::new(let_stmt)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: let_stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Statement::Let{name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 5,
|
|
|
|
}
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 10})))}));
|
2017-11-05 15:26:52 -06:00
|
|
|
let_stmt = "let foo =1.0;";
|
|
|
|
assert_eq!(let_statement(LocatedSpan::new(let_stmt)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: let_stmt.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Statement::Let{name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 5,
|
|
|
|
}
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 10})))}));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expression_statement_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(expression_statement(LocatedSpan::new("foo")).is_incomplete() );
|
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("1.0;")),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: 4,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("1.0 ;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new(" 1.0;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Float(value_node!(1.0, Position{line: 1, column: 2}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("foo;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 4,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("foo ;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new(" foo;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 2}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("\"foo\";")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 6,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new("\"foo\" ;")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 7,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 1}))))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression_statement(LocatedSpan::new(" \"foo\";")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 7,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Statement::Expression(
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 2}))))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expression_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 1,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 1})))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("foo")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 3,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1})))));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1 + 1")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Add,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 5})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1 - 1")),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Sub,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 5})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1 * 1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Mul,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 5})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1 / 1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 5,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Div,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 5})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1+1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 3,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Add,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 3})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1-1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 3,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Sub,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 3})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1*1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 3,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Mul,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 3})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("1/1")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: 3,
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Binary(BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Div,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 1})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 3})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 0 },
|
2017-11-06 21:06:30 -06:00
|
|
|
})));
|
2017-11-05 15:26:52 -06:00
|
|
|
let macro_expr = "macro (arg1, arg2) => { foo = arg1 }";
|
|
|
|
assert_eq!(expression(LocatedSpan::new(macro_expr)),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: macro_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Macro(MacroDef{
|
|
|
|
argdefs: vec![
|
2017-11-08 20:06:16 -06:00
|
|
|
Positioned::new("arg1".to_string(), Position{line: 1, column: 8}),
|
|
|
|
Positioned::new("arg2".to_string(), Position{line: 1, column: 14}),
|
2017-11-06 21:06:30 -06:00
|
|
|
],
|
|
|
|
fields: vec![
|
|
|
|
(Token::new("foo", Position{line: 1, column: 25}),
|
|
|
|
Expression::Simple(Value::Symbol(value_node!("arg1".to_string(), Position{line: 1, column: 31})))),
|
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-06 21:06:30 -06:00
|
|
|
})
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
);
|
2017-11-08 18:55:10 -06:00
|
|
|
let select_expr = "select foo, 1, { foo = 2 }";
|
|
|
|
assert_eq!(expression(LocatedSpan::new(select_expr)),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: select_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Select(SelectDef{
|
|
|
|
val: Box::new(Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 8})))),
|
|
|
|
default: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 13})))),
|
|
|
|
tuple: vec![
|
|
|
|
(Token::new("foo", Position{line: 1, column: 18}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(2, Position{line: 1, column: 24}))))
|
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-06 21:06:30 -06:00
|
|
|
})
|
2017-11-05 15:26:52 -06:00
|
|
|
)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
2017-11-08 18:55:10 -06:00
|
|
|
let call_expr = "foo.bar (1, \"foo\")";
|
|
|
|
assert_eq!(expression(LocatedSpan::new(call_expr)),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: call_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Expression::Call(CallDef{
|
|
|
|
macroref: vec![Token::new("foo", Position{line:1,column: 1}),
|
|
|
|
Token::new("bar", Position{line:1,column: 5})],
|
|
|
|
arglist: vec![
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 10}))),
|
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 13}))),
|
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-06 21:06:30 -06:00
|
|
|
})
|
|
|
|
)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
2017-11-08 18:55:10 -06:00
|
|
|
assert_eq!(expression(LocatedSpan::new("(1 + 1)")),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: 7,
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Expression::Grouped(
|
|
|
|
Box::new(
|
|
|
|
Expression::Binary(
|
|
|
|
BinaryOpDef{
|
|
|
|
kind: BinaryExprType::Add,
|
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 2})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 6})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 1 },
|
2017-11-06 21:06:30 -06:00
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
2017-11-06 21:06:30 -06:00
|
|
|
)
|
2017-06-07 20:14:40 -05:00
|
|
|
);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-08-08 21:02:54 -05:00
|
|
|
#[test]
|
|
|
|
fn test_format_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(format_expression(LocatedSpan::new("\"foo")).is_err() );
|
|
|
|
assert!(format_expression(LocatedSpan::new("\"foo\"")).is_incomplete() );
|
|
|
|
assert!(format_expression(LocatedSpan::new("\"foo\" %")).is_incomplete() );
|
|
|
|
assert!(format_expression(LocatedSpan::new("\"foo\" % (1, 2")).is_incomplete() );
|
|
|
|
|
|
|
|
let mut fmt_expr = "\"foo @ @\" % (1, 2)";
|
|
|
|
assert_eq!(format_expression(LocatedSpan::new(fmt_expr)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: fmt_expr.len(),
|
|
|
|
line: 1
|
|
|
|
},
|
|
|
|
Expression::Format(
|
|
|
|
FormatDef{
|
|
|
|
template: "foo @ @".to_string(),
|
2017-11-06 21:06:30 -06:00
|
|
|
args: vec![Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 14}))),
|
|
|
|
Expression::Simple(Value::Int(value_node!(2, Position{line: 1, column: 17})))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 1},
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
)
|
2017-08-08 21:02:54 -05:00
|
|
|
)
|
|
|
|
);
|
2017-11-05 15:26:52 -06:00
|
|
|
|
|
|
|
fmt_expr = "\"foo @ @\"%(1, 2)";
|
|
|
|
assert_eq!(format_expression(LocatedSpan::new(fmt_expr)),
|
|
|
|
IResult::Done(LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: fmt_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
|
|
|
Expression::Format(
|
|
|
|
FormatDef{
|
|
|
|
template: "foo @ @".to_string(),
|
2017-11-06 21:06:30 -06:00
|
|
|
args: vec![Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 12}))),
|
|
|
|
Expression::Simple(Value::Int(value_node!(2, Position{line: 1, column: 15})))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 1 },
|
2017-11-05 15:26:52 -06:00
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
2017-08-12 14:48:28 -05:00
|
|
|
);
|
2017-08-08 21:02:54 -05:00
|
|
|
}
|
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_call_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(call_expression(LocatedSpan::new("foo")).is_incomplete() );
|
|
|
|
assert!(call_expression(LocatedSpan::new("foo (")).is_incomplete() );
|
|
|
|
assert!(call_expression(LocatedSpan::new("foo (1")).is_incomplete() );
|
|
|
|
assert!(call_expression(LocatedSpan::new("foo (1,")).is_incomplete() );
|
|
|
|
assert!(call_expression(LocatedSpan::new("foo (1,2")).is_incomplete() );
|
|
|
|
|
|
|
|
let mut copy_expr = "foo (1, \"foo\")";
|
|
|
|
assert_eq!(call_expression(LocatedSpan::new(copy_expr)),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
line: 1,
|
|
|
|
offset: copy_expr.len(),
|
|
|
|
},
|
|
|
|
Expression::Call(CallDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
macroref: vec![Token::new("foo", Position{line:1, column: 1})],
|
2017-11-05 15:26:52 -06:00
|
|
|
arglist: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 6}))),
|
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 9}))),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-05 15:26:52 -06:00
|
|
|
})
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
2017-08-08 21:02:54 -05:00
|
|
|
);
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
copy_expr = "foo.bar (1, \"foo\")";
|
|
|
|
assert_eq!(call_expression(LocatedSpan::new(copy_expr)),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
line: 1,
|
|
|
|
offset: copy_expr.len(),
|
|
|
|
},
|
|
|
|
Expression::Call(CallDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
macroref: vec![Token::new("foo", Position{line: 1, column: 1}),
|
|
|
|
Token::new("bar", Position{line: 1, column: 5})],
|
2017-11-05 15:26:52 -06:00
|
|
|
arglist: vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 10}))),
|
|
|
|
Expression::Simple(Value::String(value_node!("foo".to_string(), Position{line: 1, column: 13}))),
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-05 15:26:52 -06:00
|
|
|
})
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_select_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(select_expression(LocatedSpan::new("select")).is_incomplete());
|
|
|
|
assert!(select_expression(LocatedSpan::new("select foo")).is_incomplete());
|
|
|
|
assert!(select_expression(LocatedSpan::new("select foo, 1")).is_incomplete());
|
|
|
|
assert!(select_expression(LocatedSpan::new("select foo, 1, {")).is_incomplete());
|
|
|
|
|
|
|
|
let select_expr = "select foo, 1, { foo = 2 }";
|
|
|
|
assert_eq!(select_expression(LocatedSpan::new(select_expr)),
|
2017-11-06 21:06:30 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
2017-11-05 15:26:52 -06:00
|
|
|
fragment: "",
|
|
|
|
offset: select_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
Expression::Select(SelectDef{
|
|
|
|
val: Box::new(Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 8})))),
|
|
|
|
default: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 13})))),
|
|
|
|
tuple: vec![
|
|
|
|
(Token::new("foo", Position{line: 1, column: 18}), Expression::Simple(Value::Int(value_node!(2, Position{line: 1, column: 24}))))
|
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-06 21:06:30 -06:00
|
|
|
})
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
2017-07-12 20:55:03 -05:00
|
|
|
fn test_macro_expression_parsing() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(macro_expression(LocatedSpan::new("foo")).is_err() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro \"foo\"")).is_err() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro 1")).is_err() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg, arg2")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg1, arg2) =>")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg1, arg2) => {")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg1, arg2) => { foo")).is_incomplete() );
|
|
|
|
assert!(macro_expression(LocatedSpan::new("macro (arg1, arg2) => { foo =")).is_incomplete() );
|
|
|
|
|
|
|
|
let macro_expr = "macro (arg1, arg2) => {foo=1,bar=2}";
|
|
|
|
assert_eq!(macro_expression(LocatedSpan::new(macro_expr)),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: macro_expr.len(),
|
|
|
|
line: 1
|
|
|
|
},
|
|
|
|
Expression::Macro(MacroDef{
|
2017-11-08 20:06:16 -06:00
|
|
|
argdefs: vec![Positioned::new("arg1".to_string(), Position{line: 1, column: 8}),
|
|
|
|
Positioned::new("arg2".to_string(), Position{line: 1, column: 14})],
|
2017-11-06 21:06:30 -06:00
|
|
|
fields: vec![(Token::new("foo", Position{line: 1, column: 24}), Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 28})))),
|
|
|
|
(Token::new("bar", Position{line: 1, column: 30}), Expression::Simple(Value::Int(value_node!(2, Position{line: 1, column: 34}))))
|
2017-11-05 15:26:52 -06:00
|
|
|
],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-05 15:26:52 -06:00
|
|
|
})
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(copy_expression(LocatedSpan::new("{}")).is_err() );
|
|
|
|
assert!(copy_expression(LocatedSpan::new("foo")).is_incomplete() );
|
|
|
|
assert!(copy_expression(LocatedSpan::new("foo{")).is_incomplete() );
|
|
|
|
|
|
|
|
let mut copy_expr = "foo{}";
|
|
|
|
assert_eq!(copy_expression(LocatedSpan::new(copy_expr)),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: copy_expr.len(),
|
|
|
|
line: 1
|
|
|
|
},
|
|
|
|
Expression::Copy(CopyDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: vec![Token::new("foo", Position{line: 1, column: 1})],
|
2017-11-05 15:26:52 -06:00
|
|
|
fields: Vec::new(),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-05 15:26:52 -06:00
|
|
|
})
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
2017-11-05 15:26:52 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
copy_expr = "foo{bar=1}";
|
|
|
|
assert_eq!(copy_expression(LocatedSpan::new(copy_expr)),
|
|
|
|
IResult::Done(
|
|
|
|
LocatedSpan{
|
|
|
|
fragment: "",
|
|
|
|
offset: copy_expr.len(),
|
|
|
|
line: 1
|
|
|
|
},
|
|
|
|
Expression::Copy(CopyDef{
|
2017-11-06 21:06:30 -06:00
|
|
|
selector: vec![Token::new("foo", Position{line: 1, column: 1})],
|
|
|
|
fields: vec![(Token::new("bar", Position{line: 1, column: 5}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 9}))))],
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position{line: 1, column: 0},
|
2017-11-05 15:26:52 -06:00
|
|
|
})
|
|
|
|
)
|
|
|
|
);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_grouped_expression_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(grouped_expression(LocatedSpan::new("foo")).is_err() );
|
|
|
|
assert!(grouped_expression(LocatedSpan::new("(foo")).is_incomplete() );
|
|
|
|
assert_eq!(grouped_expression(LocatedSpan::new("(foo)")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 5, line: 1},
|
2017-06-10 11:09:24 -05:00
|
|
|
Expression::Grouped(
|
|
|
|
Box::new(
|
|
|
|
Expression::Simple(
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 2}))))))
|
2017-06-10 11:09:24 -05:00
|
|
|
);
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(grouped_expression(LocatedSpan::new("(1 + 1)")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
2017-06-10 11:09:24 -05:00
|
|
|
Expression::Grouped(
|
|
|
|
Box::new(
|
2017-09-21 00:06:43 -05:00
|
|
|
Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 2})),
|
2017-09-21 00:06:43 -05:00
|
|
|
right: Box::new(Expression::Simple(
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Int(value_node!(1, Position{line: 1, column: 6})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 1 },
|
2017-09-21 00:06:43 -05:00
|
|
|
}
|
2017-06-10 11:09:24 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_tuple_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(tuple(LocatedSpan::new("{")).is_incomplete() );
|
|
|
|
assert!(tuple(LocatedSpan::new("{ foo")).is_incomplete() );
|
|
|
|
assert!(tuple(LocatedSpan::new("{ foo =")).is_incomplete() );
|
|
|
|
assert!(tuple(LocatedSpan::new("{ foo = 1")).is_incomplete() );
|
|
|
|
assert!(tuple(LocatedSpan::new("{ foo = 1,")).is_incomplete() );
|
|
|
|
assert!(tuple(LocatedSpan::new("{ foo = 1, bar =")).is_incomplete() );
|
|
|
|
|
|
|
|
let mut tuple_expr = "{ }";
|
|
|
|
assert_eq!(tuple(LocatedSpan::new(tuple_expr)),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: tuple_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Value::Tuple(
|
2017-11-06 21:06:30 -06:00
|
|
|
value_node!(vec![], Position{line: 1, column: 0}))));
|
2017-06-10 11:09:24 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
tuple_expr = "{ foo = 1 }";
|
|
|
|
assert_eq!(tuple(LocatedSpan::new(tuple_expr)),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: tuple_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Value::Tuple(
|
2017-09-23 11:19:45 -05:00
|
|
|
value_node!(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line:1, column: 3}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 9}))))
|
|
|
|
], Position{line: 1, column: 0}))));
|
2017-06-10 11:09:24 -05:00
|
|
|
|
2017-11-05 15:26:52 -06:00
|
|
|
tuple_expr = "{ foo = 1, bar = \"1\" }";
|
|
|
|
assert_eq!(tuple(LocatedSpan::new(tuple_expr)),
|
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: tuple_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Value::Tuple(
|
2017-09-23 11:19:45 -05:00
|
|
|
value_node!(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 3}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 9})))),
|
|
|
|
(Token::new("bar", Position{line: 1, column: 12}),
|
|
|
|
Expression::Simple(Value::String(value_node!("1".to_string(), Position{line: 1, column: 18}))))
|
|
|
|
], Position{line: 1, column: 0}))));
|
2017-11-05 15:26:52 -06:00
|
|
|
tuple_expr = "{ foo = 1, bar = {} }";
|
2017-11-08 18:55:10 -06:00
|
|
|
assert_eq!(tuple(LocatedSpan::new(tuple_expr)),
|
2017-11-05 15:26:52 -06:00
|
|
|
IResult::Done(LocatedSpan {
|
|
|
|
fragment: "",
|
|
|
|
offset: tuple_expr.len(),
|
|
|
|
line: 1,
|
|
|
|
},
|
2017-06-10 11:09:24 -05:00
|
|
|
Value::Tuple(
|
2017-09-23 11:19:45 -05:00
|
|
|
value_node!(vec![
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 3}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 9})))),
|
|
|
|
(Token::new("bar", Position{line: 1, column: 12}),
|
|
|
|
Expression::Simple(Value::Tuple(value_node!(Vec::new(), Position{line: 1, column: 17}))))
|
|
|
|
], Position{line: 1, column: 0}))));
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_field_value_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(field_value(LocatedSpan::new("foo")).is_incomplete() );
|
|
|
|
assert!(field_value(LocatedSpan::new("foo =")).is_incomplete() );
|
|
|
|
|
|
|
|
assert_eq!(field_value(LocatedSpan::new("foo = 1")),
|
|
|
|
IResult::Done(LocatedSpan { offset: 7, line: 1, fragment: "" },
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 7}))))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(field_value(LocatedSpan::new("foo = \"1\"")),
|
|
|
|
IResult::Done(LocatedSpan { offset: 9, line: 1, fragment: "" },
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::String(value_node!("1".to_string(), Position{line: 1, column: 7}))))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(field_value(LocatedSpan::new("foo = bar")),
|
|
|
|
IResult::Done(LocatedSpan { offset: 9, line: 1, fragment: "" },
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::Symbol(value_node!("bar".to_string(), Position{line: 1, column: 7}))))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(field_value(LocatedSpan::new("foo = bar ")),
|
|
|
|
IResult::Done(LocatedSpan { offset: 10, line: 1, fragment: "" },
|
2017-11-06 21:06:30 -06:00
|
|
|
(Token::new("foo", Position{line: 1, column: 1}),
|
|
|
|
Expression::Simple(Value::Symbol(value_node!("bar".to_string(), Position{line: 1, column: 7}))))) );
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_number_parsing() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert!(number(LocatedSpan::new(".")).is_err() );
|
|
|
|
assert!(number(LocatedSpan::new(". ")).is_err() );
|
|
|
|
assert_eq!(number(LocatedSpan::new("1.0")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 3, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Float(value_node!(1.0, Position{line: 1, column: 1}))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(number(LocatedSpan::new("1.")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 2, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Float(value_node!(1.0, Position{line: 1, column: 1}))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(number(LocatedSpan::new("1")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 1, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Int(value_node!(1, Position{line: 1, column: 1}))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(number(LocatedSpan::new(".1")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 2, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Float(value_node!(0.1, Position{line: 1, column: 1}))) );
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
|
|
|
|
2017-07-19 18:42:31 -05:00
|
|
|
#[test]
|
|
|
|
fn test_symbol_parsing() {
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(symbol(LocatedSpan::new("foo")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 3, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(symbol(LocatedSpan::new("foo-bar")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Symbol(value_node!("foo-bar".to_string(), Position{line: 1, column: 1}))) );
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(symbol(LocatedSpan::new("foo_bar")),
|
|
|
|
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
2017-11-06 21:06:30 -06:00
|
|
|
Value::Symbol(value_node!("foo_bar".to_string(), Position{line: 1, column: 1}))) );
|
2017-07-19 18:42:31 -05:00
|
|
|
}
|
|
|
|
|
2017-06-10 11:09:24 -05:00
|
|
|
#[test]
|
|
|
|
fn test_parse() {
|
2017-11-05 15:26:52 -06:00
|
|
|
let bad_input = LocatedSpan::new("import mylib as lib;");
|
2017-06-10 11:09:24 -05:00
|
|
|
let bad_result = parse(bad_input);
|
|
|
|
assert!(bad_result.is_err() );
|
|
|
|
|
|
|
|
// Valid parsing tree
|
2017-11-08 18:55:10 -06:00
|
|
|
let input = LocatedSpan::new("import \"mylib\" as lib;let foo = 1;1+1;");
|
2017-06-10 11:09:24 -05:00
|
|
|
let result = parse(input);
|
|
|
|
assert!(result.is_done() );
|
|
|
|
let tpl = result.unwrap();
|
2017-11-05 15:26:52 -06:00
|
|
|
assert_eq!(tpl.0.fragment, "");
|
2017-06-10 11:09:24 -05:00
|
|
|
assert_eq!(tpl.1,
|
2017-06-07 20:14:40 -05:00
|
|
|
vec![
|
|
|
|
Statement::Import{
|
2017-07-19 18:42:31 -05:00
|
|
|
path: "mylib".to_string(),
|
2017-11-05 15:26:52 -06:00
|
|
|
name: Token{
|
|
|
|
fragment: "lib".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 19,
|
|
|
|
}
|
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
},
|
|
|
|
Statement::Let{
|
2017-11-05 15:26:52 -06:00
|
|
|
name: Token{
|
|
|
|
fragment: "foo".to_string(),
|
|
|
|
pos: Position{
|
|
|
|
line: 1,
|
|
|
|
column: 27,
|
|
|
|
}
|
|
|
|
},
|
2017-11-06 21:06:30 -06:00
|
|
|
value: Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 33})))
|
2017-06-07 20:14:40 -05:00
|
|
|
},
|
|
|
|
Statement::Expression(
|
2017-09-21 00:06:43 -05:00
|
|
|
Expression::Binary(
|
2017-09-21 08:12:18 -05:00
|
|
|
BinaryOpDef{
|
2017-09-21 00:06:43 -05:00
|
|
|
kind: BinaryExprType::Add,
|
2017-11-06 21:06:30 -06:00
|
|
|
left: Value::Int(value_node!(1, Position{line: 1, column: 35})),
|
|
|
|
right: Box::new(Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 37})))),
|
2017-11-08 20:06:16 -06:00
|
|
|
pos: Position { line: 1, column: 34 },
|
2017-09-21 00:06:43 -05:00
|
|
|
})
|
2017-06-07 20:14:40 -05:00
|
|
|
)
|
|
|
|
]);
|
2017-06-10 11:09:24 -05:00
|
|
|
}
|
2017-06-07 20:14:40 -05:00
|
|
|
}
|