Parse and format top-level import defs with no alias or exposed members

This commit is contained in:
Agus Zubiaga 2023-11-29 09:52:20 -03:00
parent ebfcd71e8d
commit 933fde77a0
No known key found for this signature in database
12 changed files with 117 additions and 25 deletions

View file

@ -196,6 +196,7 @@ impl<'a> Formattable for ValueDef<'a> {
Expect { condition, .. } => condition.is_multiline(),
ExpectFx { condition, .. } => condition.is_multiline(),
Dbg { condition, .. } => condition.is_multiline(),
ModuleImport { name: _ } => false,
}
}
@ -238,6 +239,13 @@ impl<'a> Formattable for ValueDef<'a> {
buf.newline();
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
}
ModuleImport { name } => {
buf.indent(indent);
buf.push_str("import");
buf.spaces(1);
buf.push_str(name.value.as_str());
}
}
}
}

View file

@ -565,6 +565,9 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
condition: arena.alloc(condition.remove_spaces(arena)),
preceding_comment: Region::zero(),
},
ModuleImport { name } => ModuleImport {
name: name.remove_spaces(arena),
},
}
}
}

View file

@ -455,6 +455,11 @@ pub enum ValueDef<'a> {
condition: &'a Loc<Expr<'a>>,
preceding_comment: Region,
},
/// e.g. `import [Req] as Http from InternalHttp`.
ModuleImport {
name: Loc<crate::header::ModuleName<'a>>,
},
}
#[derive(Debug, Clone, PartialEq, Default)]
@ -1792,6 +1797,7 @@ impl<'a> Malformed for ValueDef<'a> {
condition,
preceding_comment: _,
} => condition.is_malformed(),
ValueDef::ModuleImport { name } => name.value.contains_dot(),
}
}
}

View file

@ -12,8 +12,8 @@ use crate::keyword;
use crate::parser::{
self, backtrackable, increment_min_indent, line_min_indent, optional, reset_min_indent,
sep_by1, sep_by1_e, set_min_indent, specialize, specialize_ref, then, word1, word1_indent,
word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString,
EType, EWhen, Either, ParseResult, Parser,
word2, EClosure, EExpect, EExpr, EIf, EImport, EInParens, EList, ENumber, EPattern, ERecord,
EString, EType, EWhen, Either, ParseResult, Parser,
};
use crate::pattern::{closure_param, loc_implements_parser};
use crate::state::State;
@ -574,30 +574,43 @@ pub fn parse_single_def<'a>(
min_indent,
) {
Err((NoProgress, _)) => {
match parse_expect.parse(arena, state.clone(), min_indent) {
match loc!(import()).parse(arena, state.clone(), min_indent) {
Err((_, _)) => {
// a hacky way to get expression-based error messages. TODO fix this
Ok((NoProgress, None, initial))
match parse_expect.parse(arena, state.clone(), min_indent) {
Err((_, _)) => {
// a hacky way to get expression-based error messages. TODO fix this
Ok((NoProgress, None, initial))
}
Ok((_, expect_flavor, state)) => parse_statement_inside_def(
arena,
state,
min_indent,
options,
start,
spaces_before_current_start,
spaces_before_current,
|preceding_comment, loc_def_expr| match expect_flavor {
Either::Second(_) => ValueDef::Expect {
condition: arena.alloc(loc_def_expr),
preceding_comment,
},
Either::First(_) => ValueDef::ExpectFx {
condition: arena.alloc(loc_def_expr),
preceding_comment,
},
},
),
}
}
Ok((_, expect_flavor, state)) => parse_statement_inside_def(
arena,
Ok((_, loc_import, state)) => Ok((
MadeProgress,
Some(SingleDef {
type_or_value: Either::Second(loc_import.value),
region: loc_import.region,
spaces_before: spaces_before_current,
}),
state,
min_indent,
options,
start,
spaces_before_current_start,
spaces_before_current,
|preceding_comment, loc_def_expr| match expect_flavor {
Either::Second(_) => ValueDef::Expect {
condition: arena.alloc(loc_def_expr),
preceding_comment,
},
Either::First(_) => ValueDef::ExpectFx {
condition: arena.alloc(loc_def_expr),
preceding_comment,
},
},
),
)),
}
}
Err((MadeProgress, _)) => {
@ -823,6 +836,24 @@ pub fn parse_single_def<'a>(
}
}
#[inline(always)]
fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport> {
map!(
skip_first!(
and!(
crate::parser::keyword_e(crate::keyword::IMPORT, EImport::Import),
spaces()
),
loc!(crate::module::module_name_help(EImport::ModuleName))
),
|loc_module_name| {
ValueDef::ModuleImport {
name: loc_module_name,
}
}
)
}
/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg`
#[allow(clippy::too_many_arguments)]
fn parse_statement_inside_def<'a>(

View file

@ -128,6 +128,10 @@ impl<'a> ModuleName<'a> {
pub const fn as_str(&'a self) -> &'a str {
self.0
}
pub fn contains_dot(&self) -> bool {
self.0.contains('.')
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]

View file

@ -6,6 +6,7 @@ pub const WHEN: &str = "when";
pub const AS: &str = "as";
pub const IS: &str = "is";
pub const DBG: &str = "dbg";
pub const IMPORT: &str = "import";
pub const EXPECT: &str = "expect";
pub const EXPECT_FX: &str = "expect-fx";
pub const CRASH: &str = "crash";
@ -14,4 +15,6 @@ pub const CRASH: &str = "crash";
pub const IMPLEMENTS: &str = "implements";
pub const WHERE: &str = "where";
pub const KEYWORDS: [&str; 10] = [IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH];
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH,
];

View file

@ -590,7 +590,7 @@ fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> {
specialize(|_, pos| EImports::Shorthand(pos), lowercase_ident())
}
fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
pub fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
where
F: Fn(Position) -> E,
E: 'a,

View file

@ -87,6 +87,7 @@ impl_space_problem! {
EGeneratesWith,
EHeader<'a>,
EIf<'a>,
EImport,
EImports,
EInParens<'a>,
EClosure<'a>,
@ -346,6 +347,7 @@ pub enum EExpr<'a> {
Expect(EExpect<'a>, Position),
Dbg(EExpect<'a>, Position),
Import(EImport, Position),
Closure(EClosure<'a>, Position),
Underscore(Position),
@ -517,6 +519,13 @@ pub enum EExpect<'a> {
IndentCondition(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EImport {
Import(Position),
ModuleName(Position),
Space(BadInputError, Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EPattern<'a> {
Record(PRecord<'a>, Position),

View file

@ -0,0 +1,23 @@
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-12,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
ModuleImport {
name: @8-12 ModuleName(
"Json",
),
},
],
}

View file

@ -0,0 +1 @@
import Json

View file

@ -323,6 +323,7 @@ mod test_snapshots {
pass/highest_float.expr,
pass/highest_int.expr,
pass/if_def.expr,
pass/import.moduledefs,
pass/int_with_underscore.expr,
pass/interface_with_newline.header,
pass/lambda_in_chain.expr,
@ -568,6 +569,8 @@ mod test_snapshots {
Err(err) => Err(format!("{err:?}")),
};
println!("{:?}", result);
if expect == TestExpectation::Pass {
let tokens = roc_parse::highlight::highlight(&source);
for token in tokens {