Support both inline and header imports

Load will now convert header imports to inline import defs, so that
we can support both temporarily.
This commit is contained in:
Agus Zubiaga 2024-04-20 18:57:53 -03:00
parent 7a53484479
commit 7ebfc6d06d
No known key found for this signature in database
18 changed files with 235 additions and 121 deletions

View file

@ -1,6 +1,10 @@
use bumpalo::Bump;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use roc_parse::{module, module::module_defs, parser::Parser, state::State};
use roc_parse::{
ast::Defs,
module::{self, parse_module_defs},
state::State,
};
use std::path::PathBuf;
pub fn parse_benchmark(c: &mut Criterion) {
@ -18,11 +22,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
let (_actual, state) =
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
let min_indent = 0;
let res = module_defs()
.parse(&arena, state, min_indent)
.map(|tuple| tuple.1)
.unwrap();
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();
black_box(res.len());
})
@ -43,11 +43,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
let (_actual, state) =
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
let min_indent = 0;
let res = module_defs()
.parse(&arena, state, min_indent)
.map(|tuple| tuple.1)
.unwrap();
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();
black_box(res.len());
})

View file

@ -98,6 +98,115 @@ pub struct Module<'a> {
pub header: Header<'a>,
}
impl<'a> Module<'a> {
pub fn header_imports_to_defs(
arena: &'a Bump,
imports: Option<
header::KeywordItem<'a, header::ImportsKeyword, header::ImportsCollection<'a>>,
>,
) -> Defs<'a> {
let mut defs = Defs::default();
if let Some(imports) = imports {
let len = imports.item.len();
for (index, import) in imports.item.iter().enumerate() {
let spaced = import.extract_spaces();
let value_def = match spaced.item {
header::ImportsEntry::Package(pkg_name, name, exposed) => {
Self::header_import_to_value_def(
Some(pkg_name),
name,
exposed,
import.region,
)
}
header::ImportsEntry::Module(name, exposed) => {
Self::header_import_to_value_def(None, name, exposed, import.region)
}
header::ImportsEntry::IngestedFile(path, typed_ident) => {
ValueDef::IngestedFileImport(IngestedFileImport {
before_path: &[],
path: Loc {
value: path,
region: import.region,
},
name: header::KeywordItem {
keyword: Spaces {
before: &[],
item: ImportAsKeyword,
after: &[],
},
item: Loc {
value: typed_ident,
region: import.region,
},
},
})
}
};
defs.push_value_def(
value_def,
import.region,
if index == 0 {
let mut before = vec![CommentOrNewline::Newline, CommentOrNewline::Newline];
before.extend(spaced.before);
arena.alloc(before)
} else {
spaced.before
},
if index == len - 1 {
let mut after = spaced.after.to_vec();
after.push(CommentOrNewline::Newline);
after.push(CommentOrNewline::Newline);
arena.alloc(after)
} else {
spaced.after
},
);
}
}
defs
}
fn header_import_to_value_def(
pkg_name: Option<&'a str>,
name: header::ModuleName<'a>,
exposed: Collection<'a, Loc<Spaced<'a, header::ExposedName<'a>>>>,
region: Region,
) -> ValueDef<'a> {
use crate::header::KeywordItem;
let new_exposed = if exposed.is_empty() {
None
} else {
Some(KeywordItem {
keyword: Spaces {
before: &[],
item: ImportExposingKeyword,
after: &[],
},
item: exposed,
})
};
ValueDef::ModuleImport(ModuleImport {
before_name: &[],
name: Loc {
region,
value: ImportedModuleName {
package: pkg_name,
name,
},
},
alias: None,
exposed: new_exposed,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Header<'a> {
Interface(InterfaceHeader<'a>),

View file

@ -2442,41 +2442,42 @@ fn assigned_expr_field_to_pattern_help<'a>(
})
}
pub fn toplevel_defs<'a>() -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, initial_space, state) =
space0_e(EExpr::IndentEnd).parse(arena, state, min_indent)?;
pub fn parse_top_level_defs<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
mut output: Defs<'a>,
) -> ParseResult<'a, Defs<'a>, EExpr<'a>> {
let (_, initial_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, 0)?;
let start_column = state.column();
let start_column = state.column();
let options = ExprParseOptions {
accept_multi_backpassing: true,
check_for_arrow: true,
suffixed_found: false,
};
let options = ExprParseOptions {
accept_multi_backpassing: true,
check_for_arrow: true,
suffixed_found: false,
};
let mut output = Defs::default();
let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied());
let existing_len = output.tags.len();
let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?;
let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied());
let (_, final_space, state) =
space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?;
let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?;
if !output.tags.is_empty() {
// add surrounding whitespace
let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied());
let (_, final_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?;
debug_assert!(output.space_before[0].is_empty());
output.space_before[0] = before;
if output.tags.len() > existing_len {
// add surrounding whitespace
let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied());
let last = output.tags.len() - 1;
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
output.space_after[last] = after;
}
debug_assert!(output.space_before[existing_len].is_empty());
output.space_before[existing_len] = before;
Ok((MadeProgress, output, state))
let last = output.tags.len() - 1;
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
output.space_after[last] = after;
}
Ok((MadeProgress, output, state))
}
// PARSER HELPERS

