mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
Parsing the map operations.
This commit is contained in:
parent
51edc6d15c
commit
eeac1ba599
9
TODO.md
9
TODO.md
@ -1,18 +1,11 @@
|
|||||||
# Major Planned Features
|
# Major Planned Features
|
||||||
|
|
||||||
## Boolean operations
|
## Boolean operations and type
|
||||||
|
|
||||||
* equality (for everything)
|
* equality (for everything)
|
||||||
* contains (for lists or strings)
|
* contains (for lists or strings)
|
||||||
* less than or greater than (for numeric types)
|
* less than or greater than (for numeric types)
|
||||||
|
|
||||||
## List processing
|
|
||||||
|
|
||||||
* Map over lists
|
|
||||||
* Filtering lists
|
|
||||||
* Flags could automatically expand a list of values into a list of flags.
|
|
||||||
* Joining a lists elements. (i.e. folds)
|
|
||||||
|
|
||||||
## Query Language (Experimental)
|
## Query Language (Experimental)
|
||||||
|
|
||||||
You should be able to ask the compiler to tell you any value or set of values in the
|
You should be able to ask the compiler to tell you any value or set of values in the
|
||||||
|
@ -15,7 +15,13 @@ let db_conn1 = mk_db_conn(dbhost1, 3306, dbname);
|
|||||||
let db_conn2 = mk_db_conn(dbhost2, 3306, dbname);
|
let db_conn2 = mk_db_conn(dbhost2, 3306, dbname);
|
||||||
|
|
||||||
// We have two database connections in a list
|
// We have two database connections in a list
|
||||||
let db_conns = [db_conn1.conn_string, db_conn2.conn_string];
|
let db_conn_list = [db_conn1, db_conn2];
|
||||||
|
|
||||||
|
let connstr_mapper = macro (item) => {
|
||||||
|
str = item.conn_string
|
||||||
|
};
|
||||||
|
|
||||||
|
let db_conns = map connstr_mapper.str [db_conn1, db_conn2];
|
||||||
|
|
||||||
// Our server configuration.
|
// Our server configuration.
|
||||||
let server_config = {
|
let server_config = {
|
||||||
|
21
src/ast.rs
21
src/ast.rs
@ -599,6 +599,10 @@ impl MacroDef {
|
|||||||
// noop
|
// noop
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
&Expression::ListOp(_) => {
|
||||||
|
// noop
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,6 +655,21 @@ pub struct ListDef {
|
|||||||
pub pos: Position,
|
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.
|
/// Encodes a ucg expression. Expressions compute a value from.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
@ -667,6 +686,7 @@ pub enum Expression {
|
|||||||
Call(CallDef),
|
Call(CallDef),
|
||||||
Macro(MacroDef),
|
Macro(MacroDef),
|
||||||
Select(SelectDef),
|
Select(SelectDef),
|
||||||
|
ListOp(ListOpDef),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
@ -681,6 +701,7 @@ impl Expression {
|
|||||||
&Expression::Call(ref def) => &def.pos,
|
&Expression::Call(ref def) => &def.pos,
|
||||||
&Expression::Macro(ref def) => &def.pos,
|
&Expression::Macro(ref def) => &def.pos,
|
||||||
&Expression::Select(ref def) => &def.pos,
|
&Expression::Select(ref def) => &def.pos,
|
||||||
|
&Expression::ListOp(ref def) => &def.pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
src/build.rs
40
src/build.rs
@ -493,12 +493,16 @@ impl Builder {
|
|||||||
&Val::List(_) => {
|
&Val::List(_) => {
|
||||||
stack.push_back(first.clone());
|
stack.push_back(first.clone());
|
||||||
}
|
}
|
||||||
_ => {
|
val => {
|
||||||
|
eprintln!("Not a tuple or list! {:?}", val)
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let &Some(ref tail) = &sl.tail {
|
if let &Some(ref tail) = &sl.tail {
|
||||||
|
if tail.len() == 0 {
|
||||||
|
return Ok(first);
|
||||||
|
}
|
||||||
let mut it = tail.iter().peekable();
|
let mut it = tail.iter().peekable();
|
||||||
loop {
|
loop {
|
||||||
let vref = stack.pop_front().unwrap();
|
let vref = stack.pop_front().unwrap();
|
||||||
@ -839,6 +843,39 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(jwall): We still need to write unit tests for these.
|
||||||
|
fn eval_list_op(&self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
let l = &def.target.elems;
|
||||||
|
let mac = &def.mac;
|
||||||
|
if let &Val::Macro(ref macdef) = try!(self.lookup_selector(&mac.sel)).as_ref() {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for expr in l.iter() {
|
||||||
|
let argvals = vec![try!(self.eval_expr(expr))];
|
||||||
|
let fields = try!(macdef.eval(argvals));
|
||||||
|
if let Some(v) = Self::find_in_fieldlist(&def.field, &fields) {
|
||||||
|
match def.typ {
|
||||||
|
ListOpType::Map => {
|
||||||
|
out.push(v.clone());
|
||||||
|
}
|
||||||
|
ListOpType::Filter => {
|
||||||
|
if let &Val::Empty = v.as_ref() {
|
||||||
|
// noop
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(Rc::new(Val::List(out)));
|
||||||
|
}
|
||||||
|
return Err(Box::new(error::Error::new(
|
||||||
|
format!("Expected macro but got {:?}", mac),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.pos.clone(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
// Evals a single Expression in the context of a running Builder.
|
// Evals a single Expression in the context of a running Builder.
|
||||||
// It does not mutate the builders collected state at all.
|
// It does not mutate the builders collected state at all.
|
||||||
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
||||||
@ -853,6 +890,7 @@ impl Builder {
|
|||||||
&Expression::Call(ref def) => self.eval_call(def),
|
&Expression::Call(ref def) => self.eval_call(def),
|
||||||
&Expression::Macro(ref def) => self.eval_macro_def(def),
|
&Expression::Macro(ref def) => self.eval_macro_def(def),
|
||||||
&Expression::Select(ref def) => self.eval_select(def),
|
&Expression::Select(ref def) => self.eval_select(def),
|
||||||
|
&Expression::ListOp(ref def) => self.eval_list_op(def),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
src/lib.rs
72
src/lib.rs
@ -54,19 +54,25 @@
|
|||||||
//! * select
|
//! * select
|
||||||
//! * macro
|
//! * macro
|
||||||
//! * env
|
//! * env
|
||||||
|
//! * map
|
||||||
|
//! * filter
|
||||||
//! * NULL
|
//! * NULL
|
||||||
//!
|
//!
|
||||||
//! ### Primitive types
|
//! ### Primitive types
|
||||||
//!
|
//!
|
||||||
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
|
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
|
||||||
//!
|
//!
|
||||||
//! * An Int is any integer number.
|
//! #### Int
|
||||||
|
//!
|
||||||
|
//! An Int is any integer number.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! 1; // a single Integer
|
//! 1; // a single Integer
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * A Float is any number with a decimal point.
|
//! #### Float
|
||||||
|
//!
|
||||||
|
//! A Float is any number with a decimal point.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! 1.0; // A typical float.
|
//! 1.0; // A typical float.
|
||||||
@ -74,7 +80,9 @@
|
|||||||
//! .1 // the leading 0 is also optional.
|
//! .1 // the leading 0 is also optional.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * A String is any quoted text. Backslashes within a string escape the next preceding
|
//! #### String
|
||||||
|
//!
|
||||||
|
//! A String is any quoted text. Backslashes within a string escape the next preceding
|
||||||
//! character.
|
//! character.
|
||||||
//!
|
//!
|
||||||
//! ``` ucg
|
//! ``` ucg
|
||||||
@ -82,7 +90,9 @@
|
|||||||
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * A NULL is an empty type. It represents no value.
|
//! #### NULL or the Empty type.
|
||||||
|
//!
|
||||||
|
//! A NULL is an empty type. It represents no value.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let empty = NULL;
|
//! let empty = NULL;
|
||||||
@ -92,7 +102,9 @@
|
|||||||
//!
|
//!
|
||||||
//! ucg has two complex data types, Lists and Tuples.
|
//! ucg has two complex data types, Lists and Tuples.
|
||||||
//!
|
//!
|
||||||
//! * Lists are surrounded with square brackets `[ ]` and have comma separated elements.
|
//! #### Lists
|
||||||
|
//!
|
||||||
|
//! Lists are surrounded with square brackets `[ ]` and have comma separated elements.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! [1, 2, 3]; // A simple list of numbers.
|
//! [1, 2, 3]; // A simple list of numbers.
|
||||||
@ -108,7 +120,47 @@
|
|||||||
//! let zero = mylist.0;
|
//! let zero = mylist.0;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! * Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
|
//! ##### List macros
|
||||||
|
//!
|
||||||
|
//! ucg supports a couple of ways to use macros for mapping or filtering a list to a new list.
|
||||||
|
//!
|
||||||
|
//! A map expression starts with the map keyword followed by the name of a macro with exactly
|
||||||
|
//! one argument, a `.`, and the name of the output field for the macro. ucg will apply the macro
|
||||||
|
//! to each element of the list and then take the output field from the resulting tuple and add append
|
||||||
|
//! it to the resulting list. If the output field does not exist in the macro it will be a compile
|
||||||
|
//! error.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! let list = [1, 2, 3, 4];
|
||||||
|
//! let mapper = macro(item) => { result = item + 1 };
|
||||||
|
//!
|
||||||
|
//! // results in: [2, 3, 4, 5]
|
||||||
|
//! let mapped = map mapper.result list;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//
|
||||||
|
//! A filter expression starts with the filter keyword followed by the name of a macro with exactly
|
||||||
|
//! one argument, a `.`, and the name of the output field for the macro. The filter will apply the
|
||||||
|
//! macro to each element of the list and if the output field is a Value that is not NULL then the
|
||||||
|
//! list element is appended to the output list. If the output field returns a NULL Value then the
|
||||||
|
//! element is not appended to the output list. If the output field does not exist in the macro it
|
||||||
|
//! will be a compile error.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! let list = ["foo", "bar", "foo", "bar"];
|
||||||
|
//! let filtrator = macro(item) => {
|
||||||
|
//! ok = select item NULL {
|
||||||
|
//! foo = 1
|
||||||
|
//! }
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! // results in: ["foo", "foo"]
|
||||||
|
//! let filtered = filter filtrator.ok list;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! #### Tuple
|
||||||
|
//!
|
||||||
|
//! Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
|
||||||
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
|
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
|
||||||
//! be a bareword without quotes.
|
//! be a bareword without quotes.
|
||||||
//!
|
//!
|
||||||
@ -223,10 +275,10 @@
|
|||||||
//!
|
//!
|
||||||
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
|
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
|
||||||
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the `macro`
|
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the `macro`
|
||||||
//! keyword followed by the arguments in parentheses and then a tuple.
|
//! keyword followed by the arguments in parentheses, a `=>`, and then a tuple.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
//! let myfunc = macro (arg1, arg2) {
|
//! let myfunc = macro (arg1, arg2) => {
|
||||||
//! host = arg1,
|
//! host = arg1,
|
||||||
//! port = arg2,
|
//! port = arg2,
|
||||||
//! connstr = "couchdb://@:@" % (arg1, arg2),
|
//! connstr = "couchdb://@:@" % (arg1, arg2),
|
||||||
@ -278,6 +330,10 @@
|
|||||||
//!
|
//!
|
||||||
//! let mysqlconf = dbconfigs.mysql;
|
//! let mysqlconf = dbconfigs.mysql;
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
// The following is necessary to allow the macros in tokenizer and parse modules
|
||||||
|
// to succeed.
|
||||||
|
#![recursion_limit = "128"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate nom;
|
extern crate nom;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
104
src/parse.rs
104
src/parse.rs
@ -404,7 +404,7 @@ named!(macro_expression<TokenIter, Expression, ParseError>,
|
|||||||
map_res!(
|
map_res!(
|
||||||
do_parse!(
|
do_parse!(
|
||||||
pos: pos >>
|
pos: pos >>
|
||||||
start: word!("macro") >>
|
word!("macro") >>
|
||||||
punct!("(") >>
|
punct!("(") >>
|
||||||
arglist: arglist >>
|
arglist: arglist >>
|
||||||
punct!(")") >>
|
punct!(")") >>
|
||||||
@ -512,6 +512,105 @@ named!(call_expression<TokenIter, Expression, ParseError>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn symbol_or_list(input: TokenIter) -> NomResult<Value> {
|
||||||
|
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<Expression> {
|
||||||
|
let pos = tpl.0;
|
||||||
|
let t = if &tpl.1.fragment == "map" {
|
||||||
|
ListOpType::Map
|
||||||
|
} else if &tpl.1.fragment == "filter" {
|
||||||
|
ListOpType::Filter
|
||||||
|
} else {
|
||||||
|
return Err(ParseError {
|
||||||
|
description: format!(
|
||||||
|
"Expected one of 'map' or 'filter' but got '{}'",
|
||||||
|
tpl.1.fragment
|
||||||
|
),
|
||||||
|
pos: 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(ParseError {
|
||||||
|
description: format!("Missing a result field for the macro"),
|
||||||
|
pos: pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
&mut Some(ref mut tl) => {
|
||||||
|
if tl.len() < 1 {
|
||||||
|
return Err(ParseError {
|
||||||
|
description: format!("Missing a result field for the macro"),
|
||||||
|
pos: 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(ParseError {
|
||||||
|
pos: pos,
|
||||||
|
description: format!("Expected a list but got {:?}", list),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Err(ParseError {
|
||||||
|
pos: pos,
|
||||||
|
description: format!("Expected a macro but got {:?}", macroname),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(list_op_expression<TokenIter, Expression, ParseError>,
|
||||||
|
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
|
// NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters
|
||||||
// alot. We need to process alternatives in order of decreasing
|
// alot. We need to process alternatives in order of decreasing
|
||||||
// specificity. Unfortunately this means we are required to go in a
|
// specificity. Unfortunately this means we are required to go in a
|
||||||
@ -525,6 +624,7 @@ named!(call_expression<TokenIter, Expression, ParseError>,
|
|||||||
named!(expression<TokenIter, Expression, ParseError>,
|
named!(expression<TokenIter, Expression, ParseError>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
expr: alt!(
|
expr: alt!(
|
||||||
|
complete!(list_op_expression) |
|
||||||
complete!(add_expression) |
|
complete!(add_expression) |
|
||||||
complete!(sub_expression) |
|
complete!(sub_expression) |
|
||||||
complete!(mul_expression) |
|
complete!(mul_expression) |
|
||||||
@ -625,7 +725,7 @@ pub fn parse(input: LocatedSpan<&str>) -> Result<Vec<Statement>, ParseError> {
|
|||||||
IResult::Error(e) => {
|
IResult::Error(e) => {
|
||||||
return Err(ParseError {
|
return Err(ParseError {
|
||||||
description: format!(
|
description: format!(
|
||||||
"Tokenization error: {:?} current token: {:?}",
|
"Statement Parse error: {:?} current token: {:?}",
|
||||||
e, i_[0]
|
e, i_[0]
|
||||||
),
|
),
|
||||||
pos: Position {
|
pos: Position {
|
||||||
|
@ -205,6 +205,14 @@ named!(astok( Span ) -> Token,
|
|||||||
do_tag_tok!(TokenType::BAREWORD, "as")
|
do_tag_tok!(TokenType::BAREWORD, "as")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(maptok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::BAREWORD, "map")
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(filtertok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::BAREWORD, "filter")
|
||||||
|
);
|
||||||
|
|
||||||
fn end_of_input(input: Span) -> nom::IResult<Span, Token> {
|
fn end_of_input(input: Span) -> nom::IResult<Span, Token> {
|
||||||
match eof!(input,) {
|
match eof!(input,) {
|
||||||
nom::IResult::Done(_, _) => {
|
nom::IResult::Done(_, _) => {
|
||||||
@ -273,7 +281,6 @@ named!(token( Span ) -> Token,
|
|||||||
alt!(
|
alt!(
|
||||||
strtok |
|
strtok |
|
||||||
emptytok | // This must come before the barewordtok
|
emptytok | // This must come before the barewordtok
|
||||||
barewordtok |
|
|
||||||
digittok |
|
digittok |
|
||||||
commatok |
|
commatok |
|
||||||
rbracetok |
|
rbracetok |
|
||||||
@ -297,6 +304,9 @@ named!(token( Span ) -> Token,
|
|||||||
macrotok |
|
macrotok |
|
||||||
importtok |
|
importtok |
|
||||||
astok |
|
astok |
|
||||||
|
maptok |
|
||||||
|
filtertok |
|
||||||
|
barewordtok |
|
||||||
whitespace |
|
whitespace |
|
||||||
end_of_input)
|
end_of_input)
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user