Use loc! macro instead of loc()

This commit is contained in:
Richard Feldman 2019-11-20 05:37:19 -05:00
parent 155ae60348
commit cc74c37045
6 changed files with 138 additions and 107 deletions

View file

@ -5,6 +5,7 @@ pub mod ident;
pub mod keyword; pub mod keyword;
pub mod module; pub mod module;
pub mod number_literal; pub mod number_literal;
#[macro_use]
pub mod parser; pub mod parser;
pub mod problems; pub mod problems;
pub mod record; pub mod record;
@ -27,7 +28,7 @@ use parse::collection::collection;
use parse::ident::{ident, unqualified_ident, variant_or_ident, Ident}; use parse::ident::{ident, unqualified_ident, variant_or_ident, Ident};
use parse::number_literal::number_literal; use parse::number_literal::number_literal;
use parse::parser::{ use parse::parser::{
allocated, and, attempt, between, char, either, loc, map, map_with_arena, not, not_followed_by, allocated, and, attempt, between, char, either, map, map_with_arena, not, not_followed_by,
one_of10, one_of17, one_of2, one_of3, one_of5, one_of6, one_or_more, optional, skip_first, one_of10, one_of17, one_of2, one_of3, one_of5, one_of6, one_or_more, optional, skip_first,
skip_second, string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, skip_second, string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason,
ParseResult, Parser, State, ParseResult, Parser, State,
@ -57,7 +58,7 @@ fn app_module<'a>() -> impl Parser<'a, Module<'a>> {
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> { fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
map( map(
and( and(
skip_first(string("interface"), and(space1(1), loc(ident()))), skip_first(string("interface"), and(space1(1), loc!(ident()))),
and(mod_header_list("exposes"), mod_header_list("imports")), and(mod_header_list("exposes"), mod_header_list("imports")),
), ),
|( |(
@ -118,7 +119,7 @@ fn mod_header_list<'a>(
> { > {
and( and(
and(skip_second(space1(1), string(kw)), space1(1)), and(skip_second(space1(1), string(kw)), space1(1)),
collection(char('['), loc(mod_header_entry()), char(','), char(']'), 1), collection(char('['), loc!(mod_header_entry()), char(','), char(']'), 1),
) )
} }
@ -150,15 +151,15 @@ fn loc_parse_expr_body_without_operators<'a>(
) -> ParseResult<'a, Located<Expr<'a>>> { ) -> ParseResult<'a, Located<Expr<'a>>> {
one_of10( one_of10(
loc_parenthetical_expr(min_indent), loc_parenthetical_expr(min_indent),
loc(string_literal()), loc!(string_literal()),
loc(number_literal()), loc!(number_literal()),
loc(closure(min_indent)), loc!(closure(min_indent)),
loc(record_literal(min_indent)), loc!(record_literal(min_indent)),
loc(list_literal(min_indent)), loc!(list_literal(min_indent)),
loc(unary_op(min_indent)), loc!(unary_op(min_indent)),
loc(case_expr(min_indent)), loc!(case_expr(min_indent)),
loc(if_expr(min_indent)), loc!(if_expr(min_indent)),
loc(ident_etc(min_indent)), loc!(ident_etc(min_indent)),
) )
.parse(arena, state) .parse(arena, state)
} }
@ -170,8 +171,8 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
one_of2( one_of2(
map_with_arena( map_with_arena(
and( and(
loc(char('!')), loc!(char('!')),
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
), ),
|arena, (loc_op, loc_expr)| { |arena, (loc_op, loc_expr)| {
Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Not)) Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Not))
@ -179,8 +180,8 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
), ),
map_with_arena( map_with_arena(
and( and(
loc(char('-')), loc!(char('-')),
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
), ),
|arena, (loc_op, loc_expr)| { |arena, (loc_op, loc_expr)| {
Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Negate)) Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Negate))
@ -200,11 +201,11 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
// parse the spaces and then attach them retroactively to the expression // parse the spaces and then attach them retroactively to the expression
// preceding the operator (the one we parsed before considering operators). // preceding the operator (the one we parsed before considering operators).
optional(and( optional(and(
and(space0(min_indent), loc(binop())), and(space0(min_indent), loc!(binop())),
// The spaces *after* the operator can be attached directly to // The spaces *after* the operator can be attached directly to
// the expression following the operator. // the expression following the operator.
space0_before( space0_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
)), )),
@ -232,11 +233,11 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> { pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
then( then(
loc(and( loc!(and(
between( between(
char('('), char('('),
space0_around( space0_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
char(')'), char(')'),
@ -427,11 +428,11 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
/// It would be too weird to parse; imagine `(UserId userId) : ...` above `(UserId userId) = ...` /// It would be too weird to parse; imagine `(UserId userId) : ...` above `(UserId userId) = ...`
pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> { pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
move |arena, state| { move |arena, state| {
let (loc_tuple, state) = loc(and( let (loc_tuple, state) = loc!(and(
space0_after( space0_after(
between( between(
char('('), char('('),
space0_around(loc(pattern(min_indent)), min_indent), space0_around(loc!(pattern(min_indent)), min_indent),
char(')'), char(')'),
), ),
min_indent, min_indent,
@ -484,7 +485,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
// Spaces after the '=' (at a normal indentation level) and then the expr. // Spaces after the '=' (at a normal indentation level) and then the expr.
// The expr itself must be indented more than the pattern and '=' // The expr itself must be indented more than the pattern and '='
space0_before( space0_before(
loc(move |arena, state| parse_expr(indented_more, arena, state)), loc!(move |arena, state| parse_expr(indented_more, arena, state)),
min_indent, min_indent,
), ),
), ),
@ -579,17 +580,17 @@ fn parse_def_expr<'a>(
// //
// It should be indented more than the original, and it will // It should be indented more than the original, and it will
// end when outdented again. // end when outdented again.
loc(move |arena, state| parse_expr(indented_more, arena, state)), loc!(move |arena, state| parse_expr(indented_more, arena, state)),
and( and(
// Optionally parse additional defs. // Optionally parse additional defs.
zero_or_more(allocated(space1_before( zero_or_more(allocated(space1_before(
loc(def(original_indent)), loc!(def(original_indent)),
original_indent, original_indent,
))), ))),
// Parse the final expression that will be returned. // Parse the final expression that will be returned.
// It should be indented the same amount as the original. // It should be indented the same amount as the original.
space1_before( space1_before(
loc(move |arena, state| parse_expr(original_indent, arena, state)), loc!(move |arena, state| parse_expr(original_indent, arena, state)),
original_indent, original_indent,
), ),
), ),
@ -640,15 +641,15 @@ fn loc_parse_function_arg<'a>(
) -> ParseResult<'a, Located<Expr<'a>>> { ) -> ParseResult<'a, Located<Expr<'a>>> {
one_of10( one_of10(
loc_parenthetical_expr(min_indent), loc_parenthetical_expr(min_indent),
loc(string_literal()), loc!(string_literal()),
loc(number_literal()), loc!(number_literal()),
loc(closure(min_indent)), loc!(closure(min_indent)),
loc(record_literal(min_indent)), loc!(record_literal(min_indent)),
loc(list_literal(min_indent)), loc!(list_literal(min_indent)),
loc(unary_op(min_indent)), loc!(unary_op(min_indent)),
loc(case_expr(min_indent)), loc!(case_expr(min_indent)),
loc(if_expr(min_indent)), loc!(if_expr(min_indent)),
loc(ident_without_apply()), loc!(ident_without_apply()),
) )
.parse(arena, state) .parse(arena, state)
} }
@ -689,7 +690,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
attempt( attempt(
Attempting::ClosureBody, Attempting::ClosureBody,
space0_before( space0_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
), ),
@ -714,20 +715,20 @@ fn parse_closure_param<'a>(
) -> ParseResult<'a, Located<Pattern<'a>>> { ) -> ParseResult<'a, Located<Pattern<'a>>> {
one_of5( one_of5(
// An ident is the most common param, e.g. \foo -> ... // An ident is the most common param, e.g. \foo -> ...
loc(ident_pattern()), loc!(ident_pattern()),
// Underscore is also common, e.g. \_ -> ... // Underscore is also common, e.g. \_ -> ...
loc(underscore_pattern()), loc!(underscore_pattern()),
// You can destructure records in params, e.g. \{ x, y } -> ... // You can destructure records in params, e.g. \{ x, y } -> ...
loc(record_destructure(min_indent)), loc!(record_destructure(min_indent)),
// If you wrap it in parens, you can match any arbitrary pattern at all. // If you wrap it in parens, you can match any arbitrary pattern at all.
// e.g. \User.UserId userId -> ... // e.g. \User.UserId userId -> ...
between( between(
char('('), char('('),
space0_around(loc(pattern(min_indent)), min_indent), space0_around(loc!(pattern(min_indent)), min_indent),
char(')'), char(')'),
), ),
// The least common, but still allowed, e.g. \Foo -> ... // The least common, but still allowed, e.g. \Foo -> ...
loc(map(unqualified_variant(), |name| { loc!(map(unqualified_variant(), |name| {
Pattern::Variant(&[], name) Pattern::Variant(&[], name)
})), })),
) )
@ -766,7 +767,7 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
map( map(
collection( collection(
char('{'), char('{'),
loc(ident_pattern()), loc!(ident_pattern()),
char(','), char(','),
char('}'), char('}'),
min_indent, min_indent,
@ -791,7 +792,7 @@ pub fn case_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
Attempting::CaseCondition, Attempting::CaseCondition,
skip_second( skip_second(
space1_around( space1_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
string(keyword::WHEN), string(keyword::WHEN),
@ -825,7 +826,7 @@ pub fn case_branches<'a>(
// 2. Parse the other branches. Their indentation levels must be == the first branch's. // 2. Parse the other branches. Their indentation levels must be == the first branch's.
let (mut loc_first_pattern, state) = let (mut loc_first_pattern, state) =
space1_before(loc(pattern(min_indent)), min_indent).parse(arena, state)?; space1_before(loc!(pattern(min_indent)), min_indent).parse(arena, state)?;
let original_indent = state.indent_col; let original_indent = state.indent_col;
let indented_more = original_indent + 1; let indented_more = original_indent + 1;
let (spaces_before_arrow, state) = space0(min_indent).parse(arena, state)?; let (spaces_before_arrow, state) = space0(min_indent).parse(arena, state)?;
@ -844,7 +845,7 @@ pub fn case_branches<'a>(
string("->"), string("->"),
// The expr must be indented more than the pattern preceding it // The expr must be indented more than the pattern preceding it
space0_before( space0_before(
loc(move |arena, state| parse_expr(indented_more, arena, state)), loc!(move |arena, state| parse_expr(indented_more, arena, state)),
indented_more, indented_more,
), ),
) )
@ -855,7 +856,7 @@ pub fn case_branches<'a>(
let branch_parser = and( let branch_parser = and(
then( then(
space1_around(loc(pattern(min_indent)), min_indent), space1_around(loc!(pattern(min_indent)), min_indent),
move |_arena, state, loc_pattern| { move |_arena, state, loc_pattern| {
if state.indent_col == original_indent { if state.indent_col == original_indent {
Ok((loc_pattern, state)) Ok((loc_pattern, state))
@ -869,7 +870,7 @@ pub fn case_branches<'a>(
skip_first( skip_first(
string("->"), string("->"),
space1_before( space1_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
), ),
@ -900,7 +901,7 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
skip_first( skip_first(
string(keyword::IF), string(keyword::IF),
space1_around( space1_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
), ),
@ -908,14 +909,14 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
skip_first( skip_first(
string(keyword::THEN), string(keyword::THEN),
space1_around( space1_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
), ),
skip_first( skip_first(
string(keyword::ELSE), string(keyword::ELSE),
space1_before( space1_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), ),
), ),
@ -945,7 +946,7 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
then( then(
// Spaces, then '-', then *not* more spaces. // Spaces, then '-', then *not* more spaces.
not_followed_by( not_followed_by(
and(space1(min_indent), loc(char('-'))), and(space1(min_indent), loc!(char('-'))),
one_of3(char(' '), char('#'), char('\n')), one_of3(char(' '), char('#'), char('\n')),
), ),
move |arena, state, (spaces, loc_minus_char)| { move |arena, state, (spaces, loc_minus_char)| {
@ -1009,7 +1010,7 @@ fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Exp
pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
then( then(
and( and(
loc(ident()), loc!(ident()),
optional(either( optional(either(
// There may optionally be function args after this ident // There may optionally be function args after this ident
loc_function_args(min_indent), loc_function_args(min_indent),
@ -1074,7 +1075,7 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
} }
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> { pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> {
then(loc(ident()), move |_arena, state, loc_ident| { then(loc!(ident()), move |_arena, state, loc_ident| {
Ok((ident_to_expr(loc_ident.value), state)) Ok((ident_to_expr(loc_ident.value), state))
}) })
} }
@ -1155,7 +1156,7 @@ fn binop<'a>() -> impl Parser<'a, BinOp> {
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
let elems = collection( let elems = collection(
char('['), char('['),
loc(expr(min_indent)), loc!(expr(min_indent)),
char(','), char(','),
char(']'), char(']'),
min_indent, min_indent,
@ -1181,7 +1182,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
and( and(
attempt( attempt(
Attempting::Record, Attempting::Record,
loc(record(loc(expr(min_indent)), min_indent)), loc!(record(loc!(expr(min_indent)), min_indent)),
), ),
optional(and(space0(min_indent), equals_with_indent())), optional(and(space0(min_indent), equals_with_indent())),
), ),

View file

@ -1,7 +1,7 @@
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use parse::ast::Attempting; use parse::ast::Attempting;
use region::{Located, Region}; use region::Region;
use std::{char, u16}; use std::{char, u16};
/// A position in a source file. /// A position in a source file.
@ -330,33 +330,6 @@ where
} }
} }
#[inline(always)]
pub fn loc_impl<'a, P, Val>(parser: P) -> impl Parser<'a, Located<Val>>
where
P: Parser<'a, Val>,
{
move |arena, state: State<'a>| {
let start_col = state.column;
let start_line = state.line;
match parser.parse(arena, state) {
Ok((value, state)) => {
let end_col = state.column;
let end_line = state.line;
let region = Region {
start_col,
start_line,
end_col,
end_line,
};
Ok((Located { region, value }, state))
}
Err((fail, state)) => Err((fail, state)),
}
}
}
#[inline(always)] #[inline(always)]
pub fn zero_or_more_impl<'a, P, A>(parser: P) -> impl Parser<'a, Vec<'a, A>> pub fn zero_or_more_impl<'a, P, A>(parser: P) -> impl Parser<'a, Vec<'a, A>>
where where
@ -1334,15 +1307,6 @@ where
BoxedParser::new(map_with_arena_impl(parser, transform)) BoxedParser::new(map_with_arena_impl(parser, transform))
} }
pub fn loc<'a, P, Val>(parser: P) -> BoxedParser<'a, Located<Val>>
where
P: Parser<'a, Val>,
P: 'a,
Val: 'a,
{
BoxedParser::new(loc_impl(parser))
}
pub fn attempt<'a, P, Val>(attempting: Attempting, parser: P) -> BoxedParser<'a, Val> pub fn attempt<'a, P, Val>(attempting: Attempting, parser: P) -> BoxedParser<'a, Val>
where where
P: Parser<'a, Val>, P: Parser<'a, Val>,
@ -1379,3 +1343,29 @@ where
{ {
BoxedParser::new(one_or_more_impl(parser)) BoxedParser::new(one_or_more_impl(parser))
} }
#[macro_export]
macro_rules! loc {
($parser:expr) => {
move |arena, state: State<'a>| {
let start_col = state.column;
let start_line = state.line;
match $parser.parse(arena, state) {
Ok((value, state)) => {
let end_col = state.column;
let end_line = state.line;
let region = Region {
start_col,
start_line,
end_col,
end_line,
};
Ok((Located { region, value }, state))
}
Err((fail, state)) => Err((fail, state)),
}
}
};
}

View file

@ -4,8 +4,8 @@ use parse::ast::Spaceable;
use parse::blankspace::{space0, space0_before}; use parse::blankspace::{space0, space0_before};
use parse::collection::collection; use parse::collection::collection;
use parse::ident::unqualified_ident; use parse::ident::unqualified_ident;
use parse::parser::{and, char, loc, map_with_arena, optional, skip_first, Parser}; use parse::parser::{and, char, map_with_arena, optional, skip_first, Parser, State};
use region::Located; use region::{Located, Region};
/// Parse a record - generally one of these two: /// Parse a record - generally one of these two:
/// ///
@ -30,6 +30,15 @@ where
) )
} }
/// For some reason, record() needs to use this instead of using the loc! macro directly.
#[inline(always)]
pub fn loc<'a, P, Val>(parser: P) -> impl Parser<'a, Located<Val>>
where
P: Parser<'a, Val>,
{
loc!(parser)
}
fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>> fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>>
where where
P: Parser<'a, Located<S>>, P: Parser<'a, Located<S>>,
@ -42,7 +51,7 @@ where
map_with_arena( map_with_arena(
and( and(
// You must have a field name, e.g. "email" // You must have a field name, e.g. "email"
loc(unqualified_ident()), loc!(unqualified_ident()),
and( and(
space0(min_indent), space0(min_indent),
// Having a value is optional; both `{ email }` and `{ email: blah }` work. // Having a value is optional; both `{ email }` and `{ email: blah }` work.

View file

@ -5,22 +5,22 @@ use collections::arena_join;
use parse::ast::{Attempting, TypeAnnotation}; use parse::ast::{Attempting, TypeAnnotation};
use parse::blankspace::{space0_around, space1_before}; use parse::blankspace::{space0_around, space1_before};
use parse::parser::{ use parse::parser::{
and, between, char, loc, map, map_with_arena, one_of5, optional, skip_first, string, and, between, char, map, map_with_arena, one_of5, optional, skip_first, string, unexpected,
unexpected, unexpected_eof, zero_or_more, ParseResult, Parser, State, unexpected_eof, zero_or_more, ParseResult, Parser, State,
}; };
use parse::record::record; use parse::record::record;
use region::Located; use region::{Located, Region};
pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> { pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
one_of5( one_of5(
// The `*` type variable, e.g. in (List *) Wildcard, // The `*` type variable, e.g. in (List *) Wildcard,
map(loc(char('*')), |loc_val| { map(loc!(char('*')), |loc_val| {
loc_val.map(|_| TypeAnnotation::Wildcard) loc_val.map(|_| TypeAnnotation::Wildcard)
}), }),
loc_parenthetical_type(min_indent), loc_parenthetical_type(min_indent),
loc(record_type(min_indent)), loc!(record_type(min_indent)),
loc(applied_type(min_indent)), loc!(applied_type(min_indent)),
loc(parse_type_variable), loc!(parse_type_variable),
) )
} }

View file

@ -1,5 +1,4 @@
extern crate bumpalo; extern crate bumpalo;
extern crate roc;
use self::bumpalo::Bump; use self::bumpalo::Bump;
use roc::can; use roc::can;
@ -13,11 +12,41 @@ use roc::ident::Ident;
use roc::parse; use roc::parse;
use roc::parse::ast::{self, Attempting}; use roc::parse::ast::{self, Attempting};
use roc::parse::blankspace::space0_before; use roc::parse::blankspace::space0_before;
use roc::parse::parser::{loc, Fail, Parser, State}; use roc::parse::parser::{Fail, Parser, State};
use roc::region::{Located, Region}; use roc::region::{Located, Region};
use roc::subs::{Subs, Variable}; use roc::subs::{Subs, Variable};
use roc::types::{Expected, Type}; use roc::types::{Expected, Type};
// TODO Figure out some way to import this macro from roc::parse::parser - it has been
// surprisingly difficult to convince an integration test to do this.
//
// See https://users.rust-lang.org/t/sharing-code-and-macros-in-tests-directory/3098/4
#[macro_export]
macro_rules! loc {
($parser:expr) => {
move |arena, state: State<'a>| {
let start_col = state.column;
let start_line = state.line;
match $parser.parse(arena, state) {
Ok((value, state)) => {
let end_col = state.column;
let end_line = state.line;
let region = Region {
start_col,
start_line,
end_col,
end_line,
};
Ok((Located { region, value }, state))
}
Err((fail, state)) => Err((fail, state)),
}
}
};
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> { pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
@ -26,7 +55,7 @@ pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>,
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> { pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
let state = State::new(&input, Attempting::Module); let state = State::new(&input, Attempting::Module);
let parser = space0_before(loc(parse::expr(0)), 0); let parser = space0_before(loc!(parse::expr(0)), 0);
let answer = parser.parse(&arena, state); let answer = parser.parse(&arena, state);
answer answer

View file

@ -3,6 +3,7 @@ extern crate pretty_assertions;
#[macro_use] #[macro_use]
extern crate indoc; extern crate indoc;
extern crate bumpalo; extern crate bumpalo;
#[macro_use]
extern crate roc; extern crate roc;
#[cfg(test)] #[cfg(test)]
@ -11,11 +12,12 @@ mod test_format {
use roc::parse; use roc::parse;
use roc::parse::ast::{format, Attempting, Expr}; use roc::parse::ast::{format, Attempting, Expr};
use roc::parse::blankspace::space0_before; use roc::parse::blankspace::space0_before;
use roc::parse::parser::{loc, Fail, Parser, State}; use roc::parse::parser::{Fail, Parser, State};
use roc::region::{Located, Region};
fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Expr<'a>, Fail> { fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Expr<'a>, Fail> {
let state = State::new(&input, Attempting::Module); let state = State::new(&input, Attempting::Module);
let parser = space0_before(loc(parse::expr(0)), 0); let parser = space0_before(loc!(parse::expr(0)), 0);
let answer = parser.parse(&arena, state); let answer = parser.parse(&arena, state);
answer answer