View file

@ -211,7 +211,6 @@ keywords! {
RequiresKeyword => "requires",
ProvidesKeyword => "provides",
ToKeyword => "to",
// [modules-revamp] TODO: Remove
ImportsKeyword => "imports",
}
@ -230,6 +229,9 @@ pub struct InterfaceHeader<'a> {
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
}
pub type ImportsKeywordItem<'a> = KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>;
pub type ImportsCollection<'a> = Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>;
#[derive(Clone, Debug, PartialEq)]
pub struct HostedHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],

View file

@ -28,12 +28,19 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
}
}
#[inline(always)]
pub fn module_defs<'a>() -> impl Parser<'a, Defs<'a>, SyntaxError<'a>> {
skip_second!(
specialize_err(SyntaxError::Expr, crate::expr::toplevel_defs(),),
end_of_file()
)
pub fn parse_module_defs<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
defs: Defs<'a>,
) -> Result<Defs<'a>, SyntaxError<'a>> {
let min_indent = 0;
match crate::expr::parse_top_level_defs(arena, state.clone(), defs) {
Ok((_, defs, state)) => match end_of_file().parse(arena, state, min_indent) {
Ok(_) => Ok(defs),
Err((_, fail)) => Err(fail),
},
Err((_, fail)) => Err(SyntaxError::Expr(fail, state.pos())),
}
}
pub fn parse_header<'a>(

View file

@ -1,7 +1,6 @@
use crate::ast;
use crate::ast::Defs;
use crate::module::module_defs;
use crate::parser::Parser;
use crate::module::parse_module_defs;
use crate::parser::SourceError;
use crate::parser::SyntaxError;
use crate::state::State;
@ -34,12 +33,7 @@ pub fn parse_loc_with<'a>(
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
let state = State::new(input.trim().as_bytes());
let min_indent = 0;
match module_defs().parse(arena, state, min_indent) {
Ok(tuple) => Ok(tuple.1),
Err(tuple) => Err(tuple.1),
}
parse_module_defs(arena, state, Defs::default())
}
pub fn parse_header_with<'a>(

View file

@ -22,8 +22,8 @@ mod test_parse {
use roc_parse::ast::StrSegment::*;
use roc_parse::ast::{self, EscapedChar};
use roc_parse::ast::{CommentOrNewline, StrLiteral::*};
use roc_parse::module::module_defs;
use roc_parse::parser::{Parser, SyntaxError};
use roc_parse::module::parse_module_defs;
use roc_parse::parser::SyntaxError;
use roc_parse::state::State;
use roc_parse::test_helpers::parse_expr_with;
use roc_region::all::{Loc, Region};
@ -352,9 +352,7 @@ mod test_parse {
List.map list isTest
"
);
let actual = module_defs()
.parse(&arena, State::new(src.as_bytes()), 0)
.map(|tuple| tuple.1);
let actual = parse_module_defs(&arena, State::new(src.as_bytes()), ast::Defs::default());
// It should occur twice in the debug output - once for the pattern,
// and then again for the lookup.
@ -378,13 +376,12 @@ mod test_parse {
);
let state = State::new(src.as_bytes());
let parser = module_defs();
let parsed = parser.parse(arena, state, 0);
let parsed = parse_module_defs(arena, state, ast::Defs::default());
match parsed {
Ok((_, _, _state)) => {
Ok(_) => {
// dbg!(_state);
}
Err((_, _fail)) => {
Err(_) => {
// dbg!(_fail, _state);
panic!("Failed to parse!");
}