mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
attempts at parsing
This commit is contained in:
parent
72518bca9c
commit
d174cb72ae
2 changed files with 408 additions and 18 deletions
|
@ -4,7 +4,7 @@ use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PlatformHeader};
|
|||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::soa::{EitherIndex, Slice};
|
||||
use roc_collections::soa::{EitherIndex, Index, Slice};
|
||||
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
|
@ -334,7 +334,7 @@ pub enum ValueDef<'a> {
|
|||
Expect(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct Defs<'a> {
|
||||
pub tags: std::vec::Vec<EitherIndex<TypeDef<'a>, ValueDef<'a>>>,
|
||||
pub regions: std::vec::Vec<Region>,
|
||||
|
@ -346,12 +346,62 @@ pub struct Defs<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Defs<'a> {
|
||||
pub fn defs(&self) -> impl Iterator<Item = Result<&TypeDef<'a>, &ValueDef<'a>>> {
|
||||
pub fn defs<I>(&self) -> impl Iterator<Item = Result<&TypeDef<'a>, &ValueDef<'a>>> {
|
||||
self.tags.iter().map(|tag| match tag.split() {
|
||||
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
|
||||
Err(value_index) => Err(&self.value_defs[value_index.index()]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<Result<&TypeDef<'a>, &ValueDef<'a>>> {
|
||||
self.tags.last().map(|tag| match tag.split() {
|
||||
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
|
||||
Err(value_index) => Err(&self.value_defs[value_index.index()]),
|
||||
})
|
||||
}
|
||||
|
||||
/// NOTE assumes the def itself is pushed already!
|
||||
fn push_def_help(
|
||||
&mut self,
|
||||
tag: EitherIndex<TypeDef<'a>, ValueDef<'a>>,
|
||||
region: Region,
|
||||
spaces_before: &[CommentOrNewline<'a>],
|
||||
spaces_after: &[CommentOrNewline<'a>],
|
||||
) {
|
||||
self.tags.push(tag);
|
||||
|
||||
self.regions.push(region);
|
||||
|
||||
let before = Slice::extend_new(&mut self.spaces, spaces_before.iter().copied());
|
||||
self.space_before.push(before);
|
||||
|
||||
let after = Slice::extend_new(&mut self.spaces, spaces_after.iter().copied());
|
||||
self.space_after.push(after);
|
||||
}
|
||||
|
||||
pub fn push_value_def(
|
||||
&mut self,
|
||||
value_def: ValueDef<'a>,
|
||||
region: Region,
|
||||
spaces_before: &[CommentOrNewline<'a>],
|
||||
spaces_after: &[CommentOrNewline<'a>],
|
||||
) {
|
||||
let value_def_index = Index::push_new(&mut self.value_defs, value_def);
|
||||
let tag = EitherIndex::from_right(value_def_index);
|
||||
self.push_def_help(tag, region, spaces_before, spaces_after)
|
||||
}
|
||||
|
||||
pub fn push_type_def(
|
||||
&mut self,
|
||||
type_def: TypeDef<'a>,
|
||||
region: Region,
|
||||
spaces_before: &[CommentOrNewline<'a>],
|
||||
spaces_after: &[CommentOrNewline<'a>],
|
||||
) {
|
||||
let type_def_index = Index::push_new(&mut self.type_defs, type_def);
|
||||
let tag = EitherIndex::from_left(type_def_index);
|
||||
self.push_def_help(tag, region, spaces_before, spaces_after)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Def, Derived, Expr, ExtractSpaces, Has, Pattern,
|
||||
Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
AssignedField, Collection, CommentOrNewline, Def, Defs, Derived, Expr, ExtractSpaces, Has,
|
||||
Pattern, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
|
||||
|
@ -811,6 +811,306 @@ struct DefState<'a> {
|
|||
spaces_after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
fn parse_toplevel_defs_end<'a>(
|
||||
options: ExprParseOptions,
|
||||
start_column: u32,
|
||||
mut defs: Defs<'a>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Defs<'a>, EExpr<'a>> {
|
||||
let min_indent = start_column;
|
||||
let initial = state.clone();
|
||||
|
||||
let mut spaces_before_current = &[] as &[_];
|
||||
|
||||
let state = match space0_e(min_indent, EExpr::IndentStart).parse(arena, state) {
|
||||
Err((MadeProgress, _, s)) => {
|
||||
return Err((MadeProgress, EExpr::DefMissingFinalExpr(s.pos()), s));
|
||||
}
|
||||
Ok((_, spaces, state)) => {
|
||||
spaces_before_current = spaces;
|
||||
state
|
||||
}
|
||||
Err((NoProgress, _, state)) => state,
|
||||
};
|
||||
|
||||
let start = state.pos();
|
||||
let column = state.column();
|
||||
|
||||
match space0_after_e(
|
||||
crate::pattern::loc_pattern_help(min_indent),
|
||||
min_indent,
|
||||
EPattern::IndentEnd,
|
||||
)
|
||||
.parse(arena, state.clone())
|
||||
{
|
||||
Err((NoProgress, _, _)) => {
|
||||
match crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect)
|
||||
.parse(arena, state)
|
||||
{
|
||||
Err((_, _, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, defs, initial))
|
||||
}
|
||||
Ok((_, _, state)) => {
|
||||
let parse_def_expr = space0_before_e(
|
||||
move |a, s| parse_loc_expr(min_indent + 1, a, s),
|
||||
min_indent,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
|
||||
|
||||
let end = loc_def_expr.region.end();
|
||||
let region = Region::new(start, end);
|
||||
|
||||
let value_def = ValueDef::Expect(arena.alloc(loc_def_expr));
|
||||
defs.push_value_def(value_def, region, spaces_before_current, &[]);
|
||||
|
||||
parse_toplevel_defs_end(options, column, defs, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err((MadeProgress, _, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, defs, initial))
|
||||
}
|
||||
Ok((_, loc_pattern, state)) => {
|
||||
// First let's check whether this is an ability definition.
|
||||
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> = match loc_pattern.value
|
||||
{
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
region,
|
||||
},
|
||||
args,
|
||||
) => Some((name, *region, args)),
|
||||
Pattern::Tag(name) => Some((name, loc_pattern.region, &[])),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((name, name_region, args)) = opt_tag_and_args {
|
||||
if let Ok((_, loc_has, state)) =
|
||||
loc_has_parser(min_indent).parse(arena, state.clone())
|
||||
{
|
||||
let (_, (type_def, def_region), state) = finish_parsing_ability_def_help(
|
||||
start_column,
|
||||
Loc::at(name_region, name),
|
||||
args,
|
||||
loc_has,
|
||||
arena,
|
||||
state,
|
||||
)?;
|
||||
|
||||
defs.push_type_def(type_def, def_region, spaces_before_current, &[]);
|
||||
|
||||
return parse_toplevel_defs_end(options, column, defs, arena, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, this is a def or alias.
|
||||
match operator().parse(arena, state) {
|
||||
Ok((_, BinOp::Assignment, state)) => {
|
||||
let parse_def_expr = space0_before_e(
|
||||
move |a, s| parse_loc_expr(min_indent + 1, a, s),
|
||||
min_indent,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
|
||||
|
||||
{
|
||||
let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region);
|
||||
|
||||
if spaces_before_current.len() <= 1 {
|
||||
match defs.last() {
|
||||
Some((
|
||||
before_ann_spaces,
|
||||
Def::Value(ValueDef::Annotation(ann_pattern, ann_type)),
|
||||
)) => {
|
||||
return append_body_definition_help(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
before_ann_spaces,
|
||||
spaces_before_current,
|
||||
loc_pattern,
|
||||
loc_def_expr,
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
);
|
||||
}
|
||||
Some((
|
||||
before_ann_spaces,
|
||||
Def::Type(TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
}),
|
||||
)) => {
|
||||
// This is a case like
|
||||
// UserId x : [UserId Int]
|
||||
// UserId x = UserId 42
|
||||
// We optimistically parsed the first line as an alias; we now turn it
|
||||
// into an annotation.
|
||||
let loc_name =
|
||||
arena.alloc(header.name.map(|x| Pattern::Tag(x)));
|
||||
let ann_pattern = Pattern::Apply(loc_name, header.vars);
|
||||
let vars_region =
|
||||
Region::across_all(header.vars.iter().map(|v| &v.region));
|
||||
let region_ann_pattern =
|
||||
Region::span_across(&loc_name.region, &vars_region);
|
||||
let loc_ann_pattern = Loc::at(region_ann_pattern, ann_pattern);
|
||||
|
||||
return append_body_definition_help(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
before_ann_spaces,
|
||||
spaces_before_current,
|
||||
loc_pattern,
|
||||
loc_def_expr,
|
||||
arena.alloc(loc_ann_pattern),
|
||||
ann_type,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
defs.extend(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the previous and current def can't be joined up
|
||||
let mut loc_def = Loc::at(
|
||||
region,
|
||||
Def::Value(ValueDef::Body(
|
||||
arena.alloc(loc_pattern),
|
||||
&*arena.alloc(loc_def_expr),
|
||||
)),
|
||||
);
|
||||
|
||||
if !spaces_before_current.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
.with_spaces_before(spaces_before_current, loc_def.region);
|
||||
}
|
||||
|
||||
defs.push(arena.alloc(loc_def));
|
||||
};
|
||||
|
||||
parse_toplevel_defs_end(options, column, defs, arena, state)
|
||||
}
|
||||
Ok((_, BinOp::IsAliasType, state)) => {
|
||||
let (_, ann_type, state) =
|
||||
alias_signature_with_space_before(min_indent + 1).parse(arena, state)?;
|
||||
|
||||
let region = Region::span_across(&loc_pattern.region, &ann_type.region);
|
||||
|
||||
// the previous and current def can't be joined up
|
||||
match &loc_pattern.value {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
|
||||
defs.push_type_def(type_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
|
||||
defs.push_type_def(type_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, ann_type);
|
||||
defs.push_value_def(value_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
};
|
||||
|
||||
parse_toplevel_defs_end(options, column, defs, arena, state)
|
||||
}
|
||||
Ok((_, BinOp::IsOpaqueType, state)) => {
|
||||
let (_, (signature, derived), state) =
|
||||
opaque_signature_with_space_before(min_indent + 1).parse(arena, state)?;
|
||||
|
||||
let region = Region::span_across(&loc_pattern.region, &signature.region);
|
||||
|
||||
// the previous and current def can't be joined up
|
||||
match &loc_pattern.value {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
|
||||
defs.push_type_def(type_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
|
||||
defs.push_type_def(type_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, signature);
|
||||
defs.push_value_def(value_def, region, spaces_before_current, &[]);
|
||||
}
|
||||
};
|
||||
|
||||
parse_toplevel_defs_end(options, column, defs, arena, state)
|
||||
}
|
||||
|
||||
_ => Ok((MadeProgress, defs, initial)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_defs_end<'a>(
|
||||
options: ExprParseOptions,
|
||||
start_column: u32,
|
||||
|
@ -1309,6 +1609,32 @@ fn finish_parsing_ability_def<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, &'a Loc<Def<'a>>, EExpr<'a>> {
|
||||
let (_, (type_def, def_region), state) =
|
||||
finish_parsing_ability_def_help(start_column, name, args, loc_has, arena, state)?;
|
||||
|
||||
let def = Def::Type(type_def);
|
||||
|
||||
let loc_def = &*(if spaces_before.is_empty() {
|
||||
arena.alloc(Loc::at(def_region, def))
|
||||
} else {
|
||||
arena.alloc(
|
||||
arena
|
||||
.alloc(def)
|
||||
.with_spaces_before(spaces_before, def_region),
|
||||
)
|
||||
});
|
||||
|
||||
Ok((MadeProgress, loc_def, state))
|
||||
}
|
||||
|
||||
fn finish_parsing_ability_def_help<'a>(
|
||||
start_column: u32,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
loc_has: Loc<Has<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> {
|
||||
let mut demands = Vec::with_capacity_in(2, arena);
|
||||
|
||||
let min_indent_for_demand = start_column + 1;
|
||||
|
@ -1347,23 +1673,13 @@ fn finish_parsing_ability_def<'a>(
|
|||
}
|
||||
|
||||
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
|
||||
let def = Def::Type(TypeDef::Ability {
|
||||
let type_def = TypeDef::Ability {
|
||||
header: TypeHeader { name, vars: args },
|
||||
loc_has,
|
||||
members: demands.into_bump_slice(),
|
||||
});
|
||||
};
|
||||
|
||||
let loc_def = &*(if spaces_before.is_empty() {
|
||||
arena.alloc(Loc::at(def_region, def))
|
||||
} else {
|
||||
arena.alloc(
|
||||
arena
|
||||
.alloc(def)
|
||||
.with_spaces_before(spaces_before, def_region),
|
||||
)
|
||||
});
|
||||
|
||||
Ok((MadeProgress, loc_def, state))
|
||||
Ok((MadeProgress, (type_def, def_region), state))
|
||||
}
|
||||
|
||||
fn finish_parsing_ability<'a>(
|
||||
|
@ -1960,6 +2276,30 @@ fn assigned_expr_field_to_pattern_help<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn toplevel_defs<'a>(min_indent: u32) -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let (_, initial_space, state) =
|
||||
space0_e(min_indent, EExpr::IndentEnd).parse(arena, state)?;
|
||||
|
||||
let start_column = state.column();
|
||||
|
||||
let options = ExprParseOptions {
|
||||
accept_multi_backpassing: false,
|
||||
check_for_arrow: true,
|
||||
};
|
||||
|
||||
let output = Defs::default();
|
||||
|
||||
let (_, output, state) =
|
||||
parse_toplevel_defs_end(options, start_column, output, arena, state)?;
|
||||
|
||||
let (_, final_space, state) =
|
||||
space0_e(start_column, EExpr::IndentEnd).parse(arena, state)?;
|
||||
|
||||
Ok((MadeProgress, output, state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defs<'a>(min_indent: u32) -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let def_state = DefState {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue