mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-01 21:40:58 +00:00
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:
parent
7a53484479
commit
7ebfc6d06d
18 changed files with 235 additions and 121 deletions
|
|
@ -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());
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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>),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>],
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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>(
|
||||
|
|
|
|||
|
|
@ -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!");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue