mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into editor-comments
This commit is contained in:
commit
f941e30b86
271 changed files with 15287 additions and 8370 deletions
|
@ -1,12 +1,70 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, TypedIdent};
|
||||
use crate::header::{AppHeader, InterfaceHeader, PlatformHeader};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Spaces<'a, T> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub item: T,
|
||||
pub after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Spaced<'a, T> {
|
||||
Item(T),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Item(item) => item.fmt(f),
|
||||
Self::SpaceBefore(item, space) => f
|
||||
.debug_tuple("SpaceBefore")
|
||||
.field(item)
|
||||
.field(space)
|
||||
.finish(),
|
||||
Self::SpaceAfter(item, space) => f
|
||||
.debug_tuple("SpaceAfter")
|
||||
.field(item)
|
||||
.field(space)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtractSpaces<'a>: Sized + Copy {
|
||||
type Item;
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item>;
|
||||
}
|
||||
|
||||
impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for &'a T {
|
||||
type Item = T::Item;
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item> {
|
||||
(*self).extract_spaces()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for Loc<T> {
|
||||
type Item = T::Item;
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item> {
|
||||
let spaces = self.value.extract_spaces();
|
||||
Spaces {
|
||||
before: spaces.before,
|
||||
item: spaces.item,
|
||||
after: spaces.after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Module<'a> {
|
||||
Interface { header: InterfaceHeader<'a> },
|
||||
|
@ -167,6 +225,12 @@ pub struct PrecedenceConflict<'a> {
|
|||
pub expr: &'a Loc<Expr<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct AliasHeader<'a> {
|
||||
pub name: Loc<&'a str>,
|
||||
pub vars: &'a [Loc<Pattern<'a>>],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Def<'a> {
|
||||
// TODO in canonicalization, validate the pattern; only certain patterns
|
||||
|
@ -178,8 +242,7 @@ pub enum Def<'a> {
|
|||
///
|
||||
/// Foo : Bar Baz
|
||||
Alias {
|
||||
name: Loc<&'a str>,
|
||||
vars: &'a [Loc<Pattern<'a>>],
|
||||
header: AliasHeader<'a>,
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
|
@ -207,6 +270,17 @@ pub enum Def<'a> {
|
|||
NotYetImplemented(&'static str),
|
||||
}
|
||||
|
||||
impl<'a> Def<'a> {
|
||||
pub fn unroll_spaces_before(&self) -> (&'a [CommentOrNewline<'a>], &Def) {
|
||||
let (spaces, def): (&'a [_], &Def) = match self {
|
||||
Def::SpaceBefore(def, spaces) => (spaces, def),
|
||||
def => (&[], def),
|
||||
};
|
||||
debug_assert!(!matches!(def, Def::SpaceBefore(_, _)));
|
||||
(spaces, def)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum TypeAnnotation<'a> {
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
|
@ -222,7 +296,7 @@ pub enum TypeAnnotation<'a> {
|
|||
As(
|
||||
&'a Loc<TypeAnnotation<'a>>,
|
||||
&'a [CommentOrNewline<'a>],
|
||||
&'a Loc<TypeAnnotation<'a>>,
|
||||
AliasHeader<'a>,
|
||||
),
|
||||
|
||||
Record {
|
||||
|
@ -341,7 +415,7 @@ pub enum Pattern<'a> {
|
|||
PrivateTag(&'a str),
|
||||
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
|
||||
|
||||
/// This is Loc<Pattern> rather than Loc<str> so we can record comments
|
||||
/// This is Located<Pattern> rather than Located<str> so we can record comments
|
||||
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
||||
/// In practice, these patterns will always be Identifier
|
||||
RecordDestructure(Collection<'a, Loc<Pattern<'a>>>),
|
||||
|
@ -649,6 +723,15 @@ pub trait Spaceable<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Spaceable<'a> for Spaced<'a, T> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Spaced::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Spaced::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for Expr<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Expr::SpaceBefore(self, spaces)
|
||||
|
@ -676,24 +759,6 @@ impl<'a> Spaceable<'a> for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for ImportsEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ImportsEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ImportsEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for TypedIdent<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
TypedIdent::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
TypedIdent::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Val> Spaceable<'a> for AssignedField<'a, Val> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
AssignedField::SpaceBefore(self, spaces)
|
||||
|
@ -735,4 +800,122 @@ impl<'a> Expr<'a> {
|
|||
value: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tag(&self) -> bool {
|
||||
matches!(self, Expr::GlobalTag(_) | Expr::PrivateTag(_))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_extract_spaces {
|
||||
($t:ident $(< $($generic_args:ident),* >)?) => {
|
||||
|
||||
impl<'a, $($($generic_args: Copy),*)?> ExtractSpaces<'a> for $t<'a, $($($generic_args),*)?> {
|
||||
type Item = Self;
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item> {
|
||||
match self {
|
||||
$t::SpaceBefore(item, before) => {
|
||||
match item {
|
||||
$t::SpaceBefore(_, _) => todo!(),
|
||||
$t::SpaceAfter(item, after) => {
|
||||
Spaces {
|
||||
before,
|
||||
item: **item,
|
||||
after,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Spaces {
|
||||
before,
|
||||
item: **item,
|
||||
after: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
$t::SpaceAfter(item, after) => {
|
||||
match item {
|
||||
$t::SpaceBefore(item, before) => {
|
||||
Spaces {
|
||||
before,
|
||||
item: **item,
|
||||
after,
|
||||
}
|
||||
}
|
||||
$t::SpaceAfter(_, _) => todo!(),
|
||||
_ => {
|
||||
Spaces {
|
||||
before: &[],
|
||||
item: **item,
|
||||
after,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Spaces {
|
||||
before: &[],
|
||||
item: *self,
|
||||
after: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_extract_spaces!(Expr);
|
||||
impl_extract_spaces!(Tag);
|
||||
impl_extract_spaces!(AssignedField<T>);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
||||
fn extract_spaces(&self) -> Spaces<'a, T> {
|
||||
match self {
|
||||
Spaced::SpaceBefore(item, before) => match item {
|
||||
Spaced::SpaceBefore(_, _) => todo!(),
|
||||
Spaced::SpaceAfter(item, after) => {
|
||||
if let Spaced::Item(item) = item {
|
||||
Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after,
|
||||
}
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
Spaced::Item(item) => Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after: &[],
|
||||
},
|
||||
},
|
||||
Spaced::SpaceAfter(item, after) => match item {
|
||||
Spaced::SpaceBefore(item, before) => {
|
||||
if let Spaced::Item(item) = item {
|
||||
Spaces {
|
||||
before,
|
||||
item: *item,
|
||||
after,
|
||||
}
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
Spaced::SpaceAfter(_, _) => todo!(),
|
||||
Spaced::Item(item) => Spaces {
|
||||
before: &[],
|
||||
item: *item,
|
||||
after,
|
||||
},
|
||||
},
|
||||
Spaced::Item(item) => Spaces {
|
||||
before: &[],
|
||||
item: *item,
|
||||
after: &[],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
use crate::ast::CommentOrNewline;
|
||||
use crate::ast::Spaceable;
|
||||
use crate::parser::{
|
||||
self, and, backtrackable, BadInputError, Col, Parser, Progress::*, Row, State,
|
||||
};
|
||||
use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
use roc_region::all::Loc;
|
||||
use roc_region::all::Position;
|
||||
|
||||
pub fn space0_around_ee<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_before_problem: fn(Row, Col) -> E,
|
||||
indent_after_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_before_problem: fn(Position) -> E,
|
||||
indent_after_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>, E>,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
|
@ -36,14 +36,14 @@ where
|
|||
pub fn space0_before_optional_after<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_before_problem: fn(Row, Col) -> E,
|
||||
indent_after_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_before_problem: fn(Position) -> E,
|
||||
indent_after_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>, E>,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
|
@ -66,9 +66,9 @@ fn spaces_around_help<'a, S>(
|
|||
arena: &'a Bump,
|
||||
tuples: (
|
||||
&'a [CommentOrNewline<'a>],
|
||||
(Located<S>, &'a [CommentOrNewline<'a>]),
|
||||
(Loc<S>, &'a [CommentOrNewline<'a>]),
|
||||
),
|
||||
) -> Located<S>
|
||||
) -> Loc<S>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
|
@ -101,19 +101,19 @@ where
|
|||
pub fn space0_before_e<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>, E>,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
and!(space0_e(min_indent, space_problem, indent_problem), parser),
|
||||
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Located<S>)| {
|
||||
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
|
||||
if space_list.is_empty() {
|
||||
loc_expr
|
||||
} else {
|
||||
|
@ -128,19 +128,19 @@ where
|
|||
pub fn space0_after_e<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>, E>,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
and!(parser, space0_e(min_indent, space_problem, indent_problem)),
|
||||
|arena: &'a Bump, (loc_expr, space_list): (Located<S>, &'a [CommentOrNewline<'a>])| {
|
||||
|arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| {
|
||||
if space_list.is_empty() {
|
||||
loc_expr
|
||||
} else {
|
||||
|
@ -154,24 +154,24 @@ where
|
|||
|
||||
pub fn check_indent<'a, E>(
|
||||
min_indent: u16,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, (), E>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
move |_, state: State<'a>| {
|
||||
if state.column >= min_indent {
|
||||
if state.pos.column >= min_indent {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, indent_problem(state.line, state.column), state))
|
||||
Err((NoProgress, indent_problem(state.pos), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn space0_e<'a, E>(
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||
where
|
||||
E: 'a,
|
||||
|
@ -182,8 +182,8 @@ where
|
|||
#[inline(always)]
|
||||
fn spaces_help_help<'a, E>(
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
space_problem: fn(BadInputError, Position) -> E,
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||
where
|
||||
E: 'a,
|
||||
|
@ -193,48 +193,44 @@ where
|
|||
move |arena, mut state: State<'a>| {
|
||||
let comments_and_newlines = Vec::new_in(arena);
|
||||
|
||||
match eat_spaces(state.bytes, state.line, state.column, comments_and_newlines) {
|
||||
HasTab { row, col } => {
|
||||
match eat_spaces(state.bytes(), state.pos, comments_and_newlines) {
|
||||
HasTab(pos) => {
|
||||
// there was a tab character
|
||||
let mut state = state;
|
||||
state.pos = pos;
|
||||
// TODO: it _seems_ like if we're changing the line/column, we should also be
|
||||
// advancing the state by the corresponding number of bytes.
|
||||
// Not doing this is likely a bug!
|
||||
// state = state.advance(<something>);
|
||||
Err((
|
||||
MadeProgress,
|
||||
space_problem(BadInputError::HasTab, row, col),
|
||||
State {
|
||||
line: row,
|
||||
column: col,
|
||||
..state
|
||||
},
|
||||
space_problem(BadInputError::HasTab, pos),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Good {
|
||||
row,
|
||||
col,
|
||||
pos,
|
||||
bytes,
|
||||
comments_and_newlines,
|
||||
} => {
|
||||
if bytes == state.bytes {
|
||||
if bytes == state.bytes() {
|
||||
Ok((NoProgress, &[] as &[_], state))
|
||||
} else if state.line != row {
|
||||
} else if state.pos.line != pos.line {
|
||||
// we parsed at least one newline
|
||||
|
||||
state.indent_col = col;
|
||||
state.indent_column = pos.column;
|
||||
|
||||
if col >= min_indent {
|
||||
state.line = row;
|
||||
state.column = col;
|
||||
state.bytes = bytes;
|
||||
if pos.column >= min_indent {
|
||||
state.pos = pos;
|
||||
state = state.advance(state.bytes().len() - bytes.len());
|
||||
|
||||
Ok((MadeProgress, comments_and_newlines.into_bump_slice(), state))
|
||||
} else {
|
||||
Err((
|
||||
MadeProgress,
|
||||
indent_problem(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
Err((MadeProgress, indent_problem(state.pos), state))
|
||||
}
|
||||
} else {
|
||||
state.column = col;
|
||||
state.bytes = bytes;
|
||||
state.pos.column = pos.column;
|
||||
state = state.advance(state.bytes().len() - bytes.len());
|
||||
|
||||
Ok((MadeProgress, comments_and_newlines.into_bump_slice(), state))
|
||||
}
|
||||
|
@ -245,21 +241,16 @@ where
|
|||
|
||||
enum SpaceState<'a> {
|
||||
Good {
|
||||
row: Row,
|
||||
col: Col,
|
||||
pos: Position,
|
||||
bytes: &'a [u8],
|
||||
comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
||||
},
|
||||
HasTab {
|
||||
row: Row,
|
||||
col: Col,
|
||||
},
|
||||
HasTab(Position),
|
||||
}
|
||||
|
||||
fn eat_spaces<'a>(
|
||||
mut bytes: &'a [u8],
|
||||
mut row: Row,
|
||||
mut col: Col,
|
||||
mut pos: Position,
|
||||
mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
||||
) -> SpaceState<'a> {
|
||||
use SpaceState::*;
|
||||
|
@ -268,30 +259,30 @@ fn eat_spaces<'a>(
|
|||
match c {
|
||||
b' ' => {
|
||||
bytes = &bytes[1..];
|
||||
col += 1;
|
||||
pos.column += 1;
|
||||
}
|
||||
b'\n' => {
|
||||
bytes = &bytes[1..];
|
||||
row += 1;
|
||||
col = 0;
|
||||
pos.line += 1;
|
||||
pos.column = 0;
|
||||
comments_and_newlines.push(CommentOrNewline::Newline);
|
||||
}
|
||||
b'\r' => {
|
||||
bytes = &bytes[1..];
|
||||
}
|
||||
b'\t' => {
|
||||
return HasTab { row, col };
|
||||
return HasTab(pos);
|
||||
}
|
||||
b'#' => {
|
||||
return eat_line_comment(&bytes[1..], row, col + 1, comments_and_newlines);
|
||||
pos.column += 1;
|
||||
return eat_line_comment(&bytes[1..], pos, comments_and_newlines);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
Good {
|
||||
row,
|
||||
col,
|
||||
pos,
|
||||
bytes,
|
||||
comments_and_newlines,
|
||||
}
|
||||
|
@ -299,8 +290,7 @@ fn eat_spaces<'a>(
|
|||
|
||||
fn eat_line_comment<'a>(
|
||||
mut bytes: &'a [u8],
|
||||
row: Row,
|
||||
mut col: Col,
|
||||
mut pos: Position,
|
||||
mut comments_and_newlines: Vec<'a, CommentOrNewline<'a>>,
|
||||
) -> SpaceState<'a> {
|
||||
use SpaceState::*;
|
||||
|
@ -309,7 +299,7 @@ fn eat_line_comment<'a>(
|
|||
match bytes.get(1) {
|
||||
Some(b' ') => {
|
||||
bytes = &bytes[2..];
|
||||
col += 2;
|
||||
pos.column += 2;
|
||||
|
||||
true
|
||||
}
|
||||
|
@ -318,16 +308,17 @@ fn eat_line_comment<'a>(
|
|||
bytes = &bytes[2..];
|
||||
|
||||
comments_and_newlines.push(CommentOrNewline::DocComment(""));
|
||||
return eat_spaces(bytes, row + 1, 0, comments_and_newlines);
|
||||
pos.line += 1;
|
||||
pos.column = 0;
|
||||
return eat_spaces(bytes, pos, comments_and_newlines);
|
||||
}
|
||||
None => {
|
||||
// consume the second #
|
||||
col += 1;
|
||||
pos.column += 1;
|
||||
bytes = &bytes[1..];
|
||||
|
||||
return Good {
|
||||
row,
|
||||
col,
|
||||
pos,
|
||||
bytes,
|
||||
comments_and_newlines,
|
||||
};
|
||||
|
@ -340,13 +331,13 @@ fn eat_line_comment<'a>(
|
|||
};
|
||||
|
||||
let initial = bytes;
|
||||
let initial_col = col;
|
||||
let initial_column = pos.column;
|
||||
|
||||
for c in bytes {
|
||||
match c {
|
||||
b'\t' => return HasTab { row, col },
|
||||
b'\t' => return HasTab(pos),
|
||||
b'\n' => {
|
||||
let delta = (col - initial_col) as usize;
|
||||
let delta = (pos.column - initial_column) as usize;
|
||||
let comment = unsafe { std::str::from_utf8_unchecked(&initial[..delta]) };
|
||||
|
||||
if is_doc_comment {
|
||||
|
@ -354,17 +345,19 @@ fn eat_line_comment<'a>(
|
|||
} else {
|
||||
comments_and_newlines.push(CommentOrNewline::LineComment(comment));
|
||||
}
|
||||
return eat_spaces(&bytes[1..], row + 1, 0, comments_and_newlines);
|
||||
pos.line += 1;
|
||||
pos.column = 0;
|
||||
return eat_spaces(&bytes[1..], pos, comments_and_newlines);
|
||||
}
|
||||
_ => {
|
||||
bytes = &bytes[1..];
|
||||
col += 1;
|
||||
pos.column += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We made it to the end of the bytes. This means there's a comment without a trailing newline.
|
||||
let delta = (col - initial_col) as usize;
|
||||
let delta = (pos.column - initial_column) as usize;
|
||||
let comment = unsafe { std::str::from_utf8_unchecked(&initial[..delta]) };
|
||||
|
||||
if is_doc_comment {
|
||||
|
@ -374,8 +367,7 @@ fn eat_line_comment<'a>(
|
|||
}
|
||||
|
||||
Good {
|
||||
row,
|
||||
col,
|
||||
pos,
|
||||
bytes,
|
||||
comments_and_newlines,
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,20 +1,13 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
||||
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation};
|
||||
use crate::blankspace::space0_e;
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
specialize, word1, EPackageEntry, EPackageName, EPackageOrPath, Parser, State,
|
||||
};
|
||||
use crate::parser::Progress::*;
|
||||
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
||||
use crate::state::State;
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct PackageName<'a> {
|
||||
pub account: &'a str,
|
||||
pub pkg: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Version<'a> {
|
||||
Exact(&'a str),
|
||||
|
@ -33,10 +26,7 @@ pub enum VersionComparison {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum PackageOrPath<'a> {
|
||||
Package(PackageName<'a>, Version<'a>),
|
||||
Path(StrLiteral<'a>),
|
||||
}
|
||||
pub struct PackageName<'a>(pub &'a str);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ModuleName<'a>(&'a str);
|
||||
|
@ -57,11 +47,30 @@ impl<'a> ModuleName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ExposedName<'a>(&'a str);
|
||||
|
||||
impl<'a> From<ExposedName<'a>> for &'a str {
|
||||
fn from(name: ExposedName<'a>) -> Self {
|
||||
name.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExposedName<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
ExposedName(name)
|
||||
}
|
||||
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
|
@ -75,15 +84,15 @@ pub struct InterfaceHeader<'a> {
|
|||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum To<'a> {
|
||||
ExistingPackage(&'a str),
|
||||
NewPackage(PackageOrPath<'a>),
|
||||
NewPackage(PackageName<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -102,8 +111,8 @@ pub struct AppHeader<'a> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
|
||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -118,37 +127,25 @@ pub struct PackageHeader<'a> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PlatformRigid<'a> {
|
||||
Entry { rigid: &'a str, alias: &'a str },
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for PlatformRigid<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PlatformRigid::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PlatformRigid::SpaceAfter(self, spaces)
|
||||
}
|
||||
pub struct PlatformRigid<'a> {
|
||||
pub rigid: &'a str,
|
||||
pub alias: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformRequires<'a> {
|
||||
pub rigids: Collection<'a, Loc<PlatformRigid<'a>>>,
|
||||
pub signature: Loc<TypedIdent<'a>>,
|
||||
pub rigids: Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>,
|
||||
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: PlatformRequires<'a>,
|
||||
pub exposes: Collection<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Collection<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
|
@ -174,26 +171,7 @@ pub struct Effects<'a> {
|
|||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||
pub effect_shortname: &'a str,
|
||||
pub effect_type_name: &'a str,
|
||||
pub entries: Collection<'a, Loc<TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ExposesEntry<'a, T> {
|
||||
/// e.g. `Task`
|
||||
Exposed(T),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
pub entries: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -201,71 +179,35 @@ pub enum ImportsEntry<'a> {
|
|||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(
|
||||
ModuleName<'a>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
|
||||
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
|
||||
/// e.g. `pf.Task` or `pf.Task.{ after }` or `pf.{ Task.{ Task, after } }`
|
||||
Package(
|
||||
&'a str,
|
||||
ModuleName<'a>,
|
||||
Collection<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> ExposesEntry<'a, &'a str> {
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
use ExposesEntry::*;
|
||||
|
||||
match self {
|
||||
Exposed(string) => string,
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
||||
}
|
||||
}
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct TypedIdent<'a> {
|
||||
pub ident: Loc<&'a str>,
|
||||
pub spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
pub ann: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
Entry {
|
||||
ident: Loc<&'a str>,
|
||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
pub struct PackageEntry<'a> {
|
||||
pub shorthand: &'a str,
|
||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
pub package_name: Loc<PackageName<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum PackageEntry<'a> {
|
||||
Entry {
|
||||
shorthand: &'a str,
|
||||
spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
package_or_path: Loc<PackageOrPath<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for PackageEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a>> {
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||
move |arena, state| {
|
||||
// You may optionally have a package shorthand,
|
||||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||
|
@ -275,151 +217,46 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, EPackageEntry<'a
|
|||
|
||||
let (_, opt_shorthand, state) = maybe!(and!(
|
||||
skip_second!(
|
||||
specialize(|_, r, c| EPackageEntry::Shorthand(r, c), lowercase_ident()),
|
||||
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
|
||||
word1(b':', EPackageEntry::Colon)
|
||||
),
|
||||
space0_e(
|
||||
min_indent,
|
||||
EPackageEntry::Space,
|
||||
EPackageEntry::IndentPackageOrPath
|
||||
EPackageEntry::IndentPackage
|
||||
)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let (_, package_or_path, state) = loc!(specialize(
|
||||
EPackageEntry::BadPackageOrPath,
|
||||
package_or_path()
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
let (_, package_or_path, state) =
|
||||
loc!(specialize(EPackageEntry::BadPackage, package_name())).parse(arena, state)?;
|
||||
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
None => PackageEntry::Entry {
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
package_or_path,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
};
|
||||
|
||||
Ok((MadeProgress, entry, state))
|
||||
Ok((MadeProgress, Spaced::Item(entry), state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, EPackageOrPath<'a>> {
|
||||
one_of![
|
||||
map!(
|
||||
specialize(EPackageOrPath::BadPath, string_literal::parse()),
|
||||
PackageOrPath::Path
|
||||
),
|
||||
map!(
|
||||
and!(
|
||||
specialize(EPackageOrPath::BadPackage, package_name()),
|
||||
skip_first!(skip_spaces(), package_version())
|
||||
),
|
||||
|(name, version)| { PackageOrPath::Package(name, version) }
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
fn skip_spaces<'a, T>() -> impl Parser<'a, (), T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
|_, mut state: State<'a>| {
|
||||
let mut chomped = 0;
|
||||
let mut it = state.bytes.iter();
|
||||
|
||||
while let Some(b' ') = it.next() {
|
||||
chomped += 1;
|
||||
}
|
||||
|
||||
if chomped == 0 {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
state.column += chomped;
|
||||
state.bytes = it.as_slice();
|
||||
|
||||
Ok((MadeProgress, (), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn package_version<'a, T>() -> impl Parser<'a, Version<'a>, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
move |_, _| todo!("TODO parse package version")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName> {
|
||||
use encode_unicode::CharExt;
|
||||
// e.g. rtfeldman/blah
|
||||
//
|
||||
// Package names and accounts can be capitalized and can contain dashes.
|
||||
// They cannot contain underscores or other special characters.
|
||||
// They must be ASCII.
|
||||
|
||||
|_, mut state: State<'a>| match chomp_package_part(state.bytes) {
|
||||
Err(progress) => Err((
|
||||
progress,
|
||||
EPackageName::Account(state.line, state.column),
|
||||
state,
|
||||
)),
|
||||
Ok(account) => {
|
||||
let mut chomped = account.len();
|
||||
if let Ok(('/', width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) {
|
||||
chomped += width;
|
||||
match chomp_package_part(&state.bytes[chomped..]) {
|
||||
Err(progress) => Err((
|
||||
progress,
|
||||
EPackageName::Pkg(state.line, state.column + chomped as u16),
|
||||
state,
|
||||
)),
|
||||
Ok(pkg) => {
|
||||
chomped += pkg.len();
|
||||
|
||||
state.column += chomped as u16;
|
||||
state.bytes = &state.bytes[chomped..];
|
||||
|
||||
let value = PackageName { account, pkg };
|
||||
Ok((MadeProgress, value, state))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err((
|
||||
MadeProgress,
|
||||
EPackageName::MissingSlash(state.line, state.column + chomped as u16),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chomp_package_part(buffer: &[u8]) -> Result<&str, Progress> {
|
||||
use encode_unicode::CharExt;
|
||||
|
||||
let mut chomped = 0;
|
||||
|
||||
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
if ch == '-' || ch.is_ascii_alphanumeric() {
|
||||
chomped += width;
|
||||
} else {
|
||||
// we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if chomped == 0 {
|
||||
Err(Progress::NoProgress)
|
||||
} else {
|
||||
let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
|
||||
|
||||
Ok(name)
|
||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let pos = state.pos;
|
||||
specialize(EPackageName::BadPath, string_literal::parse())
|
||||
.parse(arena, state)
|
||||
.and_then(|(progress, text, next_state)| match text {
|
||||
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), next_state)),
|
||||
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(pos), next_state)),
|
||||
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(pos), next_state)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{BadInputError, Col, EExpr, ParseResult, Parser, Row, State};
|
||||
use crate::parser::{BadInputError, EExpr, ParseResult, Parser};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Position;
|
||||
|
||||
/// The parser accepts all of these in any position where any one of them could
|
||||
/// appear. This way, canonicalization can give more helpful error messages like
|
||||
|
@ -59,14 +61,14 @@ impl<'a> Ident<'a> {
|
|||
/// * A record field, e.g. "email" in `.email` or in `email:`
|
||||
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
|
||||
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||
move |_, state: State<'a>| match chomp_lowercase_part(state.bytes) {
|
||||
move |_, state: State<'a>| match chomp_lowercase_part(state.bytes()) {
|
||||
Err(progress) => Err((progress, (), state)),
|
||||
Ok(ident) => {
|
||||
if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) {
|
||||
Err((NoProgress, (), state))
|
||||
} else {
|
||||
let width = ident.len();
|
||||
match state.advance_without_indenting_ee(width, |_, _| ()) {
|
||||
match state.advance_without_indenting_ee(width, |_| ()) {
|
||||
Ok(state) => Ok((MadeProgress, ident, state)),
|
||||
Err(bad) => Err(bad),
|
||||
}
|
||||
|
@ -77,13 +79,13 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
|||
|
||||
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||
move |arena, state: State<'a>| {
|
||||
if state.bytes.starts_with(b"@") {
|
||||
match chomp_private_tag(state.bytes, state.line, state.column) {
|
||||
Err(BadIdent::Start(_, _)) => Err((NoProgress, (), state)),
|
||||
if state.bytes().starts_with(b"@") {
|
||||
match chomp_private_tag(state.bytes(), state.pos) {
|
||||
Err(BadIdent::Start(_)) => Err((NoProgress, (), state)),
|
||||
Err(_) => Err((MadeProgress, (), state)),
|
||||
Ok(ident) => {
|
||||
let width = ident.len();
|
||||
match state.advance_without_indenting_ee(width, |_, _| ()) {
|
||||
match state.advance_without_indenting_ee(width, |_| ()) {
|
||||
Ok(state) => Ok((MadeProgress, ident, state)),
|
||||
Err(bad) => Err(bad),
|
||||
}
|
||||
|
@ -101,11 +103,11 @@ pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
|
|||
/// * A type name
|
||||
/// * A global tag
|
||||
pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||
move |_, state: State<'a>| match chomp_uppercase_part(state.bytes) {
|
||||
move |_, state: State<'a>| match chomp_uppercase_part(state.bytes()) {
|
||||
Err(progress) => Err((progress, (), state)),
|
||||
Ok(ident) => {
|
||||
let width = ident.len();
|
||||
match state.advance_without_indenting_ee(width, |_, _| ()) {
|
||||
match state.advance_without_indenting_ee(width, |_| ()) {
|
||||
Ok(state) => Ok((MadeProgress, ident, state)),
|
||||
Err(bad) => Err(bad),
|
||||
}
|
||||
|
@ -114,14 +116,14 @@ pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
|||
}
|
||||
|
||||
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
||||
move |_, state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes) {
|
||||
move |_, state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes()) {
|
||||
Err(progress) => Err((progress, (), state)),
|
||||
Ok(ident) => {
|
||||
if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) {
|
||||
Err((MadeProgress, (), state))
|
||||
} else {
|
||||
let width = ident.len();
|
||||
match state.advance_without_indenting_ee(width, |_, _| ()) {
|
||||
match state.advance_without_indenting_ee(width, |_| ()) {
|
||||
Ok(state) => Ok((MadeProgress, ident, state)),
|
||||
Err(bad) => Err(bad),
|
||||
}
|
||||
|
@ -132,14 +134,14 @@ pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> {
|
|||
|
||||
macro_rules! advance_state {
|
||||
($state:expr, $n:expr) => {
|
||||
$state.advance_without_indenting_ee($n, |r, c| {
|
||||
BadIdent::Space(crate::parser::BadInputError::LineTooLong, r, c)
|
||||
$state.advance_without_indenting_ee($n, |pos| {
|
||||
BadIdent::Space(crate::parser::BadInputError::LineTooLong, pos)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse_ident<'a>(arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Ident<'a>, EExpr<'a>> {
|
||||
let initial = state;
|
||||
let initial = state.clone();
|
||||
|
||||
match parse_ident_help(arena, state) {
|
||||
Ok((progress, ident, state)) => {
|
||||
|
@ -148,11 +150,7 @@ pub fn parse_ident<'a>(arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Ide
|
|||
if let Some(first) = parts.first() {
|
||||
for keyword in crate::keyword::KEYWORDS.iter() {
|
||||
if first == keyword {
|
||||
return Err((
|
||||
NoProgress,
|
||||
EExpr::Start(initial.line, initial.column),
|
||||
initial,
|
||||
));
|
||||
return Err((NoProgress, EExpr::Start(initial.pos), initial));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,13 +159,11 @@ pub fn parse_ident<'a>(arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Ide
|
|||
|
||||
Ok((progress, ident, state))
|
||||
}
|
||||
Err((NoProgress, _, state)) => {
|
||||
Err((NoProgress, EExpr::Start(state.line, state.column), state))
|
||||
}
|
||||
Err((NoProgress, _, state)) => Err((NoProgress, EExpr::Start(state.pos), state)),
|
||||
Err((MadeProgress, fail, state)) => match fail {
|
||||
BadIdent::Start(r, c) => Err((NoProgress, EExpr::Start(r, c), state)),
|
||||
BadIdent::Space(e, r, c) => Err((NoProgress, EExpr::Space(e, r, c), state)),
|
||||
_ => malformed_identifier(initial.bytes, fail, state),
|
||||
BadIdent::Start(pos) => Err((NoProgress, EExpr::Start(pos), state)),
|
||||
BadIdent::Space(e, pos) => Err((NoProgress, EExpr::Space(e, pos), state)),
|
||||
_ => malformed_identifier(initial.bytes(), fail, state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -177,12 +173,12 @@ fn malformed_identifier<'a>(
|
|||
problem: BadIdent,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, Ident<'a>, EExpr<'a>> {
|
||||
let chomped = chomp_malformed(state.bytes);
|
||||
let delta = initial_bytes.len() - state.bytes.len();
|
||||
let chomped = chomp_malformed(state.bytes());
|
||||
let delta = initial_bytes.len() - state.bytes().len();
|
||||
let parsed_str = unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) };
|
||||
|
||||
state = state.advance_without_indenting_ee(chomped, |r, c| {
|
||||
EExpr::Space(crate::parser::BadInputError::LineTooLong, r, c)
|
||||
state = state.advance_without_indenting_ee(chomped, |pos| {
|
||||
EExpr::Space(crate::parser::BadInputError::LineTooLong, pos)
|
||||
})?;
|
||||
|
||||
Ok((MadeProgress, Ident::Malformed(parsed_str, problem), state))
|
||||
|
@ -208,16 +204,16 @@ pub fn chomp_malformed(bytes: &[u8]) -> usize {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BadIdent {
|
||||
Start(Row, Col),
|
||||
Space(BadInputError, Row, Col),
|
||||
Start(Position),
|
||||
Space(BadInputError, Position),
|
||||
|
||||
Underscore(Row, Col),
|
||||
QualifiedTag(Row, Col),
|
||||
WeirdAccessor(Row, Col),
|
||||
WeirdDotAccess(Row, Col),
|
||||
WeirdDotQualified(Row, Col),
|
||||
StrayDot(Row, Col),
|
||||
BadPrivateTag(Row, Col),
|
||||
Underscore(Position),
|
||||
QualifiedTag(Position),
|
||||
WeirdAccessor(Position),
|
||||
WeirdDotAccess(Position),
|
||||
WeirdDotQualified(Position),
|
||||
StrayDot(Position),
|
||||
BadPrivateTag(Position),
|
||||
}
|
||||
|
||||
fn chomp_lowercase_part(buffer: &[u8]) -> Result<&str, Progress> {
|
||||
|
@ -264,7 +260,7 @@ where
|
|||
}
|
||||
|
||||
/// a `.foo` accessor function
|
||||
fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> {
|
||||
fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||
// assumes the leading `.` has been chomped already
|
||||
use encode_unicode::CharExt;
|
||||
|
||||
|
@ -273,20 +269,23 @@ fn chomp_accessor(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> {
|
|||
let chomped = name.len();
|
||||
|
||||
if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
Err(BadIdent::WeirdAccessor(row, col))
|
||||
Err(BadIdent::WeirdAccessor(pos))
|
||||
} else {
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// we've already made progress with the initial `.`
|
||||
Err(BadIdent::StrayDot(row, col + 1))
|
||||
Err(BadIdent::StrayDot(Position {
|
||||
line: pos.line,
|
||||
column: pos.column + 1,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// a `@Token` private tag
|
||||
fn chomp_private_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent> {
|
||||
fn chomp_private_tag(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
|
||||
// assumes the leading `@` has NOT been chomped already
|
||||
debug_assert_eq!(buffer.get(0), Some(&b'@'));
|
||||
use encode_unicode::CharExt;
|
||||
|
@ -296,21 +295,23 @@ fn chomp_private_tag(buffer: &[u8], row: Row, col: Col) -> Result<&str, BadIdent
|
|||
let width = 1 + name.len();
|
||||
|
||||
if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[width..]) {
|
||||
Err(BadIdent::BadPrivateTag(row, col + width as u16))
|
||||
Err(BadIdent::BadPrivateTag(pos.bump_column(width as u16)))
|
||||
} else {
|
||||
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..width]) };
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
Err(_) => Err(BadIdent::BadPrivateTag(row, col + 1)),
|
||||
Err(_) => Err(BadIdent::BadPrivateTag(Position {
|
||||
line: pos.line,
|
||||
column: pos.column + 1,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn chomp_identifier_chain<'a>(
|
||||
arena: &'a Bump,
|
||||
buffer: &'a [u8],
|
||||
row: Row,
|
||||
col: Col,
|
||||
pos: Position,
|
||||
) -> Result<(u16, Ident<'a>), (u16, BadIdent)> {
|
||||
use encode_unicode::CharExt;
|
||||
|
||||
|
@ -319,7 +320,7 @@ fn chomp_identifier_chain<'a>(
|
|||
|
||||
match char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
Ok((ch, width)) => match ch {
|
||||
'.' => match chomp_accessor(&buffer[1..], row, col) {
|
||||
'.' => match chomp_accessor(&buffer[1..], pos) {
|
||||
Ok(accessor) => {
|
||||
let bytes_parsed = 1 + accessor.len();
|
||||
|
||||
|
@ -327,7 +328,7 @@ fn chomp_identifier_chain<'a>(
|
|||
}
|
||||
Err(fail) => return Err((1, fail)),
|
||||
},
|
||||
'@' => match chomp_private_tag(buffer, row, col) {
|
||||
'@' => match chomp_private_tag(buffer, pos) {
|
||||
Ok(tagname) => {
|
||||
let bytes_parsed = tagname.len();
|
||||
|
||||
|
@ -341,10 +342,10 @@ fn chomp_identifier_chain<'a>(
|
|||
first_is_uppercase = c.is_uppercase();
|
||||
}
|
||||
_ => {
|
||||
return Err((0, BadIdent::Start(row, col)));
|
||||
return Err((0, BadIdent::Start(pos)));
|
||||
}
|
||||
},
|
||||
Err(_) => return Err((0, BadIdent::Start(row, col))),
|
||||
Err(_) => return Err((0, BadIdent::Start(pos))),
|
||||
}
|
||||
|
||||
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
|
@ -390,15 +391,15 @@ fn chomp_identifier_chain<'a>(
|
|||
}
|
||||
Err(0) if !module_name.is_empty() => Err((
|
||||
chomped as u16,
|
||||
BadIdent::QualifiedTag(row, chomped as u16 + col),
|
||||
BadIdent::QualifiedTag(pos.bump_column(chomped as u16)),
|
||||
)),
|
||||
Err(1) if parts.is_empty() => Err((
|
||||
chomped as u16 + 1,
|
||||
BadIdent::WeirdDotQualified(row, chomped as u16 + col + 1),
|
||||
BadIdent::WeirdDotQualified(pos.bump_column(chomped as u16 + 1)),
|
||||
)),
|
||||
Err(width) => Err((
|
||||
chomped as u16 + width,
|
||||
BadIdent::WeirdDotAccess(row, chomped as u16 + col + width),
|
||||
BadIdent::WeirdDotAccess(pos.bump_column(chomped as u16 + width)),
|
||||
)),
|
||||
}
|
||||
} else if let Ok(('_', _)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
|
@ -407,7 +408,7 @@ fn chomp_identifier_chain<'a>(
|
|||
// to give good error messages for this case
|
||||
Err((
|
||||
chomped as u16 + 1,
|
||||
BadIdent::Underscore(row, col + chomped as u16 + 1),
|
||||
BadIdent::Underscore(pos.bump_column(chomped as u16 + 1)),
|
||||
))
|
||||
} else if first_is_uppercase {
|
||||
// just one segment, starting with an uppercase letter; that's a global tag
|
||||
|
@ -448,10 +449,10 @@ fn chomp_module_chain(buffer: &[u8]) -> Result<u16, Progress> {
|
|||
}
|
||||
|
||||
pub fn concrete_type<'a>() -> impl Parser<'a, (&'a str, &'a str), ()> {
|
||||
move |_, state: State<'a>| match chomp_concrete_type(state.bytes) {
|
||||
move |_, state: State<'a>| match chomp_concrete_type(state.bytes()) {
|
||||
Err(progress) => Err((progress, (), state)),
|
||||
Ok((module_name, type_name, width)) => {
|
||||
match state.advance_without_indenting_ee(width, |_, _| ()) {
|
||||
match state.advance_without_indenting_ee(width, |_| ()) {
|
||||
Ok(state) => Ok((MadeProgress, (module_name, type_name), state)),
|
||||
Err(bad) => Err(bad),
|
||||
}
|
||||
|
@ -527,7 +528,7 @@ fn parse_ident_help<'a>(
|
|||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, Ident<'a>, BadIdent> {
|
||||
match chomp_identifier_chain(arena, state.bytes, state.line, state.column) {
|
||||
match chomp_identifier_chain(arena, state.bytes(), state.pos) {
|
||||
Ok((width, ident)) => {
|
||||
state = advance_state!(state, width as usize)?;
|
||||
Ok((MadeProgress, ident, state))
|
||||
|
|
|
@ -14,6 +14,7 @@ pub mod module;
|
|||
pub mod number_literal;
|
||||
pub mod pattern;
|
||||
pub mod problems;
|
||||
pub mod state;
|
||||
pub mod string_literal;
|
||||
pub mod test_helpers;
|
||||
pub mod type_annotation;
|
||||
|
|
|
@ -1,44 +1,37 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Def, Module};
|
||||
use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::header::{
|
||||
package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
|
||||
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To,
|
||||
TypedIdent,
|
||||
package_entry, package_name, AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader,
|
||||
ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent,
|
||||
};
|
||||
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, specialize, word1, word2, Col, EEffects, EExposes, EHeader, EImports, EPackages,
|
||||
EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError,
|
||||
backtrackable, specialize, word1, word2, EEffects, EExposes, EHeader, EImports, EPackages,
|
||||
EProvides, ERequires, ETypedIdent, Parser, SyntaxError,
|
||||
};
|
||||
use crate::state::State;
|
||||
use crate::string_literal;
|
||||
use crate::type_annotation;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_region::all::Located;
|
||||
use roc_region::all::{Loc, Position};
|
||||
|
||||
fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
||||
|_arena, state: State<'a>| {
|
||||
if state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((
|
||||
NoProgress,
|
||||
SyntaxError::NotEndOfFile(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
Err((NoProgress, SyntaxError::NotEndOfFile(state.pos), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, SyntaxError<'a>> {
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, SyntaxError<'a>> {
|
||||
// force that we parse until the end of the input
|
||||
let min_indent = 0;
|
||||
skip_second!(
|
||||
specialize(
|
||||
|e, _, _| SyntaxError::Expr(e),
|
||||
crate::expr::defs(min_indent),
|
||||
),
|
||||
specialize(|e, _| SyntaxError::Expr(e), crate::expr::defs(min_indent),),
|
||||
end_of_file()
|
||||
)
|
||||
}
|
||||
|
@ -171,11 +164,11 @@ fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> {
|
|||
|
||||
#[inline(always)]
|
||||
fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
|
||||
|_, mut state: State<'a>| match chomp_module_name(state.bytes) {
|
||||
|_, mut state: State<'a>| match chomp_module_name(state.bytes()) {
|
||||
Ok(name) => {
|
||||
let width = name.len();
|
||||
state.column += width as u16;
|
||||
state.bytes = &state.bytes[width..];
|
||||
state.pos.column += width as u16;
|
||||
state = state.advance(width);
|
||||
|
||||
Ok((MadeProgress, ModuleName::new(name), state))
|
||||
}
|
||||
|
@ -220,7 +213,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
|||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
|
@ -303,8 +296,8 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
to: Located<To<'a>>,
|
||||
entries: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
to: Loc<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
|
@ -315,10 +308,10 @@ struct ProvidesTo<'a> {
|
|||
fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
|
||||
one_of![
|
||||
specialize(
|
||||
|_, r, c| EProvides::Identifier(r, c),
|
||||
|_, pos| EProvides::Identifier(pos),
|
||||
map!(lowercase_ident(), To::ExistingPackage)
|
||||
),
|
||||
specialize(EProvides::Package, map!(package_or_path(), To::NewPackage))
|
||||
specialize(EProvides::Package, map!(package_name(), To::NewPackage))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -362,7 +355,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EProvides<'a>,
|
||||
> {
|
||||
|
@ -385,22 +378,22 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
EProvides::Open,
|
||||
EProvides::Space,
|
||||
EProvides::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_entry<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, &'a str>>, E>
|
||||
) -> impl Parser<'a, Loc<Spaced<'a, ExposedName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Fn(Position) -> E,
|
||||
F: Copy,
|
||||
E: 'a,
|
||||
{
|
||||
loc!(map!(
|
||||
specialize(|_, r, c| to_expectation(r, c), unqualified_ident()),
|
||||
ExposesEntry::Exposed
|
||||
specialize(|_, pos| to_expectation(pos), unqualified_ident()),
|
||||
|n| Spaced::Item(ExposedName::new(n))
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -444,33 +437,33 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
|
|||
#[inline(always)]
|
||||
fn requires_rigids<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Collection<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
|
||||
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>, ERequires<'a>> {
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
|
||||
specialize(|_, pos| ERequires::Rigid(pos), loc!(requires_rigid())),
|
||||
word1(b',', ERequires::ListEnd),
|
||||
word1(b'}', ERequires::ListEnd),
|
||||
min_indent,
|
||||
ERequires::Open,
|
||||
ERequires::Space,
|
||||
ERequires::IndentListEnd,
|
||||
PlatformRigid::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, PlatformRigid<'a>, ()> {
|
||||
fn requires_rigid<'a>() -> impl Parser<'a, Spaced<'a, PlatformRigid<'a>>, ()> {
|
||||
map!(
|
||||
and!(
|
||||
lowercase_ident(),
|
||||
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
|
||||
skip_first!(word2(b'=', b'>', |_| ()), uppercase_ident())
|
||||
),
|
||||
|(rigid, alias)| PlatformRigid::Entry { rigid, alias }
|
||||
|(rigid, alias)| Spaced::Item(PlatformRigid { rigid, alias })
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<TypedIdent<'a>>, ERequires<'a>> {
|
||||
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
|
||||
skip_first!(
|
||||
word1(b'{', ERequires::ListStart),
|
||||
skip_second!(
|
||||
|
@ -491,7 +484,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -515,7 +508,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -523,10 +516,10 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
fn spaces_around_keyword<'a, E>(
|
||||
min_indent: u16,
|
||||
keyword: &'static str,
|
||||
expectation: fn(Row, Col) -> E,
|
||||
space_problem: fn(crate::parser::BadInputError, Row, Col) -> E,
|
||||
indent_problem1: fn(Row, Col) -> E,
|
||||
indent_problem2: fn(Row, Col) -> E,
|
||||
expectation: fn(Position) -> E,
|
||||
space_problem: fn(crate::parser::BadInputError, Position) -> E,
|
||||
indent_problem1: fn(Position) -> E,
|
||||
indent_problem2: fn(Position) -> E,
|
||||
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E>
|
||||
where
|
||||
E: 'a,
|
||||
|
@ -545,7 +538,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
EExposes,
|
||||
> {
|
||||
|
@ -569,28 +562,28 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
EExposes::Open,
|
||||
EExposes::Space,
|
||||
EExposes::IndentListEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_module<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Located<ExposesEntry<'a, ModuleName<'a>>>, E>
|
||||
) -> impl Parser<'a, Loc<Spaced<'a, ModuleName<'a>>>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Fn(Position) -> E,
|
||||
F: Copy,
|
||||
E: 'a,
|
||||
{
|
||||
loc!(map!(
|
||||
specialize(|_, r, c| to_expectation(r, c), module_name()),
|
||||
ExposesEntry::Exposed
|
||||
specialize(|_, pos| to_expectation(pos), module_name()),
|
||||
Spaced::Item
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Packages<'a> {
|
||||
entries: Collection<'a, Located<PackageEntry<'a>>>,
|
||||
entries: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
@ -618,7 +611,7 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
|
|||
EPackages::Open,
|
||||
EPackages::Space,
|
||||
EPackages::IndentListEnd,
|
||||
PackageEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
),
|
||||
|((before_packages_keyword, after_packages_keyword), entries): (
|
||||
|
@ -639,7 +632,7 @@ fn imports<'a>() -> impl Parser<
|
|||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Located<ImportsEntry<'a>>>,
|
||||
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
),
|
||||
EImports,
|
||||
> {
|
||||
|
@ -663,7 +656,7 @@ fn imports<'a>() -> impl Parser<
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentListEnd,
|
||||
ImportsEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -686,14 +679,14 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
|
||||
// e.g. `fx.`
|
||||
let (_, type_shortname, state) = skip_second!(
|
||||
specialize(|_, r, c| EEffects::Shorthand(r, c), lowercase_ident()),
|
||||
specialize(|_, pos| EEffects::Shorthand(pos), lowercase_ident()),
|
||||
word1(b'.', EEffects::ShorthandDot)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// the type name, e.g. Effects
|
||||
let (_, (type_name, spaces_after_type_name), state) = and!(
|
||||
specialize(|_, r, c| EEffects::TypeName(r, c), uppercase_ident()),
|
||||
specialize(|_, pos| EEffects::TypeName(pos), uppercase_ident()),
|
||||
space0_e(min_indent, EEffects::Space, EEffects::IndentListStart)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
@ -706,7 +699,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
EEffects::Open,
|
||||
EEffects::Space,
|
||||
EEffects::IndentListEnd,
|
||||
TypedIdent::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
@ -726,7 +719,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
||||
fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
// e.g.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
|
@ -736,7 +729,7 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
|||
and!(
|
||||
and!(
|
||||
loc!(specialize(
|
||||
|_, r, c| ETypedIdent::Identifier(r, c),
|
||||
|_, pos| ETypedIdent::Identifier(pos),
|
||||
lowercase_ident()
|
||||
)),
|
||||
space0_e(min_indent, ETypedIdent::Space, ETypedIdent::IndentHasType)
|
||||
|
@ -744,7 +737,10 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
|||
skip_first!(
|
||||
word1(b':', ETypedIdent::HasType),
|
||||
space0_before_e(
|
||||
specialize(ETypedIdent::Type, type_annotation::located_help(min_indent)),
|
||||
specialize(
|
||||
ETypedIdent::Type,
|
||||
type_annotation::located_help(min_indent, true)
|
||||
),
|
||||
min_indent,
|
||||
ETypedIdent::Space,
|
||||
ETypedIdent::IndentType,
|
||||
|
@ -752,41 +748,41 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
|
|||
)
|
||||
),
|
||||
|((ident, spaces_before_colon), ann)| {
|
||||
TypedIdent::Entry {
|
||||
Spaced::Item(TypedIdent {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
ann,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> {
|
||||
specialize(|_, r, c| EImports::Shorthand(r, c), lowercase_ident())
|
||||
specialize(|_, pos| EImports::Shorthand(pos), lowercase_ident())
|
||||
}
|
||||
|
||||
fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
|
||||
where
|
||||
F: Fn(crate::parser::Row, crate::parser::Col) -> E,
|
||||
F: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
F: 'a,
|
||||
{
|
||||
specialize(move |_, r, c| to_expectation(r, c), module_name())
|
||||
specialize(move |_, pos| to_expectation(pos), module_name())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
||||
fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> {
|
||||
let min_indent = 1;
|
||||
|
||||
type Temp<'a> = (
|
||||
(Option<&'a str>, ModuleName<'a>),
|
||||
Option<Collection<'a, Located<ExposesEntry<'a, &'a str>>>>,
|
||||
Option<Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
);
|
||||
|
||||
map_with_arena!(
|
||||
and!(
|
||||
and!(
|
||||
// e.g. `base.`
|
||||
// e.g. `pf.`
|
||||
maybe!(skip_second!(
|
||||
shortname(),
|
||||
word1(b'.', EImports::ShorthandDot)
|
||||
|
@ -806,18 +802,20 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, EImports> {
|
|||
EImports::Open,
|
||||
EImports::Space,
|
||||
EImports::IndentSetEnd,
|
||||
ExposesEntry::SpaceBefore
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
))
|
||||
),
|
||||
|_arena, ((opt_shortname, module_name), opt_values): Temp<'a>| {
|
||||
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
|
||||
|
||||
match opt_shortname {
|
||||
let entry = match opt_shortname {
|
||||
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
|
||||
|
||||
None => ImportsEntry::Module(module_name, exposed_values),
|
||||
}
|
||||
};
|
||||
|
||||
Spaced::Item(entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::ast::Base;
|
||||
use crate::parser::{ENumber, ParseResult, Parser, Progress, State};
|
||||
use crate::parser::{ENumber, ParseResult, Parser, Progress};
|
||||
use crate::state::State;
|
||||
|
||||
pub enum NumLiteral<'a> {
|
||||
Float(&'a str),
|
||||
|
@ -13,9 +14,9 @@ pub enum NumLiteral<'a> {
|
|||
|
||||
pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
|
||||
move |_arena, state: State<'a>| {
|
||||
match state.bytes.get(0) {
|
||||
match state.bytes().get(0) {
|
||||
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
|
||||
parse_number_base(false, state.bytes, state)
|
||||
parse_number_base(false, state.bytes(), state)
|
||||
}
|
||||
_ => {
|
||||
// this is not a number at all
|
||||
|
@ -27,13 +28,13 @@ pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber>
|
|||
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
|
||||
move |_arena, state: State<'a>| {
|
||||
match state.bytes.get(0) {
|
||||
match state.bytes().get(0) {
|
||||
Some(first_byte) if *first_byte == b'-' => {
|
||||
// drop the minus
|
||||
parse_number_base(true, &state.bytes[1..], state)
|
||||
parse_number_base(true, &state.bytes()[1..], state)
|
||||
}
|
||||
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
|
||||
parse_number_base(false, state.bytes, state)
|
||||
parse_number_base(false, state.bytes(), state)
|
||||
}
|
||||
_ => {
|
||||
// this is not a number at all
|
||||
|
@ -66,7 +67,7 @@ fn chomp_number_base<'a>(
|
|||
|
||||
let string = unsafe { std::str::from_utf8_unchecked(&bytes[..chomped]) };
|
||||
|
||||
let new = state.advance_without_indenting_ee(chomped + 2 + is_negative as usize, |_, _| {
|
||||
let new = state.advance_without_indenting_ee(chomped + 2 + is_negative as usize, |_| {
|
||||
ENumber::LineTooLong
|
||||
})?;
|
||||
|
||||
|
@ -99,11 +100,10 @@ fn chomp_number_dec<'a>(
|
|||
}
|
||||
|
||||
let string =
|
||||
unsafe { std::str::from_utf8_unchecked(&state.bytes[0..chomped + is_negative as usize]) };
|
||||
unsafe { std::str::from_utf8_unchecked(&state.bytes()[0..chomped + is_negative as usize]) };
|
||||
|
||||
let new = state.advance_without_indenting_ee(chomped + is_negative as usize, |_, _| {
|
||||
ENumber::LineTooLong
|
||||
})?;
|
||||
let new = state
|
||||
.advance_without_indenting_ee(chomped + is_negative as usize, |_| ENumber::LineTooLong)?;
|
||||
|
||||
Ok((
|
||||
Progress::MadeProgress,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,12 +4,13 @@ use crate::ident::{lowercase_ident, parse_ident, Ident};
|
|||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, optional, specialize, specialize_ref, word1, EPattern, PInParens, PRecord,
|
||||
ParseResult, Parser, State,
|
||||
ParseResult, Parser,
|
||||
};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
/// Different patterns are supported in different circumstances.
|
||||
/// For example, when branches can pattern match on number literals, but
|
||||
|
@ -23,9 +24,7 @@ pub enum PatternType {
|
|||
WhenBranch,
|
||||
}
|
||||
|
||||
pub fn loc_closure_param<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
pub fn loc_closure_param<'a>(min_indent: u16) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
move |arena, state| parse_closure_param(arena, state, min_indent)
|
||||
}
|
||||
|
||||
|
@ -33,7 +32,7 @@ fn parse_closure_param<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
min_indent: u16,
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
// An ident is the most common param, e.g. \foo -> ...
|
||||
loc_ident_pattern_help(min_indent, true),
|
||||
|
@ -51,9 +50,7 @@ fn parse_closure_param<'a>(
|
|||
.parse(arena, state)
|
||||
}
|
||||
|
||||
pub fn loc_pattern_help<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
pub fn loc_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent)),
|
||||
loc!(underscore_pattern_help()),
|
||||
|
@ -69,11 +66,11 @@ pub fn loc_pattern_help<'a>(
|
|||
|
||||
fn loc_tag_pattern_args_help<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>, EPattern<'a>> {
|
||||
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
|
||||
zero_or_more!(loc_tag_pattern_arg(min_indent))
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
// Don't parse operators, because they have a higher precedence than function application.
|
||||
// If we encounter one, we're done parsing function args!
|
||||
move |arena, state| {
|
||||
|
@ -83,14 +80,14 @@ fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'
|
|||
|
||||
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
|
||||
|
||||
let Located { region, value } = loc_pat;
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
if spaces.is_empty() {
|
||||
Located::at(region, value)
|
||||
Loc::at(region, value)
|
||||
} else {
|
||||
Located::at(region, Pattern::SpaceBefore(arena.alloc(value), spaces))
|
||||
Loc::at(region, Pattern::SpaceBefore(arena.alloc(value), spaces))
|
||||
},
|
||||
state,
|
||||
))
|
||||
|
@ -101,7 +98,7 @@ fn loc_parse_tag_pattern_arg<'a>(
|
|||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent)),
|
||||
loc!(underscore_pattern_help()),
|
||||
|
@ -119,7 +116,7 @@ fn loc_parse_tag_pattern_arg<'a>(
|
|||
|
||||
fn loc_pattern_in_parens_help<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, PInParens<'a>> {
|
||||
) -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', PInParens::Open),
|
||||
space0_around_ee(
|
||||
|
@ -159,7 +156,7 @@ fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
|||
|
||||
fn string_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
||||
specialize(
|
||||
|_, r, c| EPattern::Start(r, c),
|
||||
|_, pos| EPattern::Start(pos),
|
||||
map!(crate::string_literal::parse(), Pattern::StrLiteral),
|
||||
)
|
||||
}
|
||||
|
@ -167,16 +164,16 @@ fn string_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
|||
fn loc_ident_pattern_help<'a>(
|
||||
min_indent: u16,
|
||||
can_have_arguments: bool,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let original_state = state;
|
||||
let original_state = state.clone();
|
||||
|
||||
let (_, loc_ident, state) =
|
||||
specialize(|_, r, c| EPattern::Start(r, c), loc!(parse_ident)).parse(arena, state)?;
|
||||
specialize(|_, pos| EPattern::Start(pos), loc!(parse_ident)).parse(arena, state)?;
|
||||
|
||||
match loc_ident.value {
|
||||
Ident::GlobalTag(tag) => {
|
||||
let loc_tag = Located {
|
||||
let loc_tag = Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::GlobalTag(tag),
|
||||
};
|
||||
|
@ -196,14 +193,14 @@ fn loc_ident_pattern_help<'a>(
|
|||
let value =
|
||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
|
||||
Ok((MadeProgress, Located { region, value }, state))
|
||||
Ok((MadeProgress, Loc { region, value }, state))
|
||||
}
|
||||
} else {
|
||||
Ok((MadeProgress, loc_tag, state))
|
||||
}
|
||||
}
|
||||
Ident::PrivateTag(tag) => {
|
||||
let loc_tag = Located {
|
||||
let loc_tag = Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::PrivateTag(tag),
|
||||
};
|
||||
|
@ -223,7 +220,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
let value =
|
||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
|
||||
Ok((MadeProgress, Located { region, value }, state))
|
||||
Ok((MadeProgress, Loc { region, value }, state))
|
||||
}
|
||||
} else {
|
||||
Ok((MadeProgress, loc_tag, state))
|
||||
|
@ -235,13 +232,13 @@ fn loc_ident_pattern_help<'a>(
|
|||
if crate::keyword::KEYWORDS.contains(&parts[0]) {
|
||||
Err((
|
||||
NoProgress,
|
||||
EPattern::End(original_state.line, original_state.column),
|
||||
EPattern::End(original_state.pos),
|
||||
original_state,
|
||||
))
|
||||
} else if module_name.is_empty() && parts.len() == 1 {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located {
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Identifier(parts[0]),
|
||||
},
|
||||
|
@ -255,7 +252,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
};
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located {
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Malformed(
|
||||
String::from_str_in(&malformed_str, arena).into_bump_str(),
|
||||
|
@ -267,7 +264,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
}
|
||||
Ident::AccessorFunction(string) => Ok((
|
||||
MadeProgress,
|
||||
Located {
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Malformed(string),
|
||||
},
|
||||
|
@ -278,7 +275,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located {
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::MalformedIdent(malformed, problem),
|
||||
},
|
||||
|
@ -307,10 +304,9 @@ fn lowercase_ident_pattern<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, &'a str, EPattern<'a>> {
|
||||
let row = state.line;
|
||||
let col = state.column;
|
||||
let pos = state.pos;
|
||||
|
||||
specialize(move |_, _, _| EPattern::End(row, col), lowercase_ident()).parse(arena, state)
|
||||
specialize(move |_, _| EPattern::End(pos), lowercase_ident()).parse(arena, state)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -337,16 +333,15 @@ fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRec
|
|||
}
|
||||
}
|
||||
|
||||
fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, PRecord<'a>> {
|
||||
fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>> {
|
||||
use crate::parser::Either::*;
|
||||
|
||||
move |arena, state: State<'a>| {
|
||||
// You must have a field name, e.g. "email"
|
||||
// using the initial row/col is important for error reporting
|
||||
let row = state.line;
|
||||
let col = state.column;
|
||||
// using the initial pos is important for error reporting
|
||||
let pos = state.pos;
|
||||
let (progress, loc_label, state) = loc!(specialize(
|
||||
move |_, _, _| PRecord::Field(row, col),
|
||||
move |_, _| PRecord::Field(pos),
|
||||
lowercase_ident()
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
@ -370,7 +365,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<
|
|||
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let Located {
|
||||
let Loc {
|
||||
value: label,
|
||||
region,
|
||||
} = loc_label;
|
||||
|
@ -379,7 +374,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<
|
|||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located::at(
|
||||
Loc::at(
|
||||
region,
|
||||
Pattern::RequiredField(
|
||||
label,
|
||||
|
@ -400,7 +395,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<
|
|||
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let Located {
|
||||
let Loc {
|
||||
value: label,
|
||||
region,
|
||||
} = loc_label;
|
||||
|
@ -409,7 +404,7 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<
|
|||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located::at(
|
||||
Loc::at(
|
||||
region,
|
||||
Pattern::OptionalField(
|
||||
label,
|
||||
|
@ -424,14 +419,14 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<
|
|||
// If no value was provided, record it as a Var.
|
||||
// Canonicalize will know what to do with a Var later.
|
||||
None => {
|
||||
let Located { value, region } = loc_label;
|
||||
let Loc { value, region } = loc_label;
|
||||
let value = if !spaces.is_empty() {
|
||||
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier(value)), spaces)
|
||||
} else {
|
||||
Pattern::Identifier(value)
|
||||
};
|
||||
|
||||
Ok((MadeProgress, Located::at(region, value), state))
|
||||
Ok((MadeProgress, Loc::at(region, value), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
146
compiler/parse/src/state.rs
Normal file
146
compiler/parse/src/state.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use crate::parser::Progress::*;
|
||||
use crate::parser::{BadInputError, Progress};
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::{Position, Region};
|
||||
use std::fmt;
|
||||
|
||||
/// A position in a source file.
|
||||
#[derive(Clone)]
|
||||
pub struct State<'a> {
|
||||
/// The raw input bytes from the file.
|
||||
bytes: &'a [u8],
|
||||
|
||||
/// Current position within the input (line/column)
|
||||
pub pos: Position,
|
||||
|
||||
/// Current indentation level, in columns
|
||||
/// (so no indent is col 1 - this saves an arithmetic operation.)
|
||||
pub indent_column: u16,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new(bytes: &'a [u8]) -> State<'a> {
|
||||
State {
|
||||
bytes,
|
||||
pos: Position::default(),
|
||||
indent_column: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> &'a [u8] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn advance(&self, offset: usize) -> State<'a> {
|
||||
let mut state = self.clone();
|
||||
state.bytes = &state.bytes[offset..];
|
||||
state
|
||||
}
|
||||
|
||||
/// Returns the current position
|
||||
// TODO: replace this with just accessing the field
|
||||
pub const fn get_position(&self) -> Position {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Returns whether the parser has reached the end of the input
|
||||
pub const fn has_reached_end(&self) -> bool {
|
||||
self.bytes.is_empty()
|
||||
}
|
||||
|
||||
/// Use advance_spaces to advance with indenting.
|
||||
/// This assumes we are *not* advancing with spaces, or at least that
|
||||
/// any spaces on the line were preceded by non-spaces - which would mean
|
||||
/// they weren't eligible to indent anyway.
|
||||
pub fn advance_without_indenting_e<TE, E>(
|
||||
self,
|
||||
quantity: usize,
|
||||
to_error: TE,
|
||||
) -> Result<Self, (Progress, E, Self)>
|
||||
where
|
||||
TE: Fn(BadInputError, Position) -> E,
|
||||
{
|
||||
self.advance_without_indenting_ee(quantity, |p| to_error(BadInputError::LineTooLong, p))
|
||||
}
|
||||
|
||||
pub fn advance_without_indenting_ee<TE, E>(
|
||||
self,
|
||||
quantity: usize,
|
||||
to_error: TE,
|
||||
) -> Result<Self, (Progress, E, Self)>
|
||||
where
|
||||
TE: Fn(Position) -> E,
|
||||
{
|
||||
match (self.pos.column as usize).checked_add(quantity) {
|
||||
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
||||
Ok(State {
|
||||
bytes: &self.bytes[quantity..],
|
||||
pos: Position {
|
||||
line: self.pos.line,
|
||||
column: column_usize as u16,
|
||||
},
|
||||
// Once we hit a nonspace character, we are no longer indenting.
|
||||
..self
|
||||
})
|
||||
}
|
||||
_ => Err((NoProgress, to_error(self.pos), self)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a Region corresponding to the current state, but
|
||||
/// with the the end column advanced by the given amount. This is
|
||||
/// useful when parsing something "manually" (using input.chars())
|
||||
/// and thus wanting a Region while not having access to loc().
|
||||
pub fn len_region(&self, length: u16) -> Region {
|
||||
Region::new(
|
||||
self.pos,
|
||||
Position {
|
||||
line: self.pos.line,
|
||||
column: self
|
||||
.pos
|
||||
.column
|
||||
.checked_add(length)
|
||||
.unwrap_or_else(|| panic!("len_region overflowed")),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a failing ParseResult for the given FailReason
|
||||
pub fn fail<T, X>(
|
||||
self,
|
||||
_arena: &'a Bump,
|
||||
progress: Progress,
|
||||
reason: X,
|
||||
) -> Result<(Progress, T, Self), (Progress, X, Self)> {
|
||||
Err((progress, reason, self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for State<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "State {{")?;
|
||||
|
||||
match std::str::from_utf8(self.bytes) {
|
||||
Ok(string) => write!(f, "\n\tbytes: [utf8] {:?}", string)?,
|
||||
Err(_) => write!(f, "\n\tbytes: [invalid utf8] {:?}", self.bytes)?,
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"\n\t(line, col): ({}, {}),",
|
||||
self.pos.line, self.pos.column
|
||||
)?;
|
||||
write!(f, "\n\tindent_column: {}", self.indent_column)?;
|
||||
write!(f, "\n}}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_size() {
|
||||
// State should always be under 8 machine words, so it fits in a typical
|
||||
// cache line.
|
||||
let state_size = std::mem::size_of::<State>();
|
||||
let maximum = std::mem::size_of::<usize>() * 8;
|
||||
assert!(state_size <= maximum, "{:?} <= {:?}", state_size, maximum);
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use crate::ast::{EscapedChar, StrLiteral, StrSegment};
|
||||
use crate::expr;
|
||||
use crate::parser::Progress::*;
|
||||
use crate::parser::{allocated, loc, specialize_ref, word1, BadInputError, EString, Parser, State};
|
||||
use crate::parser::{allocated, loc, specialize_ref, word1, BadInputError, EString, Parser};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
||||
|
@ -11,38 +12,29 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
|||
move |arena, state: State<'a>| {
|
||||
let mut buf = bumpalo::collections::String::new_in(arena);
|
||||
|
||||
for &byte in state.bytes.iter() {
|
||||
for &byte in state.bytes().iter() {
|
||||
if (byte as char).is_ascii_hexdigit() {
|
||||
buf.push(byte as char);
|
||||
} else if buf.is_empty() {
|
||||
// We didn't find any hex digits!
|
||||
return Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
return Err((NoProgress, EString::CodePtEnd(state.pos), state));
|
||||
} else {
|
||||
let state = state.advance_without_indenting_ee(buf.len(), |r, c| {
|
||||
EString::Space(BadInputError::LineTooLong, r, c)
|
||||
let state = state.advance_without_indenting_ee(buf.len(), |pos| {
|
||||
EString::Space(BadInputError::LineTooLong, pos)
|
||||
})?;
|
||||
|
||||
return Ok((MadeProgress, buf.into_bump_str(), state));
|
||||
}
|
||||
}
|
||||
|
||||
Err((
|
||||
NoProgress,
|
||||
EString::CodePtEnd(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
Err((NoProgress, EString::CodePtEnd(state.pos), state))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! advance_state {
|
||||
($state:expr, $n:expr) => {
|
||||
$state.advance_without_indenting_ee($n, |r, c| {
|
||||
EString::Space(BadInputError::LineTooLong, r, c)
|
||||
})
|
||||
$state
|
||||
.advance_without_indenting_ee($n, |pos| EString::Space(BadInputError::LineTooLong, pos))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -53,18 +45,18 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
let is_multiline;
|
||||
let mut bytes;
|
||||
|
||||
if state.bytes.starts_with(b"\"\"\"") {
|
||||
if state.bytes().starts_with(b"\"\"\"") {
|
||||
// we will be parsing a multi-string
|
||||
is_multiline = true;
|
||||
bytes = state.bytes[3..].iter();
|
||||
bytes = state.bytes()[3..].iter();
|
||||
state = advance_state!(state, 3)?;
|
||||
} else if state.bytes.starts_with(b"\"") {
|
||||
} else if state.bytes().starts_with(b"\"") {
|
||||
// we will be parsing a single-string
|
||||
is_multiline = false;
|
||||
bytes = state.bytes[1..].iter();
|
||||
bytes = state.bytes()[1..].iter();
|
||||
state = advance_state!(state, 1)?;
|
||||
} else {
|
||||
return Err((NoProgress, EString::Open(state.line, state.column), state));
|
||||
return Err((NoProgress, EString::Open(state.pos), state));
|
||||
}
|
||||
|
||||
// At the parsing stage we keep the entire raw string, because the formatter
|
||||
|
@ -97,7 +89,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// something which signalled that we should end the
|
||||
// current segment - so use segment_parsed_bytes - 1 here,
|
||||
// to exclude that char we just parsed.
|
||||
let string_bytes = &state.bytes[0..(segment_parsed_bytes - 1)];
|
||||
let string_bytes = &state.bytes()[0..(segment_parsed_bytes - 1)];
|
||||
|
||||
match std::str::from_utf8(string_bytes) {
|
||||
Ok(string) => {
|
||||
|
@ -108,7 +100,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
Err(_) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EString::Space(BadInputError::BadUtf8, state.line, state.column),
|
||||
EString::Space(BadInputError::BadUtf8, state.pos),
|
||||
state,
|
||||
));
|
||||
}
|
||||
|
@ -200,11 +192,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// all remaining chars. This will mask all other errors, but
|
||||
// it should make it easiest to debug; the file will be a giant
|
||||
// error starting from where the open quote appeared.
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EString::EndlessSingle(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
return Err((MadeProgress, EString::EndlessSingle(state.pos), state));
|
||||
}
|
||||
}
|
||||
b'\\' => {
|
||||
|
@ -224,7 +212,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// Advance past the `\(` before using the expr parser
|
||||
state = advance_state!(state, 2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
let original_byte_count = state.bytes().len();
|
||||
|
||||
// This is an interpolated variable.
|
||||
// Parse an arbitrary expression, then give a
|
||||
|
@ -237,7 +225,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
.parse(arena, state)?;
|
||||
|
||||
// Advance the iterator past the expr we just parsed.
|
||||
for _ in 0..(original_byte_count - new_state.bytes.len()) {
|
||||
for _ in 0..(original_byte_count - new_state.bytes().len()) {
|
||||
bytes.next();
|
||||
}
|
||||
|
||||
|
@ -251,7 +239,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// Advance past the `\u` before using the expr parser
|
||||
state = advance_state!(state, 2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
let original_byte_count = state.bytes().len();
|
||||
|
||||
// Parse the hex digits, surrounded by parens, then
|
||||
// give a canonicalization error if the digits form
|
||||
|
@ -264,7 +252,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
.parse(arena, state)?;
|
||||
|
||||
// Advance the iterator past the expr we just parsed.
|
||||
for _ in 0..(original_byte_count - new_state.bytes.len()) {
|
||||
for _ in 0..(original_byte_count - new_state.bytes().len()) {
|
||||
bytes.next();
|
||||
}
|
||||
|
||||
|
@ -293,11 +281,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// Invalid escape! A backslash must be followed
|
||||
// by either an open paren or else one of the
|
||||
// escapable characters (\n, \t, \", \\, etc)
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EString::UnknownEscape(state.line, state.column),
|
||||
state,
|
||||
));
|
||||
return Err((MadeProgress, EString::UnknownEscape(state.pos), state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,9 +295,9 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
Err((
|
||||
MadeProgress,
|
||||
if is_multiline {
|
||||
EString::EndlessMulti(state.line, state.column)
|
||||
EString::EndlessMulti(state.pos)
|
||||
} else {
|
||||
EString::EndlessSingle(state.line, state.column)
|
||||
EString::EndlessSingle(state.pos)
|
||||
},
|
||||
state,
|
||||
))
|
||||
|
|
|
@ -2,10 +2,11 @@ use crate::ast;
|
|||
use crate::module::module_defs;
|
||||
// use crate::module::module_defs;
|
||||
use crate::parser::Parser;
|
||||
use crate::parser::{State, SyntaxError};
|
||||
use crate::parser::SyntaxError;
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
pub fn parse_expr_with<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -18,7 +19,7 @@ pub fn parse_expr_with<'a>(
|
|||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, SyntaxError<'a>> {
|
||||
) -> Result<Loc<ast::Expr<'a>>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
|
||||
match crate::expr::test_parse_expr(0, arena, state) {
|
||||
|
@ -30,7 +31,7 @@ pub fn parse_loc_with<'a>(
|
|||
pub fn parse_defs_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<BumpVec<'a, Located<ast::Def<'a>>>, SyntaxError<'a>> {
|
||||
) -> Result<BumpVec<'a, Loc<ast::Def<'a>>>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
|
||||
match module_defs().parse(arena, state) {
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
use crate::ast::{AssignedField, Tag, TypeAnnotation};
|
||||
use crate::ast::{AliasHeader, AssignedField, Pattern, Tag, TypeAnnotation};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, EType,
|
||||
ETypeApply, ETypeInParens, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
|
||||
ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
|
||||
Progress::{self, *},
|
||||
State,
|
||||
};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
pub fn located_help<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
expression(min_indent)
|
||||
is_trailing_comma_valid: bool,
|
||||
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
expression(min_indent, is_trailing_comma_valid)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -46,11 +47,64 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ET
|
|||
}
|
||||
}
|
||||
|
||||
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|
||||
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.line, state.column), state))
|
||||
fn check_type_alias(
|
||||
p: Progress,
|
||||
annot: Loc<TypeAnnotation>,
|
||||
) -> impl Parser<AliasHeader, ETypeInlineAlias> {
|
||||
move |arena, state| match annot.value {
|
||||
TypeAnnotation::Apply("", tag_name, vars) => {
|
||||
let mut var_names = Vec::new_in(arena);
|
||||
var_names.reserve(vars.len());
|
||||
for var in vars {
|
||||
if let TypeAnnotation::BoundVariable(v) = var.value {
|
||||
var_names.push(Loc::at(var.region, Pattern::Identifier(v)));
|
||||
} else {
|
||||
return Err((
|
||||
p,
|
||||
ETypeInlineAlias::ArgumentNotLowercase(var.region.start()),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let name_start = annot.region.start();
|
||||
let name_region =
|
||||
Region::between(name_start, name_start.bump_column(tag_name.len() as u16));
|
||||
|
||||
let header = AliasHeader {
|
||||
name: Loc::at(name_region, tag_name),
|
||||
vars: var_names.into_bump_slice(),
|
||||
};
|
||||
|
||||
Ok((p, header, state))
|
||||
}
|
||||
TypeAnnotation::Apply(_, _, _) => {
|
||||
Err((p, ETypeInlineAlias::Qualified(annot.region.start()), state))
|
||||
}
|
||||
_ => Err((p, ETypeInlineAlias::NotAnAlias(annot.region.start()), state)),
|
||||
}
|
||||
}
|
||||
|
||||
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
fn parse_type_alias_after_as<'a>(min_indent: u16) -> impl Parser<'a, AliasHeader<'a>, EType<'a>> {
|
||||
move |arena, state| {
|
||||
space0_before_e(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
EType::TSpace,
|
||||
EType::TAsIndentStart,
|
||||
)
|
||||
.parse(arena, state)
|
||||
.and_then(|(p, annot, state)| {
|
||||
specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|
||||
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos), state))
|
||||
}
|
||||
|
||||
fn term<'a>(min_indent: u16) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
one_of!(
|
||||
|
@ -71,12 +125,7 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETy
|
|||
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentEnd)),
|
||||
crate::parser::keyword_e(keyword::AS, EType::TEnd)
|
||||
),
|
||||
space0_before_e(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
EType::TSpace,
|
||||
EType::TAsIndentStart
|
||||
)
|
||||
parse_type_alias_after_as(min_indent)
|
||||
),
|
||||
Some
|
||||
),
|
||||
|
@ -84,44 +133,39 @@ fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETy
|
|||
]
|
||||
),
|
||||
|arena: &'a Bump,
|
||||
(loc_ann, opt_as): (
|
||||
Located<TypeAnnotation<'a>>,
|
||||
Option<(&'a [_], Located<TypeAnnotation<'a>>)>
|
||||
)| {
|
||||
(loc_ann, opt_as): (Loc<TypeAnnotation<'a>>, Option<(&'a [_], AliasHeader<'a>)>)| {
|
||||
match opt_as {
|
||||
Some((spaces, loc_as)) => {
|
||||
let region = Region::span_across(&loc_ann.region, &loc_as.region);
|
||||
let value =
|
||||
TypeAnnotation::As(arena.alloc(loc_ann), spaces, arena.alloc(loc_as));
|
||||
Some((spaces, alias)) => {
|
||||
let alias_vars_region =
|
||||
Region::across_all(alias.vars.iter().map(|v| &v.region));
|
||||
let region = Region::span_across(&loc_ann.region, &alias_vars_region);
|
||||
let value = TypeAnnotation::As(arena.alloc(loc_ann), spaces, alias);
|
||||
|
||||
Located { region, value }
|
||||
Loc { region, value }
|
||||
}
|
||||
|
||||
None => loc_ann,
|
||||
}
|
||||
}
|
||||
)
|
||||
.trace("type_annotation:term")
|
||||
}
|
||||
|
||||
/// The `*` type variable, e.g. in (List *) Wildcard,
|
||||
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
map!(loc!(word1(b'*', EType::TWildcard)), |loc_val: Located<
|
||||
(),
|
||||
>| {
|
||||
fn loc_wildcard<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
map!(loc!(word1(b'*', EType::TWildcard)), |loc_val: Loc<()>| {
|
||||
loc_val.map(|_| TypeAnnotation::Wildcard)
|
||||
})
|
||||
}
|
||||
|
||||
/// The `_` indicating an inferred type, e.g. in (List _)
|
||||
fn loc_inferred<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
map!(loc!(word1(b'_', EType::TInferred)), |loc_val: Located<
|
||||
(),
|
||||
>| {
|
||||
fn loc_inferred<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
map!(loc!(word1(b'_', EType::TInferred)), |loc_val: Loc<()>| {
|
||||
loc_val.map(|_| TypeAnnotation::Inferred)
|
||||
})
|
||||
}
|
||||
|
||||
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
use crate::ast::Spaceable;
|
||||
|
||||
map_with_arena!(
|
||||
|
@ -137,11 +181,11 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
|
|||
loc!(parse_type_variable)
|
||||
)
|
||||
),
|
||||
|arena: &'a Bump, (spaces, argument): (&'a [_], Located<TypeAnnotation<'a>>)| {
|
||||
|arena: &'a Bump, (spaces, argument): (&'a [_], Loc<TypeAnnotation<'a>>)| {
|
||||
if spaces.is_empty() {
|
||||
argument
|
||||
} else {
|
||||
let Located { region, value } = argument;
|
||||
let Loc { region, value } = argument;
|
||||
arena.alloc(value).with_spaces_before(spaces, region)
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +194,11 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
|
|||
|
||||
fn loc_type_in_parens<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, ETypeInParens<'a>> {
|
||||
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', ETypeInParens::Open),
|
||||
space0_around_ee(
|
||||
move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent))
|
||||
move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent, true))
|
||||
.parse(arena, state),
|
||||
min_indent,
|
||||
ETypeInParens::Space,
|
||||
|
@ -189,15 +233,14 @@ fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>>
|
|||
}
|
||||
}
|
||||
|
||||
use crate::parser::{Col, Row};
|
||||
fn parse_tag_name<'a, F, E>(to_problem: F) -> impl Parser<'a, &'a str, E>
|
||||
where
|
||||
F: Fn(Row, Col) -> E,
|
||||
F: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
move |arena, state: State<'a>| match crate::ident::tag_name().parse(arena, state) {
|
||||
Ok(good) => Ok(good),
|
||||
Err((progress, _, state)) => Err((progress, to_problem(state.line, state.column), state)),
|
||||
Err((progress, _, state)) => Err((progress, to_problem(state.pos), state)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,13 +251,12 @@ fn record_type_field<'a>(
|
|||
use crate::parser::Either::*;
|
||||
use AssignedField::*;
|
||||
|
||||
move |arena, state: State<'a>| {
|
||||
(move |arena, state: State<'a>| {
|
||||
// You must have a field name, e.g. "email"
|
||||
// using the initial row/col is important for error reporting
|
||||
let row = state.line;
|
||||
let col = state.column;
|
||||
// using the initial pos is important for error reporting
|
||||
let pos = state.pos;
|
||||
let (progress, loc_label, state) = loc!(specialize(
|
||||
move |_, _, _| ETypeRecord::Field(row, col),
|
||||
move |_, _| ETypeRecord::Field(pos),
|
||||
lowercase_ident()
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
@ -231,7 +273,7 @@ fn record_type_field<'a>(
|
|||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let val_parser = specialize_ref(ETypeRecord::Type, term(min_indent));
|
||||
let val_parser = specialize_ref(ETypeRecord::Type, expression(min_indent, true));
|
||||
|
||||
match opt_loc_val {
|
||||
Some(First(_)) => {
|
||||
|
@ -276,14 +318,15 @@ fn record_type_field<'a>(
|
|||
Ok((MadeProgress, value, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.trace("type_annotation:record_type_field")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
|
||||
use crate::type_annotation::TypeAnnotation::*;
|
||||
|
||||
move |arena, state| {
|
||||
(move |arena, state| {
|
||||
let (_, fields, state) = collection_trailing_sep_e!(
|
||||
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
|
||||
word1(b'{', ETypeRecord::Open),
|
||||
|
@ -305,7 +348,8 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, EType
|
|||
let result = Record { fields, ext };
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
})
|
||||
.trace("type_annotation:record_type")
|
||||
}
|
||||
|
||||
fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
|
||||
|
@ -316,7 +360,7 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ETyp
|
|||
// e.g. `Str Float` in `Map Str Float`
|
||||
loc_applied_args_e(min_indent)
|
||||
),
|
||||
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Located<TypeAnnotation<'a>>>)| {
|
||||
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
|
||||
match &ctor {
|
||||
TypeAnnotation::Apply(module_name, name, _) => {
|
||||
if args.is_empty() {
|
||||
|
@ -331,16 +375,20 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, ETyp
|
|||
}
|
||||
}
|
||||
)
|
||||
.trace("type_annotation:applied_type")
|
||||
}
|
||||
|
||||
fn loc_applied_args_e<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, EType<'a>> {
|
||||
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
|
||||
zero_or_more!(loc_applied_arg(min_indent))
|
||||
}
|
||||
|
||||
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, EType<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
fn expression<'a>(
|
||||
min_indent: u16,
|
||||
is_trailing_comma_valid: bool,
|
||||
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
|
||||
(move |arena, state: State<'a>| {
|
||||
let (p1, first, state) = space0_before_e(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
|
@ -349,65 +397,82 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let (p2, rest, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', EType::TFunctionArgument),
|
||||
one_of![
|
||||
space0_around_ee(
|
||||
let result = and![
|
||||
zero_or_more!(skip_first!(
|
||||
word1(b',', EType::TFunctionArgument),
|
||||
one_of![
|
||||
space0_around_ee(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
EType::TSpace,
|
||||
EType::TIndentStart,
|
||||
EType::TIndentEnd
|
||||
),
|
||||
|_, state: State<'a>| Err((
|
||||
NoProgress,
|
||||
EType::TFunctionArgument(state.pos),
|
||||
state
|
||||
))
|
||||
]
|
||||
))
|
||||
.trace("type_annotation:expression:rest_args"),
|
||||
// TODO this space0 is dropped, so newlines just before the function arrow when there
|
||||
// is only one argument are not seen by the formatter. Can we do better?
|
||||
skip_second!(
|
||||
space0_e(min_indent, EType::TSpace, EType::TIndentStart),
|
||||
word2(b'-', b'>', EType::TStart)
|
||||
)
|
||||
.trace("type_annotation:expression:arrow")
|
||||
]
|
||||
.parse(arena, state.clone());
|
||||
|
||||
match result {
|
||||
Ok((p2, (rest, _dropped_spaces), state)) => {
|
||||
let (p3, return_type, state) = space0_before_e(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
EType::TSpace,
|
||||
EType::TIndentStart,
|
||||
EType::TIndentEnd
|
||||
),
|
||||
|_, state: State<'a>| Err((
|
||||
NoProgress,
|
||||
EType::TFunctionArgument(state.line, state.column),
|
||||
state
|
||||
))
|
||||
]
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// TODO this space0 is dropped, so newlines just before the function arrow when there
|
||||
// is only one argument are not seen by the formatter. Can we do better?
|
||||
let (p3, is_function, state) = optional(skip_first!(
|
||||
space0_e(min_indent, EType::TSpace, EType::TIndentStart),
|
||||
word2(b'-', b'>', EType::TStart)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
// prepare arguments
|
||||
let mut arguments = Vec::with_capacity_in(rest.len() + 1, arena);
|
||||
arguments.push(first);
|
||||
arguments.extend(rest);
|
||||
let output = arena.alloc(arguments);
|
||||
|
||||
if is_function.is_some() {
|
||||
let (p4, return_type, state) = space0_before_e(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
EType::TSpace,
|
||||
EType::TIndentStart,
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
let result = Loc {
|
||||
region: return_type.region,
|
||||
value: TypeAnnotation::Function(output, arena.alloc(return_type)),
|
||||
};
|
||||
let progress = p1.or(p2).or(p3);
|
||||
Ok((progress, result, state))
|
||||
}
|
||||
Err(err) => {
|
||||
if !is_trailing_comma_valid {
|
||||
let (_, comma, _) = optional(skip_first!(
|
||||
space0_e(min_indent, EType::TSpace, EType::TIndentStart),
|
||||
word1(b',', EType::TStart)
|
||||
))
|
||||
.trace("check trailing comma")
|
||||
.parse(arena, state.clone())?;
|
||||
|
||||
// prepare arguments
|
||||
let mut arguments = Vec::with_capacity_in(rest.len() + 1, arena);
|
||||
arguments.push(first);
|
||||
arguments.extend(rest);
|
||||
let output = arena.alloc(arguments);
|
||||
if comma.is_some() {
|
||||
// If the surrounding scope has declared that a trailing comma is not a valid state
|
||||
// for a type annotation - and we found one anyway - return an error so that we can
|
||||
// produce a more useful error message, knowing that the user was probably writing a
|
||||
// function type and messed up the syntax somehow.
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
let result = Located {
|
||||
region: return_type.region,
|
||||
value: TypeAnnotation::Function(output, arena.alloc(return_type)),
|
||||
};
|
||||
let progress = p1.or(p2).or(p3).or(p4);
|
||||
Ok((progress, result, state))
|
||||
} else {
|
||||
let progress = p1.or(p2).or(p3);
|
||||
// if there is no function arrow, there cannot be more than 1 "argument"
|
||||
if rest.is_empty() {
|
||||
Ok((progress, first, state))
|
||||
} else {
|
||||
// e.g. `Int,Int` without an arrow and return type
|
||||
panic!()
|
||||
// We ran into trouble parsing the function bits; just return the single term
|
||||
Ok((p1, first, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.trace("type_annotation:expression")
|
||||
}
|
||||
|
||||
/// Parse a basic type annotation that's a combination of variables
|
||||
|
@ -430,7 +495,7 @@ fn parse_concrete_type<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>, ETypeApply> {
|
||||
let initial_bytes = state.bytes;
|
||||
let initial_bytes = state.bytes();
|
||||
|
||||
match crate::ident::concrete_type().parse(arena, state) {
|
||||
Ok((_, (module_name, type_name), state)) => {
|
||||
|
@ -438,19 +503,17 @@ fn parse_concrete_type<'a>(
|
|||
|
||||
Ok((MadeProgress, answer, state))
|
||||
}
|
||||
Err((NoProgress, _, state)) => {
|
||||
Err((NoProgress, ETypeApply::End(state.line, state.column), state))
|
||||
}
|
||||
Err((NoProgress, _, state)) => Err((NoProgress, ETypeApply::End(state.pos), state)),
|
||||
Err((MadeProgress, _, mut state)) => {
|
||||
// we made some progress, but ultimately failed.
|
||||
// that means a malformed type name
|
||||
let chomped = crate::ident::chomp_malformed(state.bytes);
|
||||
let delta = initial_bytes.len() - state.bytes.len();
|
||||
let chomped = crate::ident::chomp_malformed(state.bytes());
|
||||
let delta = initial_bytes.len() - state.bytes().len();
|
||||
let parsed_str =
|
||||
unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) };
|
||||
|
||||
state = state.advance_without_indenting_ee(chomped, |r, c| {
|
||||
ETypeApply::Space(crate::parser::BadInputError::LineTooLong, r, c)
|
||||
state = state.advance_without_indenting_ee(chomped, |pos| {
|
||||
ETypeApply::Space(crate::parser::BadInputError::LineTooLong, pos)
|
||||
})?;
|
||||
|
||||
Ok((MadeProgress, TypeAnnotation::Malformed(parsed_str), state))
|
||||
|
@ -468,10 +531,6 @@ fn parse_type_variable<'a>(
|
|||
|
||||
Ok((MadeProgress, answer, state))
|
||||
}
|
||||
Err((progress, _, state)) => Err((
|
||||
progress,
|
||||
EType::TBadTypeVariable(state.line, state.column),
|
||||
state,
|
||||
)),
|
||||
Err((progress, _, state)) => Err((progress, EType::TBadTypeVariable(state.pos), state)),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue