Parse inline imports and ingested files at the expression level

```
numbers =
    import "numbers.json" as numbersJson : Str
    import json.Decode exposing [decode, list, int]

    numbersJson
    |> decode (list int)
    |> Result.withDefault []
```
This commit is contained in:
Agus Zubiaga 2023-12-26 17:43:14 -03:00
parent 2d93f0c3f1
commit 11e0202eb9
No known key found for this signature in database
8 changed files with 219 additions and 5 deletions

View file

@ -306,6 +306,7 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, E
loc!(specialize(EExpr::When, when::expr_help(options))),
loc!(specialize(EExpr::Expect, expect_help(options))),
loc!(specialize(EExpr::Dbg, dbg_help(options))),
loc!(import_help(options)),
loc!(specialize(EExpr::Closure, closure_help(options))),
loc!(expr_operator_chain(options)),
fail_expr_start_e()
@ -2181,7 +2182,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo
// closure_help_help(options)
map_with_arena!(
// After the first token, all other tokens must be indented past the start of the line
indented_seq!(
indented_seq_skip_first!(
// All closures start with a '\' - e.g. (\x -> x + 1)
word1_indent(b'\\', EClosure::Start),
// Once we see the '\', we're committed to parsing this as a closure.
@ -2225,7 +2226,7 @@ mod when {
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
map_with_arena!(
and!(
indented_seq!(
indented_seq_skip_first!(
parser::keyword_e(keyword::WHEN, EWhen::When),
space0_around_e_no_after_indent_check(
specialize_ref(EWhen::Condition, expr_start(options)),
@ -2236,7 +2237,7 @@ mod when {
// ambiguity. The formatter will fix it up.
//
// We require that branches are indented relative to the line containing the `is`.
indented_seq!(
indented_seq_skip_first!(
parser::keyword_e(keyword::IS, EWhen::Is),
branches(options)
)
@ -2528,6 +2529,18 @@ fn dbg_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<
}
}
fn import_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, import_def, state) =
loc!(specialize(EExpr::Import, import())).parse(arena, state, min_indent)?;
let mut defs = Defs::default();
defs.push_value_def(import_def.value, import_def.region, &[], &[]);
parse_defs_expr(options, min_indent, defs, arena, state)
}
}
fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<'a>> {
move |arena: &'a Bump, state, min_indent| {
let (_, _, state) =

View file

@ -1384,7 +1384,7 @@ macro_rules! record {
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the line_indent() at the start of the first parser.
#[macro_export]
macro_rules! indented_seq {
macro_rules! indented_seq_skip_first {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.line_indent();
@ -1409,6 +1409,34 @@ macro_rules! indented_seq {
};
}
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the line_indent() at the start of the first parser.
#[macro_export]
macro_rules! indented_seq {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.line_indent();
// TODO: we should account for min_indent here, but this doesn't currently work
// because min_indent is sometimes larger than it really should be, which is in turn
// due to uses of `increment_indent`.
//
// let p1_indent = std::cmp::max(start_indent, min_indent);
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
match $p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
}
};
}
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the column() at the start of the first parser.
#[macro_export]

View file

@ -386,7 +386,7 @@ fn record_type<'a>(
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
and!(
indented_seq!(
specialize(EType::TApply, concrete_type()),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`

View file

@ -0,0 +1,101 @@
Defs(
Defs {
tags: [
Index(2147483648),
Index(2147483649),
],
regions: [
@0-26,
@27-50,
],
space_before: [
Slice(start = 0, length = 0),
Slice(start = 0, length = 1),
],
space_after: [
Slice(start = 0, length = 0),
Slice(start = 1, length = 0),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
ModuleImport(
ModuleImport {
before_name: [],
name: @7-11 ImportedModuleName {
package: None,
name: "Json",
},
alias: None,
exposed: Some(
KeywordItem {
keyword: Spaces {
before: [],
item: ImportExposingKeyword,
after: [],
},
item: [
@22-25 ExposedName(
"int",
),
],
},
),
},
),
ModuleImport(
ModuleImport {
before_name: [],
name: @34-44 ImportedModuleName {
package: None,
name: "JsonEncode",
},
alias: Some(
KeywordItem {
keyword: Spaces {
before: [],
item: ImportAsKeyword,
after: [],
},
item: @48-50 ImportAlias(
"JE",
),
},
),
exposed: None,
},
),
],
},
@52-70 SpaceBefore(
Apply(
@52-61 Var {
module_name: "JE",
ident: "encode",
},
[
@63-69 ParensAround(
Apply(
@63-66 Var {
module_name: "",
ident: "int",
},
[
@67-69 Num(
"42",
),
],
Space,
),
),
],
Space,
),
[
Newline,
Newline,
],
),
)

View file

@ -0,0 +1,4 @@
import Json exposing [int]
import JsonEncode as JE
JE.encode (int 42)

View file

@ -0,0 +1,63 @@
Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-33,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
IngestedFileImport(
IngestedFileImport {
before_path: [],
path: @7-19 PlainLine(
"users.json",
),
name: KeywordItem {
keyword: Spaces {
before: [],
item: ImportAsKeyword,
after: [],
},
item: @23-33 TypedIdent {
ident: @23-27 "data",
spaces_before_colon: [],
ann: @30-33 Apply(
"",
"Str",
[],
),
},
},
},
),
],
},
@35-49 SpaceBefore(
Apply(
@35-44 Var {
module_name: "",
ident: "parseJson",
},
[
@45-49 Var {
module_name: "",
ident: "data",
},
],
Space,
),
[
Newline,
Newline,
],
),
)

View file

@ -0,0 +1,3 @@
import "users.json" as data : Str
parseJson data

View file

@ -330,6 +330,8 @@ mod test_snapshots {
pass/import_with_comments.moduledefs,
pass/import_with_exposed.moduledefs,
pass/ingested_file.moduledefs,
pass/inline_import.expr,
pass/inline_ingested_file.expr,
pass/int_with_underscore.expr,
pass/interface_with_newline.header,
pass/lambda_in_chain.expr,