mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge branch 'trunk' into bytes
This commit is contained in:
commit
869d3d18d0
19 changed files with 1036 additions and 477 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2950,6 +2950,7 @@ dependencies = [
|
|||
"roc_types",
|
||||
"roc_unify",
|
||||
"tempfile",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -135,6 +135,9 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io
|
|||
Err(LoadingProblem::ParsingFailedReport(report)) => {
|
||||
print!("{}", report);
|
||||
}
|
||||
Err(LoadingProblem::NoPlatform(report)) => {
|
||||
print!("{}", report);
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{:?}", other);
|
||||
}
|
||||
|
|
|
@ -230,9 +230,8 @@ mod cli_run {
|
|||
#[test]
|
||||
#[serial(astar)]
|
||||
fn run_astar_optimized_1() {
|
||||
check_output_with_stdin(
|
||||
check_output(
|
||||
&example_file("benchmarks", "TestAStar.roc"),
|
||||
"1",
|
||||
"test-astar",
|
||||
&[],
|
||||
"True\n",
|
||||
|
|
|
@ -674,9 +674,14 @@ pub fn canonicalize_expr<'a>(
|
|||
Output::default(),
|
||||
)
|
||||
}
|
||||
ast::Expr::If(cond, then_branch, else_branch) => {
|
||||
let (loc_cond, mut output) =
|
||||
canonicalize_expr(env, var_store, scope, cond.region, &cond.value);
|
||||
ast::Expr::If(if_thens, final_else_branch) => {
|
||||
let mut branches = Vec::with_capacity(1);
|
||||
let mut output = Output::default();
|
||||
|
||||
for (condition, then_branch) in if_thens.iter() {
|
||||
let (loc_cond, cond_output) =
|
||||
canonicalize_expr(env, var_store, scope, condition.region, &condition.value);
|
||||
|
||||
let (loc_then, then_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
|
@ -684,22 +689,28 @@ pub fn canonicalize_expr<'a>(
|
|||
then_branch.region,
|
||||
&then_branch.value,
|
||||
);
|
||||
|
||||
branches.push((loc_cond, loc_then));
|
||||
|
||||
output.references = output.references.union(cond_output.references);
|
||||
output.references = output.references.union(then_output.references);
|
||||
}
|
||||
|
||||
let (loc_else, else_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
else_branch.region,
|
||||
&else_branch.value,
|
||||
final_else_branch.region,
|
||||
&final_else_branch.value,
|
||||
);
|
||||
|
||||
output.references = output.references.union(then_output.references);
|
||||
output.references = output.references.union(else_output.references);
|
||||
|
||||
(
|
||||
If {
|
||||
cond_var: var_store.fresh(),
|
||||
branch_var: var_store.fresh(),
|
||||
branches: vec![(loc_cond, loc_then)],
|
||||
branches,
|
||||
final_else: Box::new(loc_else),
|
||||
},
|
||||
output,
|
||||
|
|
|
@ -290,16 +290,21 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a
|
|||
}),
|
||||
)
|
||||
}
|
||||
If(condition, then_branch, else_branch)
|
||||
| Nested(If(condition, then_branch, else_branch)) => {
|
||||
// If does not get desugared yet so we can give more targetted error messages during
|
||||
// type checking.
|
||||
let desugared_cond = &*arena.alloc(desugar_expr(arena, &condition));
|
||||
let desugared_then = &*arena.alloc(desugar_expr(arena, &then_branch));
|
||||
let desugared_else = &*arena.alloc(desugar_expr(arena, &else_branch));
|
||||
If(if_thens, final_else_branch) | Nested(If(if_thens, final_else_branch)) => {
|
||||
// If does not get desugared into `when` so we can give more targetted error messages during type checking.
|
||||
let desugared_final_else = &*arena.alloc(desugar_expr(arena, &final_else_branch));
|
||||
|
||||
let mut desugared_if_thens = Vec::with_capacity_in(if_thens.len(), arena);
|
||||
|
||||
for (condition, then_branch) in if_thens.iter() {
|
||||
desugared_if_thens.push((
|
||||
desugar_expr(arena, condition).clone(),
|
||||
desugar_expr(arena, then_branch).clone(),
|
||||
));
|
||||
}
|
||||
|
||||
arena.alloc(Located {
|
||||
value: If(desugared_cond, desugared_then, desugared_else),
|
||||
value: If(desugared_if_thens.into_bump_slice(), desugared_final_else),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -58,8 +58,11 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
|||
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
||||
}
|
||||
|
||||
If(loc_cond, loc_if_true, loc_if_false) => {
|
||||
loc_cond.is_multiline() || loc_if_true.is_multiline() || loc_if_false.is_multiline()
|
||||
If(branches, final_else) => {
|
||||
final_else.is_multiline()
|
||||
|| branches
|
||||
.iter()
|
||||
.any(|(c, t)| c.is_multiline() || t.is_multiline())
|
||||
}
|
||||
|
||||
BinOp((loc_left, _, loc_right)) => {
|
||||
|
@ -257,8 +260,8 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
|||
// still print the return value.
|
||||
ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||
}
|
||||
If(loc_condition, loc_then, loc_else) => {
|
||||
fmt_if(buf, loc_condition, loc_then, loc_else, indent);
|
||||
If(branches, final_else) => {
|
||||
fmt_if(buf, branches, final_else, self.is_multiline(), indent);
|
||||
}
|
||||
When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
|
||||
List {
|
||||
|
@ -629,15 +632,15 @@ fn fmt_when<'a>(
|
|||
|
||||
fn fmt_if<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_condition: &'a Located<Expr<'a>>,
|
||||
loc_then: &'a Located<Expr<'a>>,
|
||||
loc_else: &'a Located<Expr<'a>>,
|
||||
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
|
||||
final_else: &'a Located<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
) {
|
||||
let is_multiline_then = loc_then.is_multiline();
|
||||
let is_multiline_else = loc_else.is_multiline();
|
||||
let is_multiline_condition = loc_condition.is_multiline();
|
||||
let is_multiline = is_multiline_then || is_multiline_else || is_multiline_condition;
|
||||
// let is_multiline_then = loc_then.is_multiline();
|
||||
// let is_multiline_else = final_else.is_multiline();
|
||||
// let is_multiline_condition = loc_condition.is_multiline();
|
||||
// let is_multiline = is_multiline_then || is_multiline_else || is_multiline_condition;
|
||||
|
||||
let return_indent = if is_multiline {
|
||||
indent + INDENT
|
||||
|
@ -645,6 +648,9 @@ fn fmt_if<'a>(
|
|||
indent
|
||||
};
|
||||
|
||||
for (loc_condition, loc_then) in branches.iter() {
|
||||
let is_multiline_condition = loc_condition.is_multiline();
|
||||
|
||||
buf.push_str("if");
|
||||
|
||||
if is_multiline_condition {
|
||||
|
@ -703,7 +709,12 @@ fn fmt_if<'a>(
|
|||
Expr::SpaceAfter(expr_above, spaces_above) => {
|
||||
expr_above.format(buf, return_indent);
|
||||
|
||||
fmt_comments_only(buf, spaces_above.iter(), NewlineAt::Top, return_indent);
|
||||
fmt_comments_only(
|
||||
buf,
|
||||
spaces_above.iter(),
|
||||
NewlineAt::Top,
|
||||
return_indent,
|
||||
);
|
||||
newline(buf, indent);
|
||||
}
|
||||
|
||||
|
@ -720,6 +731,7 @@ fn fmt_if<'a>(
|
|||
buf.push_str(" ");
|
||||
loc_then.format(buf, return_indent);
|
||||
}
|
||||
}
|
||||
|
||||
if is_multiline {
|
||||
buf.push_str("else");
|
||||
|
@ -728,7 +740,7 @@ fn fmt_if<'a>(
|
|||
buf.push_str(" else ");
|
||||
}
|
||||
|
||||
loc_else.format(buf, return_indent);
|
||||
final_else.format(buf, return_indent);
|
||||
}
|
||||
|
||||
pub fn fmt_closure<'a>(
|
||||
|
|
|
@ -19,6 +19,7 @@ roc_parse = { path = "../parse" }
|
|||
roc_solve = { path = "../solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
inlinable_string = "0.1"
|
||||
parking_lot = { version = "0.11", features = ["deadlock_detection"] }
|
||||
|
|
|
@ -631,6 +631,7 @@ struct ModuleHeader<'a> {
|
|||
module_id: ModuleId,
|
||||
module_name: ModuleNameEnum<'a>,
|
||||
module_path: PathBuf,
|
||||
is_root_module: bool,
|
||||
exposed_ident_ids: IdentIds,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
packages: MutMap<&'a str, PackageOrPath<'a>>,
|
||||
|
@ -781,6 +782,14 @@ enum Msg<'a> {
|
|||
FailedToParse(ParseProblem<'a, SyntaxError<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PlatformPath<'a> {
|
||||
NotSpecified,
|
||||
Valid(To<'a>),
|
||||
RootIsInterface,
|
||||
RootIsPkgConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State<'a> {
|
||||
pub root_id: ModuleId,
|
||||
|
@ -789,7 +798,7 @@ struct State<'a> {
|
|||
pub stdlib: &'a StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: Option<To<'a>>,
|
||||
pub platform_path: PlatformPath<'a>,
|
||||
|
||||
pub headers_parsed: MutSet<ModuleId>,
|
||||
|
||||
|
@ -994,6 +1003,8 @@ pub enum LoadingProblem<'a> {
|
|||
},
|
||||
ParsingFailed(ParseProblem<'a, SyntaxError<'a>>),
|
||||
UnexpectedHeader(String),
|
||||
/// there is no platform (likely running an Interface module)
|
||||
NoPlatform(String),
|
||||
|
||||
MsgChannelDied,
|
||||
ErrJoiningWorkerThreads,
|
||||
|
@ -1147,6 +1158,7 @@ impl<'a> LoadStart<'a> {
|
|||
load_filename(
|
||||
arena,
|
||||
filename,
|
||||
true,
|
||||
None,
|
||||
Arc::clone(&arc_modules),
|
||||
Arc::clone(&ident_ids_by_module),
|
||||
|
@ -1415,7 +1427,7 @@ where
|
|||
goal_phase,
|
||||
stdlib,
|
||||
output_path: None,
|
||||
platform_path: None,
|
||||
platform_path: PlatformPath::NotSpecified,
|
||||
module_cache: ModuleCache::default(),
|
||||
dependencies: Dependencies::default(),
|
||||
procedures: MutMap::default(),
|
||||
|
@ -1492,7 +1504,7 @@ where
|
|||
state,
|
||||
subs,
|
||||
exposed_to_host,
|
||||
)));
|
||||
)?));
|
||||
}
|
||||
Msg::FailedToParse(problem) => {
|
||||
// Shut down all the worker threads.
|
||||
|
@ -1623,16 +1635,25 @@ fn update<'a>(
|
|||
|
||||
match header_extra {
|
||||
App { to_platform } => {
|
||||
debug_assert_eq!(state.platform_path, None);
|
||||
|
||||
state.platform_path = Some(to_platform.clone());
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::Valid(to_platform.clone());
|
||||
}
|
||||
PkgConfig { .. } => {
|
||||
debug_assert_eq!(state.platform_id, None);
|
||||
|
||||
state.platform_id = Some(header.module_id);
|
||||
|
||||
if header.is_root_module {
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::RootIsPkgConfig;
|
||||
}
|
||||
}
|
||||
Interface => {
|
||||
if header.is_root_module {
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::RootIsInterface;
|
||||
}
|
||||
}
|
||||
Interface => {}
|
||||
}
|
||||
|
||||
// store an ID to name mapping, so we know the file to read when fetching dependencies' headers
|
||||
|
@ -2070,7 +2091,7 @@ fn finish_specialization(
|
|||
state: State,
|
||||
subs: Subs,
|
||||
exposed_to_host: MutMap<Symbol, Variable>,
|
||||
) -> MonomorphizedModule {
|
||||
) -> Result<MonomorphizedModule, LoadingProblem> {
|
||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||
.into_inner()
|
||||
|
@ -2097,21 +2118,89 @@ fn finish_specialization(
|
|||
..
|
||||
} = module_cache;
|
||||
|
||||
let sources = sources
|
||||
let sources: MutMap<ModuleId, (PathBuf, Box<str>)> = sources
|
||||
.into_iter()
|
||||
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||
.collect();
|
||||
|
||||
let path_to_platform = {
|
||||
use PlatformPath::*;
|
||||
let package_or_path = match platform_path {
|
||||
Some(To::ExistingPackage(shorthand)) => {
|
||||
Valid(To::ExistingPackage(shorthand)) => {
|
||||
match (*state.arc_shorthands).lock().get(shorthand) {
|
||||
Some(p_or_p) => p_or_p.clone(),
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
Some(To::NewPackage(p_or_p)) => p_or_p,
|
||||
None => panic!("no platform!"),
|
||||
Valid(To::NewPackage(p_or_p)) => p_or_p,
|
||||
other => {
|
||||
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
let module_id = state.root_id;
|
||||
|
||||
let palette = DEFAULT_PALETTE;
|
||||
|
||||
// Report parsing and canonicalization problems
|
||||
let alloc = RocDocAllocator::new(&[], module_id, &interns);
|
||||
|
||||
let report = {
|
||||
match other {
|
||||
Valid(_) => unreachable!(),
|
||||
NotSpecified => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow("I could not find a platform based on your input file."),
|
||||
alloc.reflow(r"Does the module header contain an entry that looks like this:"),
|
||||
alloc
|
||||
.parser_suggestion(" packages { base: \"platform\" }")
|
||||
.indent(4),
|
||||
alloc.reflow("See also TODO."),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "NO PLATFORM".to_string(),
|
||||
}
|
||||
}
|
||||
RootIsInterface => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"The input file is a interface file, but only app modules can be ran."),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
|
||||
alloc.reflow(r"but won't output any executable."),
|
||||
])
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "NO PLATFORM".to_string(),
|
||||
}
|
||||
}
|
||||
RootIsPkgConfig => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"The input file is a package config file, but only app modules can be ran."),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies,"),
|
||||
alloc.reflow(r"but won't output any executable."),
|
||||
])
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "NO PLATFORM".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
return Err(LoadingProblem::NoPlatform(buf));
|
||||
}
|
||||
};
|
||||
|
||||
match package_or_path {
|
||||
|
@ -2123,7 +2212,7 @@ fn finish_specialization(
|
|||
|
||||
let platform_path = path_to_platform.into();
|
||||
|
||||
MonomorphizedModule {
|
||||
Ok(MonomorphizedModule {
|
||||
can_problems,
|
||||
mono_problems,
|
||||
type_problems,
|
||||
|
@ -2136,7 +2225,7 @@ fn finish_specialization(
|
|||
procedures,
|
||||
sources,
|
||||
timings: state.timings,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn finish(
|
||||
|
@ -2319,6 +2408,7 @@ fn load_module<'a>(
|
|||
load_filename(
|
||||
arena,
|
||||
filename,
|
||||
false,
|
||||
opt_shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
|
@ -2358,6 +2448,7 @@ fn parse_header<'a>(
|
|||
arena: &'a Bump,
|
||||
read_file_duration: Duration,
|
||||
filename: PathBuf,
|
||||
is_root_module: bool,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
|
@ -2383,6 +2474,7 @@ fn parse_header<'a>(
|
|||
value: ModuleNameEnum::Interface(header.name.value),
|
||||
},
|
||||
filename,
|
||||
is_root_module,
|
||||
opt_shorthand,
|
||||
&[],
|
||||
header.exposes.into_bump_slice(),
|
||||
|
@ -2405,6 +2497,7 @@ fn parse_header<'a>(
|
|||
value: ModuleNameEnum::App(header.name.value),
|
||||
},
|
||||
filename,
|
||||
is_root_module,
|
||||
opt_shorthand,
|
||||
packages,
|
||||
header.provides.into_bump_slice(),
|
||||
|
@ -2507,9 +2600,11 @@ fn parse_header<'a>(
|
|||
}
|
||||
|
||||
/// Load a module by its filename
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn load_filename<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
is_root_module: bool,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
|
@ -2525,6 +2620,7 @@ fn load_filename<'a>(
|
|||
arena,
|
||||
file_io_duration,
|
||||
filename,
|
||||
is_root_module,
|
||||
opt_shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
|
@ -2559,6 +2655,7 @@ fn load_from_str<'a>(
|
|||
arena,
|
||||
file_io_duration,
|
||||
filename,
|
||||
false,
|
||||
None,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
|
@ -2580,6 +2677,7 @@ enum ModuleNameEnum<'a> {
|
|||
fn send_header<'a>(
|
||||
loc_name: Located<ModuleNameEnum<'a>>,
|
||||
filename: PathBuf,
|
||||
is_root_module: bool,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
|
@ -2766,6 +2864,7 @@ fn send_header<'a>(
|
|||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
|
@ -2787,6 +2886,7 @@ fn send_header<'a>(
|
|||
fn send_header_two<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
is_root_module: bool,
|
||||
shorthand: &'a str,
|
||||
app_module_id: ModuleId,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
|
@ -2983,6 +3083,7 @@ fn send_header_two<'a>(
|
|||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name,
|
||||
packages: package_entries,
|
||||
|
@ -3126,6 +3227,7 @@ fn fabricate_pkg_config_module<'a>(
|
|||
send_header_two(
|
||||
arena,
|
||||
filename,
|
||||
false,
|
||||
shorthand,
|
||||
app_module_id,
|
||||
&[],
|
||||
|
|
|
@ -127,7 +127,7 @@ pub enum Expr<'a> {
|
|||
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
||||
|
||||
// Conditionals
|
||||
If(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
If(&'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)], &'a Loc<Expr<'a>>),
|
||||
When(
|
||||
/// The condition
|
||||
&'a Loc<Expr<'a>>,
|
||||
|
|
|
@ -60,11 +60,12 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
pub fn space0_around_e<'a, P, S, E>(
|
||||
pub fn space0_around_ee<'a, P, S, E>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||
indent_problem: fn(Row, Col) -> E,
|
||||
indent_before_problem: fn(Row, Col) -> E,
|
||||
indent_after_problem: fn(Row, Col) -> E,
|
||||
) -> impl Parser<'a, Located<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
|
@ -75,8 +76,11 @@ where
|
|||
{
|
||||
parser::map_with_arena(
|
||||
and(
|
||||
space0_e(min_indent, space_problem, indent_problem),
|
||||
and(parser, space0_e(min_indent, space_problem, indent_problem)),
|
||||
space0_e(min_indent, space_problem, indent_before_problem),
|
||||
and(
|
||||
parser,
|
||||
space0_e(min_indent, space_problem, indent_after_problem),
|
||||
),
|
||||
),
|
||||
move |arena: &'a Bump,
|
||||
tuples: (
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::ast::{
|
|||
AssignedField, Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
line_comment, space0, space0_after, space0_after_e, space0_around, space0_around_e,
|
||||
space0_before, space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly,
|
||||
line_comment, space0, space0_after, space0_after_e, space0_around, space0_around_ee,
|
||||
space0_before, space0_before_e, space0_e, space1, space1_before, spaces_exactly,
|
||||
};
|
||||
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
||||
use crate::keyword;
|
||||
|
@ -11,8 +11,8 @@ use crate::number_literal::number_literal;
|
|||
use crate::parser::{
|
||||
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
||||
fail, map, newline_char, not, not_followed_by, optional, sep_by1, specialize, specialize_ref,
|
||||
then, unexpected, unexpected_eof, word1, word2, EExpr, Either, ParseResult, Parser, State,
|
||||
SyntaxError, When,
|
||||
then, unexpected, unexpected_eof, word1, word2, BadInputError, EExpr, Either, If, List,
|
||||
ParseResult, Parser, State, SyntaxError, When,
|
||||
};
|
||||
use crate::pattern::loc_closure_param;
|
||||
use crate::type_annotation;
|
||||
|
@ -324,7 +324,7 @@ pub fn expr_to_pattern<'a>(
|
|||
| Expr::Closure(_, _)
|
||||
| Expr::BinOp(_)
|
||||
| Expr::Defs(_, _)
|
||||
| Expr::If(_, _, _)
|
||||
| Expr::If(_, _)
|
||||
| Expr::When(_, _)
|
||||
| Expr::MalformedClosure
|
||||
| Expr::PrecedenceConflict(_, _, _, _)
|
||||
|
@ -1029,14 +1029,15 @@ mod when {
|
|||
and!(
|
||||
when_with_indent(),
|
||||
skip_second!(
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
loc!(specialize_ref(
|
||||
When::Syntax,
|
||||
move |arena, state| parse_expr(min_indent, arena, state)
|
||||
)),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentCondition
|
||||
When::IndentCondition,
|
||||
When::IndentIs,
|
||||
),
|
||||
parser::keyword_e(keyword::IS, When::Is)
|
||||
)
|
||||
|
@ -1182,13 +1183,14 @@ mod when {
|
|||
skip_first!(
|
||||
parser::keyword_e(keyword::IF, When::IfToken),
|
||||
// TODO we should require space before the expression but not after
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
loc!(specialize_ref(When::IfGuard, move |arena, state| {
|
||||
parse_expr(min_indent, arena, state)
|
||||
})),
|
||||
min_indent,
|
||||
When::Space,
|
||||
When::IndentIfGuard,
|
||||
When::IndentArrow,
|
||||
)
|
||||
),
|
||||
Some
|
||||
|
@ -1234,41 +1236,100 @@ mod when {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
skip_first!(
|
||||
parser::keyword(keyword::IF, min_indent),
|
||||
space1_around(
|
||||
fn if_branch<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, (Located<Expr<'a>>, Located<Expr<'a>>), If<'a>> {
|
||||
move |arena, state| {
|
||||
// NOTE: only parse spaces before the expression
|
||||
let (_, cond, state) = space0_around_ee(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
)
|
||||
),
|
||||
and!(
|
||||
skip_first!(
|
||||
parser::keyword(keyword::THEN, min_indent),
|
||||
space1_around(
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentCondition,
|
||||
If::IndentThenToken,
|
||||
)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::THEN, If::Then)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, then_branch, state) = space0_around_ee(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
)
|
||||
),
|
||||
skip_first!(
|
||||
parser::keyword(keyword::ELSE, min_indent),
|
||||
// NOTE changed this from space1_around to space1_before
|
||||
space1_before(
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentThenBranch,
|
||||
If::IndentElseToken,
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|arena: &'a Bump, (condition, (then_branch, else_branch))| {
|
||||
Expr::If(
|
||||
&*arena.alloc(condition),
|
||||
&*arena.alloc(then_branch),
|
||||
&*arena.alloc(else_branch),
|
||||
)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::ELSE, If::Else)
|
||||
.parse(arena, state)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
Ok((MadeProgress, (cond, then_branch), state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
||||
move |arena: &'a Bump, state| {
|
||||
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
|
||||
|
||||
let mut branches = Vec::with_capacity_in(1, arena);
|
||||
|
||||
let mut loop_state = state;
|
||||
|
||||
let state_final_else = loop {
|
||||
let (_, (cond, then_branch), state) = if_branch(min_indent).parse(arena, loop_state)?;
|
||||
|
||||
branches.push((cond, then_branch));
|
||||
|
||||
// try to parse another `if`
|
||||
// NOTE this drops spaces between the `else` and the `if`
|
||||
let optional_if = and!(
|
||||
backtrackable(space0_e(min_indent, If::Space, If::IndentIf)),
|
||||
parser::keyword_e(keyword::IF, If::If)
|
||||
);
|
||||
|
||||
match optional_if.parse(arena, state) {
|
||||
Err((_, _, state)) => break state,
|
||||
Ok((_, _, state)) => {
|
||||
loop_state = state;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (_, else_branch, state) = space0_before_e(
|
||||
specialize_ref(
|
||||
If::Syntax,
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||
),
|
||||
min_indent,
|
||||
If::Space,
|
||||
If::IndentElseBranch,
|
||||
)
|
||||
.parse(arena, state_final_else)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch));
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
specialize(
|
||||
|e, r, c| SyntaxError::Expr(EExpr::If(e, r, c)),
|
||||
if_expr_help(min_indent),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1632,35 +1693,40 @@ fn binop<'a>() -> impl Parser<'a, BinOp, SyntaxError<'a>> {
|
|||
map!(ascii_char(b'%'), |_| BinOp::Percent)
|
||||
)
|
||||
}
|
||||
fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
specialize(
|
||||
|e, r, c| SyntaxError::Expr(EExpr::List(e, r, c)),
|
||||
list_literal_help(min_indent),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
let elems = collection_trailing_sep!(
|
||||
ascii_char(b'['),
|
||||
loc!(expr(min_indent)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
min_indent
|
||||
);
|
||||
fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, List<'a>> {
|
||||
move |arena, state| {
|
||||
let (_, (parsed_elems, final_comments), state) = collection_trailing_sep_e!(
|
||||
word1(b'[', List::Open),
|
||||
specialize_ref(List::Syntax, loc!(expr(min_indent))),
|
||||
word1(b',', List::End),
|
||||
word1(b']', List::End),
|
||||
min_indent,
|
||||
List::Open,
|
||||
List::Space,
|
||||
List::IndentEnd
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
parser::attempt(
|
||||
Attempting::List,
|
||||
map_with_arena!(elems, |arena,
|
||||
(parsed_elems, final_comments): (
|
||||
Vec<'a, Located<Expr<'a>>>,
|
||||
&'a [CommentOrNewline<'a>]
|
||||
)| {
|
||||
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
|
||||
|
||||
for parsed_elem in parsed_elems {
|
||||
allocated.push(&*arena.alloc(parsed_elem));
|
||||
}
|
||||
|
||||
Expr::List {
|
||||
let expr = Expr::List {
|
||||
items: allocated.into_bump_slice(),
|
||||
final_comments,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// Parser<'a, Vec<'a, Located<AssignedField<'a, S>>>>
|
||||
|
|
|
@ -378,12 +378,28 @@ pub enum EExpr<'a> {
|
|||
Space(BadInputError, Row, Col),
|
||||
|
||||
When(When<'a>, Row, Col),
|
||||
If(If<'a>, Row, Col),
|
||||
|
||||
List(List<'a>, Row, Col),
|
||||
|
||||
// EInParens(PInParens<'a>, Row, Col),
|
||||
IndentStart(Row, Col),
|
||||
IndentEnd(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum List<'a> {
|
||||
Open(Row, Col),
|
||||
End(Row, Col),
|
||||
Space(BadInputError, Row, Col),
|
||||
|
||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||
Expr(&'a EExpr<'a>, Row, Col),
|
||||
|
||||
IndentOpen(Row, Col),
|
||||
IndentEnd(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum When<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
|
@ -408,6 +424,26 @@ pub enum When<'a> {
|
|||
PatternAlignment(u16, Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum If<'a> {
|
||||
Space(BadInputError, Row, Col),
|
||||
If(Row, Col),
|
||||
Then(Row, Col),
|
||||
Else(Row, Col),
|
||||
// TODO make EEXpr
|
||||
Condition(&'a EExpr<'a>, Row, Col),
|
||||
ThenBranch(&'a EExpr<'a>, Row, Col),
|
||||
ElseBranch(&'a EExpr<'a>, Row, Col),
|
||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||
|
||||
IndentCondition(Row, Col),
|
||||
IndentIf(Row, Col),
|
||||
IndentThenToken(Row, Col),
|
||||
IndentElseToken(Row, Col),
|
||||
IndentThenBranch(Row, Col),
|
||||
IndentElseBranch(Row, Col),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EPattern<'a> {
|
||||
Record(PRecord<'a>, Row, Col),
|
||||
|
@ -1431,10 +1467,11 @@ macro_rules! collection_trailing_sep_e {
|
|||
and!(
|
||||
$crate::parser::trailing_sep_by0(
|
||||
$delimiter,
|
||||
$crate::blankspace::space0_around_e(
|
||||
$crate::blankspace::space0_around_ee(
|
||||
$elem,
|
||||
$min_indent,
|
||||
$space_problem,
|
||||
$indent_problem,
|
||||
$indent_problem
|
||||
)
|
||||
),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast::Pattern;
|
||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{ident, lowercase_ident, Ident};
|
||||
use crate::number_literal::number_literal;
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
@ -133,11 +133,12 @@ fn loc_pattern_in_parens_help<'a>(
|
|||
) -> impl Parser<'a, Located<Pattern<'a>>, PInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', PInParens::Open),
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
move |arena, state| specialize_ref(PInParens::Syntax, loc_pattern(min_indent))
|
||||
.parse(arena, state),
|
||||
min_indent,
|
||||
PInParens::Space,
|
||||
PInParens::IndentOpen,
|
||||
PInParens::IndentEnd,
|
||||
),
|
||||
word1(b')', PInParens::End)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast::{AssignedField, Tag, TypeAnnotation};
|
||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::join_module_parts;
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
|
@ -146,11 +146,12 @@ fn loc_type_in_parens<'a>(
|
|||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', TInParens::Open),
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
move |arena, state| specialize_ref(TInParens::Type, expression(min_indent))
|
||||
.parse(arena, state),
|
||||
min_indent,
|
||||
TInParens::Space,
|
||||
TInParens::IndentOpen,
|
||||
TInParens::IndentEnd,
|
||||
),
|
||||
word1(b')', TInParens::End)
|
||||
|
@ -436,11 +437,12 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
let (p2, rest, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', Type::TFunctionArgument),
|
||||
one_of![
|
||||
space0_around_e(
|
||||
space0_around_ee(
|
||||
term(min_indent),
|
||||
min_indent,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart
|
||||
Type::TIndentStart,
|
||||
Type::TIndentEnd
|
||||
),
|
||||
|_, state: State<'a>| Err((
|
||||
NoProgress,
|
||||
|
|
|
@ -158,7 +158,10 @@ enum Context {
|
|||
enum Node {
|
||||
WhenCondition,
|
||||
WhenBranch,
|
||||
// WhenIfGuard,
|
||||
IfCondition,
|
||||
IfThenBranch,
|
||||
IfElseBranch,
|
||||
ListElement,
|
||||
}
|
||||
|
||||
fn to_expr_report<'a>(
|
||||
|
@ -173,10 +176,240 @@ fn to_expr_report<'a>(
|
|||
|
||||
match parse_problem {
|
||||
EExpr::When(when, row, col) => to_when_report(alloc, filename, context, &when, *row, *col),
|
||||
EExpr::If(if_, row, col) => to_if_report(alloc, filename, context, &if_, *row, *col),
|
||||
EExpr::List(list, row, col) => to_list_report(alloc, filename, context, &list, *row, *col),
|
||||
_ => todo!("unhandled parse error: {:?}", parse_problem),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_list_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
context: Context,
|
||||
parse_problem: &roc_parse::parser::List<'a>,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
) -> Report<'a> {
|
||||
use roc_parse::parser::List;
|
||||
|
||||
match *parse_problem {
|
||||
List::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
|
||||
List::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||
|
||||
List::Expr(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::ListElement, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
List::Open(row, col) | List::End(row, col) => {
|
||||
match dbg!(what_is_next(alloc.src_lines, row, col)) {
|
||||
Next::Other(Some(',')) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(
|
||||
r"I am partway through started parsing a list, but I got stuck here:",
|
||||
),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc
|
||||
.reflow(r"I was expecting to see a list entry before this comma, "),
|
||||
alloc.reflow(r"so try adding a list entry"),
|
||||
alloc.reflow(r" and see if that helps?"),
|
||||
]),
|
||||
]);
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(
|
||||
r"I am partway through started parsing a list, but I got stuck here:",
|
||||
),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(
|
||||
r"I was expecting to see a closing square bracket before this, ",
|
||||
),
|
||||
alloc.reflow(r"so try adding a "),
|
||||
alloc.parser_suggestion("]"),
|
||||
alloc.reflow(r" and see if that helps?"),
|
||||
]),
|
||||
alloc.concat(vec![
|
||||
alloc.note("When "),
|
||||
alloc.reflow(r"I get stuck like this, "),
|
||||
alloc.reflow(r"it usually means that there is a missing parenthesis "),
|
||||
alloc.reflow(r"or bracket somewhere earlier. "),
|
||||
alloc.reflow(r"It could also be a stray keyword or operator."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List::IndentOpen(row, col) | List::IndentEnd(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I cannot find the end of this list:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"You could change it to something like "),
|
||||
alloc.parser_suggestion("[ 1, 2, 3 ]"),
|
||||
alloc.reflow(" or even just "),
|
||||
alloc.parser_suggestion("[]"),
|
||||
alloc.reflow(". Anything where there is an open and a close square bracket, "),
|
||||
alloc.reflow("and where the elements of the list are separated by commas."),
|
||||
]),
|
||||
note_for_tag_union_type_indent(alloc),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED LIST".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_if_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
context: Context,
|
||||
parse_problem: &roc_parse::parser::If<'a>,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
) -> Report<'a> {
|
||||
use roc_parse::parser::If;
|
||||
|
||||
match *parse_problem {
|
||||
If::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
|
||||
If::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||
|
||||
If::Condition(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfCondition, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::ThenBranch(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfThenBranch, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::ElseBranch(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InNode(Node::IfElseBranch, start_row, start_col, Box::new(context)),
|
||||
expr,
|
||||
row,
|
||||
col,
|
||||
),
|
||||
|
||||
If::If(_row, _col) => unreachable!("another branch would be taken"),
|
||||
If::IndentIf(_row, _col) => unreachable!("another branch would be taken"),
|
||||
|
||||
If::Then(row, col) | If::IndentThenBranch(row, col) | If::IndentThenToken(row, col) => {
|
||||
to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see the "),
|
||||
alloc.keyword("then"),
|
||||
alloc.reflow(r" keyword next."),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
If::Else(row, col) | If::IndentElseBranch(row, col) | If::IndentElseToken(row, col) => {
|
||||
to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see the "),
|
||||
alloc.keyword("else"),
|
||||
alloc.reflow(r" keyword next."),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
If::IndentCondition(row, col) => to_unfinished_if_report(
|
||||
alloc,
|
||||
filename,
|
||||
row,
|
||||
col,
|
||||
start_row,
|
||||
start_col,
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was expecting to see a expression next")
|
||||
]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_unfinished_if_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
row: Row,
|
||||
col: Col,
|
||||
start_row: Row,
|
||||
start_col: Col,
|
||||
message: RocDocBuilder<'a>,
|
||||
) -> Report<'a> {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I was partway through parsing an "),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(r" expression, but I got stuck here:"),
|
||||
]),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
message,
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED IF".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_when_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
filename: PathBuf,
|
||||
|
@ -792,6 +1025,23 @@ fn to_type_report<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Type::TIndentEnd(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I am partway through parsing a type, but I got stuck here:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.note("I may be confused by indentation"),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "UNFINISHED TYPE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
Type::TAsIndentStart(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
@ -1606,6 +1856,7 @@ fn to_space_report<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Next<'a> {
|
||||
Keyword(&'a str),
|
||||
// Operator(&'a str),
|
||||
|
|
|
@ -801,35 +801,36 @@ mod test_reporting {
|
|||
)
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn if_3_branch_mismatch() {
|
||||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// if True then 2 else if False then 2 else "foo"
|
||||
// "#
|
||||
// ),
|
||||
// indoc!(
|
||||
// r#"
|
||||
// ── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||
#[test]
|
||||
fn if_3_branch_mismatch() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
if True then 2 else if False then 2 else "foo"
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||
|
||||
// The 2nd branch of this `if` does not match all the previous branches:
|
||||
The 3rd branch of this `if` does not match all the previous branches:
|
||||
|
||||
// 1│ if True then 2 else "foo"
|
||||
// ^^^^^
|
||||
1│ if True then 2 else if False then 2 else "foo"
|
||||
^^^^^
|
||||
|
||||
// The 2nd branch is a string of type
|
||||
The 3rd branch is a string of type:
|
||||
|
||||
// Str
|
||||
Str
|
||||
|
||||
// But all the previous branches have the type
|
||||
But all the previous branches have type:
|
||||
|
||||
// Num a
|
||||
Num a
|
||||
|
||||
// "#
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
I need all branches in an `if` to have the same type!
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_branch_mismatch() {
|
||||
|
@ -4636,7 +4637,7 @@ mod test_reporting {
|
|||
r#"
|
||||
── UNFINISHED TYPE ─────────────────────────────────────────────────────────────
|
||||
|
||||
I just started parsing a type, but I got stuck here:
|
||||
I am partway through parsing a type, but I got stuck here:
|
||||
|
||||
1│ f : I64, I64
|
||||
^
|
||||
|
@ -4949,4 +4950,138 @@ mod test_reporting {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_outdented_then() {
|
||||
// TODO I think we can do better here
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x =
|
||||
if 5 == 5
|
||||
then 2 else 3
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED IF ───────────────────────────────────────────────────────────────
|
||||
|
||||
I was partway through parsing an `if` expression, but I got stuck here:
|
||||
|
||||
2│ if 5 == 5
|
||||
^
|
||||
|
||||
I was expecting to see the `then` keyword next.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_missing_else() {
|
||||
// this should get better with time
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
if 5 == 5 then 2
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED IF ───────────────────────────────────────────────────────────────
|
||||
|
||||
I was partway through parsing an `if` expression, but I got stuck here:
|
||||
|
||||
1│ if 5 == 5 then 2
|
||||
^
|
||||
|
||||
I was expecting to see the `else` keyword next.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_double_comma() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
[ 1, 2, , 3 ]
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through started parsing a list, but I got stuck here:
|
||||
|
||||
1│ [ 1, 2, , 3 ]
|
||||
^
|
||||
|
||||
I was expecting to see a list entry before this comma, so try adding a
|
||||
list entry and see if that helps?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_without_end() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
[ 1, 2,
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through started parsing a list, but I got stuck here:
|
||||
|
||||
1│ [ 1, 2,
|
||||
^
|
||||
|
||||
I was expecting to see a closing square bracket before this, so try
|
||||
adding a ] and see if that helps?
|
||||
|
||||
Note: When I get stuck like this, it usually means that there is a
|
||||
missing parenthesis or bracket somewhere earlier. It could also be a
|
||||
stray keyword or operator.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_bad_indent() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x = [ 1, 2,
|
||||
]
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── UNFINISHED LIST ─────────────────────────────────────────────────────────────
|
||||
|
||||
I cannot find the end of this list:
|
||||
|
||||
1│ x = [ 1, 2,
|
||||
^
|
||||
|
||||
You could change it to something like [ 1, 2, 3 ] or even just [].
|
||||
Anything where there is an open and a close square bracket, and where
|
||||
the elements of the list are separated by commas.
|
||||
|
||||
Note: I may be confused by indentation
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
let arena = Bump::new();
|
||||
|
||||
let mut rects_arena = Bump::new();
|
||||
let mut ast_arena = Bump::new();
|
||||
|
||||
// Render loop
|
||||
window.request_redraw();
|
||||
|
@ -255,8 +256,8 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
);
|
||||
} else {
|
||||
// queue_no_file_text(&size, NOTHING_OPENED, CODE_TXT_XY.into(), &mut glyph_brush);
|
||||
|
||||
let mut pool = Pool::with_capacity(12);
|
||||
ast_arena.reset();
|
||||
let mut pool = Pool::with_capacity(1024);
|
||||
let mut var_store = VarStore::default();
|
||||
let dep_idents = IdentIds::exposed_builtins(8);
|
||||
let mut module_ids = ModuleIds::default();
|
||||
|
@ -280,7 +281,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
let (expr2, _) = crate::lang::expr::str_to_expr2(
|
||||
&arena,
|
||||
"{ x: 2, y: 5 }",
|
||||
"{ a: 1, b: 2, c: {x: 3, y: 4} }",
|
||||
&mut env,
|
||||
&mut scope,
|
||||
region,
|
||||
|
@ -288,6 +289,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
.unwrap();
|
||||
|
||||
super::render_ast::render_expr2(
|
||||
&ast_arena,
|
||||
&mut env,
|
||||
&expr2,
|
||||
&size,
|
||||
|
|
|
@ -1,16 +1,156 @@
|
|||
use crate::editor::colors::CODE_COL;
|
||||
use crate::lang::pool::PoolStr;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use cgmath::Vector2;
|
||||
use wgpu_glyph::GlyphBrush;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use crate::{
|
||||
editor::colors::CODE_COL,
|
||||
graphics::{
|
||||
primitives::text::{queue_code_text_draw, Text},
|
||||
style::CODE_FONT_SIZE,
|
||||
},
|
||||
lang::{ast::Expr2, expr::Env},
|
||||
};
|
||||
use cgmath::Vector2;
|
||||
use wgpu_glyph::GlyphBrush;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
fn pool_str_len<'a>(env: &Env<'a>, pool_str: &PoolStr) -> usize {
|
||||
env.pool.get_str(pool_str).len()
|
||||
}
|
||||
|
||||
// calculate the str len, necessary for BumpString
|
||||
fn expr2_to_len<'a>(env: &Env<'a>, expr2: &Expr2) -> usize {
|
||||
match expr2 {
|
||||
Expr2::SmallInt { text, .. } => pool_str_len(env, text),
|
||||
Expr2::I128 { text, .. } => pool_str_len(env, text),
|
||||
Expr2::U128 { text, .. } => pool_str_len(env, text),
|
||||
Expr2::Float { text, .. } => pool_str_len(env, text),
|
||||
Expr2::Str(text) => pool_str_len(env, text),
|
||||
Expr2::GlobalTag { name, .. } => pool_str_len(env, name),
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
|
||||
expr2_to_len(env, expr)
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
//TODO make bump_format to use arena
|
||||
let text = format!("{:?}", symbol);
|
||||
|
||||
text.len()
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut len_ctr = 2; // for '[' and ']'
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
||||
len_ctr += expr2_to_len(env, sub_expr2);
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
len_ctr += 2; // for ", "
|
||||
}
|
||||
}
|
||||
|
||||
len_ctr
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut len_ctr = 2; // for '{' and '}'
|
||||
|
||||
for (idx, node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(node_id);
|
||||
|
||||
len_ctr += pool_str_len(env, &pool_field_name);
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
let sub_expr2_len = expr2_to_len(env, sub_expr2);
|
||||
len_ctr += sub_expr2_len;
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
len_ctr += 2; // for ", "
|
||||
}
|
||||
}
|
||||
|
||||
len_ctr
|
||||
}
|
||||
rest => todo!("implement expr2_to_str for {:?}", rest),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bump_str<'a, 'b>(arena: &'a Bump, env: &Env<'b>, pool_str: &PoolStr) -> BumpString<'a> {
|
||||
let env_str = env.pool.get_str(pool_str);
|
||||
|
||||
BumpString::from_str_in(env_str, arena)
|
||||
}
|
||||
|
||||
pub fn expr2_to_str<'a, 'b>(arena: &'a Bump, env: &Env<'b>, expr2: &Expr2) -> BumpString<'a> {
|
||||
match expr2 {
|
||||
Expr2::SmallInt { text, .. } => get_bump_str(arena, env, text),
|
||||
Expr2::I128 { text, .. } => get_bump_str(arena, env, text),
|
||||
Expr2::U128 { text, .. } => get_bump_str(arena, env, text),
|
||||
Expr2::Float { text, .. } => get_bump_str(arena, env, text),
|
||||
Expr2::Str(text) => get_bump_str(arena, env, text),
|
||||
Expr2::GlobalTag { name, .. } => get_bump_str(arena, env, name),
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
|
||||
expr2_to_str(arena, env, expr)
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
//TODO make bump_format with arena
|
||||
let text = format!("{:?}", symbol);
|
||||
|
||||
BumpString::from_str_in(&text, arena)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut bump_str = BumpString::with_capacity_in(expr2_to_len(env, expr2), arena);
|
||||
|
||||
bump_str.push('[');
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
||||
bump_str.push_str(&expr2_to_str(arena, env, sub_expr2));
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
bump_str.push_str(", ")
|
||||
}
|
||||
}
|
||||
|
||||
bump_str.push(']');
|
||||
|
||||
bump_str
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut bump_str = BumpString::with_capacity_in(expr2_to_len(env, expr2), arena);
|
||||
|
||||
bump_str.push('{');
|
||||
|
||||
for (idx, node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(node_id);
|
||||
|
||||
let field_name = env.pool.get_str(pool_field_name);
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
|
||||
bump_str.push_str(field_name);
|
||||
bump_str.push(':');
|
||||
bump_str.push_str(&expr2_to_str(arena, env, sub_expr2));
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
bump_str.push_str(", ")
|
||||
}
|
||||
}
|
||||
|
||||
bump_str.push('}');
|
||||
|
||||
bump_str
|
||||
}
|
||||
rest => todo!("implement expr2_to_str for {:?}", rest),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_expr2<'a>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'a>,
|
||||
expr2: &Expr2,
|
||||
size: &PhysicalSize<u32>,
|
||||
|
@ -19,240 +159,18 @@ pub fn render_expr2<'a>(
|
|||
) {
|
||||
let area_bounds = (size.width as f32, size.height as f32).into();
|
||||
|
||||
match expr2 {
|
||||
Expr2::SmallInt { text, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(text),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
let expr_str = expr2_to_str(arena, env, expr2);
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::I128 { text, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(text),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::U128 { text, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(text),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::Float { text, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(text),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::Str(text) => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(text),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::GlobalTag { name, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: env.pool.get_str(name),
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
|
||||
render_expr2(env, expr, size, position, glyph_brush);
|
||||
}
|
||||
Expr2::Var(symbol) => {
|
||||
let text = format!("{:?}", symbol);
|
||||
// TODO format expr_str
|
||||
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: text.as_str(),
|
||||
text: &expr_str,
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: "[",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
|
||||
let mut x_pos = position.x;
|
||||
|
||||
for (idx, node_id) in elems.iter_node_ids().enumerate() {
|
||||
let sub_expr2 = env.pool.get(node_id);
|
||||
|
||||
x_pos += 20.0;
|
||||
|
||||
render_expr2(
|
||||
env,
|
||||
sub_expr2,
|
||||
size,
|
||||
Vector2::new(x_pos, position.y),
|
||||
glyph_brush,
|
||||
);
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
x_pos += 10.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: ",",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
}
|
||||
|
||||
x_pos += 20.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: "]",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let code_text = Text {
|
||||
position,
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: "{",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
|
||||
let mut x_pos = position.x;
|
||||
|
||||
for (idx, node_id) in fields.iter_node_ids().enumerate() {
|
||||
let (pool_field_name, _, sub_expr2_node_id) = env.pool.get(node_id);
|
||||
|
||||
let field_name = env.pool.get_str(pool_field_name);
|
||||
|
||||
x_pos += 20.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: field_name,
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
|
||||
x_pos += 10.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: ":",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
|
||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||
|
||||
x_pos += 20.0;
|
||||
|
||||
render_expr2(
|
||||
env,
|
||||
sub_expr2,
|
||||
size,
|
||||
Vector2::new(x_pos, position.y),
|
||||
glyph_brush,
|
||||
);
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
x_pos += 10.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: ",",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
}
|
||||
|
||||
x_pos += 20.0;
|
||||
|
||||
let code_text = Text {
|
||||
position: Vector2::new(x_pos, position.y),
|
||||
area_bounds,
|
||||
color: CODE_COL.into(),
|
||||
text: "}",
|
||||
size: CODE_FONT_SIZE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
queue_code_text_draw(&code_text, glyph_brush);
|
||||
}
|
||||
rest => todo!("implement {:?} render", rest),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -508,22 +508,31 @@ pub fn to_expr2<'a>(
|
|||
Output::default(),
|
||||
),
|
||||
|
||||
If(cond, then_branch, else_branch) => {
|
||||
let (cond, mut output) = to_expr2(env, scope, &cond.value, cond.region);
|
||||
If(branches, final_else) => {
|
||||
let mut new_branches = Vec::with_capacity(branches.len());
|
||||
let mut output = Output::default();
|
||||
|
||||
for (condition, then_branch) in branches.iter() {
|
||||
let (cond, cond_output) = to_expr2(env, scope, &condition.value, condition.region);
|
||||
|
||||
let (then_expr, then_output) =
|
||||
to_expr2(env, scope, &then_branch.value, then_branch.region);
|
||||
|
||||
let (else_expr, else_output) =
|
||||
to_expr2(env, scope, &else_branch.value, else_branch.region);
|
||||
|
||||
output.references.union_mut(cond_output.references);
|
||||
output.references.union_mut(then_output.references);
|
||||
|
||||
new_branches.push((cond, then_expr));
|
||||
}
|
||||
|
||||
let (else_expr, else_output) =
|
||||
to_expr2(env, scope, &final_else.value, final_else.region);
|
||||
|
||||
output.references.union_mut(else_output.references);
|
||||
|
||||
let expr = Expr2::If {
|
||||
cond_var: env.var_store.fresh(),
|
||||
expr_var: env.var_store.fresh(),
|
||||
branches: PoolVec::new(vec![(cond, then_expr)].into_iter(), env.pool),
|
||||
branches: PoolVec::new(new_branches.into_iter(), env.pool),
|
||||
final_else: env.pool.add(else_expr),
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue