mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Merge branch 'trunk' into editor_ideas
This commit is contained in:
commit
b78a7a94b2
36 changed files with 1762 additions and 989 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2960,6 +2960,7 @@ dependencies = [
|
|||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
|
@ -3061,7 +3062,6 @@ dependencies = [
|
|||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
|
|
|
@ -19,16 +19,16 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
|||
));
|
||||
}
|
||||
|
||||
pub fn build_file(
|
||||
pub fn build_file<'a>(
|
||||
arena: &'a Bump,
|
||||
target: &Triple,
|
||||
src_dir: PathBuf,
|
||||
roc_file_path: PathBuf,
|
||||
opt_level: OptLevel,
|
||||
emit_debug_info: bool,
|
||||
link_type: LinkType,
|
||||
) -> Result<PathBuf, LoadingProblem> {
|
||||
) -> Result<PathBuf, LoadingProblem<'a>> {
|
||||
let compilation_start = SystemTime::now();
|
||||
let arena = Bump::new();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
|
@ -36,13 +36,14 @@ pub fn build_file(
|
|||
|
||||
// Release builds use uniqueness optimizations
|
||||
let stdlib = match opt_level {
|
||||
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
|
||||
OptLevel::Optimize => roc_builtins::std::standard_stdlib(),
|
||||
OptLevel::Normal => arena.alloc(roc_builtins::std::standard_stdlib()),
|
||||
OptLevel::Optimize => arena.alloc(roc_builtins::std::standard_stdlib()),
|
||||
};
|
||||
|
||||
let loaded = roc_load::file::load_and_monomorphize(
|
||||
&arena,
|
||||
roc_file_path.clone(),
|
||||
&stdlib,
|
||||
stdlib,
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
ptr_bytes,
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use clap::ArgMatches;
|
||||
use clap::{App, Arg};
|
||||
use roc_build::link::LinkType;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_load::file::LoadingProblem;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
@ -77,7 +79,9 @@ pub fn build_app<'a>() -> App<'a> {
|
|||
}
|
||||
|
||||
pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
||||
let arena = Bump::new();
|
||||
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
|
||||
|
||||
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
|
||||
OptLevel::Optimize
|
||||
} else {
|
||||
|
@ -107,23 +111,33 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io
|
|||
}
|
||||
});
|
||||
|
||||
let binary_path = build::build_file(
|
||||
let res_binary_path = build::build_file(
|
||||
&arena,
|
||||
target,
|
||||
src_dir,
|
||||
path,
|
||||
opt_level,
|
||||
emit_debug_info,
|
||||
LinkType::Executable,
|
||||
)
|
||||
.expect("TODO gracefully handle build_file failing");
|
||||
);
|
||||
|
||||
if run_after_build {
|
||||
// Run the compiled app
|
||||
Command::new(binary_path)
|
||||
.spawn()
|
||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
.wait()
|
||||
.expect("TODO gracefully handle block_on failing");
|
||||
match res_binary_path {
|
||||
Ok(binary_path) => {
|
||||
if run_after_build {
|
||||
// Run the compiled app
|
||||
Command::new(binary_path)
|
||||
.spawn()
|
||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||
.wait()
|
||||
.expect("TODO gracefully handle block_on failing");
|
||||
}
|
||||
}
|
||||
Err(LoadingProblem::ParsingFailedReport(report)) => {
|
||||
print!("{}", report);
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{:?}", other);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use const_format::concatcp;
|
||||
use gen::{gen_and_eval, ReplOutput};
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_parse::parser::{Fail, FailReason};
|
||||
use roc_parse::parser::Bag;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
|
||||
use rustyline::Editor;
|
||||
use rustyline_derive::{Completer, Helper, Highlighter, Hinter};
|
||||
use std::io::{self};
|
||||
use std::io;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
const BLUE: &str = "\u{001b}[36m";
|
||||
|
@ -148,10 +148,10 @@ pub fn main() -> io::Result<()> {
|
|||
println!("{}", output);
|
||||
pending_src.clear();
|
||||
}
|
||||
Err(Fail {
|
||||
reason: FailReason::Eof(_),
|
||||
..
|
||||
}) => {}
|
||||
// Err(Fail {
|
||||
// reason: FailReason::Eof(_),
|
||||
// ..
|
||||
// }) => {}
|
||||
Err(fail) => {
|
||||
report_parse_error(fail);
|
||||
pending_src.clear();
|
||||
|
@ -191,11 +191,11 @@ pub fn main() -> io::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn report_parse_error(fail: Fail) {
|
||||
fn report_parse_error(fail: Bag<'_>) {
|
||||
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
||||
}
|
||||
|
||||
fn eval_and_format(src: &str) -> Result<String, Fail> {
|
||||
fn eval_and_format<'a>(src: &str) -> Result<String, Bag<'a>> {
|
||||
gen_and_eval(src.as_bytes(), Triple::host(), OptLevel::Normal).map(|output| match output {
|
||||
ReplOutput::NoProblems { expr, expr_type } => {
|
||||
format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type)
|
||||
|
|
|
@ -7,7 +7,7 @@ use roc_collections::all::{MutMap, MutSet};
|
|||
use roc_fmt::annotation::Formattable;
|
||||
use roc_fmt::annotation::{Newlines, Parens};
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
|
||||
use roc_parse::parser::Fail;
|
||||
use roc_parse::parser::Bag;
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::from_utf8_unchecked;
|
||||
|
@ -18,7 +18,11 @@ pub enum ReplOutput {
|
|||
NoProblems { expr: String, expr_type: String },
|
||||
}
|
||||
|
||||
pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fail> {
|
||||
pub fn gen_and_eval<'a>(
|
||||
src: &[u8],
|
||||
target: Triple,
|
||||
opt_level: OptLevel,
|
||||
) -> Result<ReplOutput, Bag<'a>> {
|
||||
use roc_reporting::report::{
|
||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_collections::all::MutMap;
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
|
@ -21,19 +21,22 @@ pub fn test_home() -> ModuleId {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -17,16 +17,16 @@ mod test_fmt {
|
|||
use roc_parse::ast::{Attempting, Expr};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::module::{self, module_defs};
|
||||
use roc_parse::parser::{Fail, Parser, State};
|
||||
use roc_parse::parser::{Bag, Parser, State};
|
||||
|
||||
fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Expr<'a>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Expr<'a>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc!(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr.value)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr.value)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
fn expr_formats_to(input: &str, expected: &str) {
|
||||
|
@ -55,14 +55,14 @@ mod test_fmt {
|
|||
let src = src.trim_end();
|
||||
let expected = expected.trim_end();
|
||||
|
||||
match module::header().parse(&arena, State::new(src.as_bytes(), Attempting::Module)) {
|
||||
Ok((actual, state)) => {
|
||||
match module::header().parse(&arena, State::new_in(&arena, src.as_bytes(), Attempting::Module)) {
|
||||
Ok((_, actual, state)) => {
|
||||
let mut buf = String::new_in(&arena);
|
||||
|
||||
fmt_module(&mut buf, &actual);
|
||||
|
||||
match module_defs().parse(&arena, state) {
|
||||
Ok((loc_defs, _)) => {
|
||||
Ok((_, loc_defs, _)) => {
|
||||
for loc_def in loc_defs {
|
||||
fmt_def(&mut buf, arena.alloc(loc_def.value), 0);
|
||||
}
|
||||
|
@ -839,6 +839,7 @@ mod test_fmt {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn final_comment_in_empty_record_type_definition() {
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
|
@ -862,6 +863,7 @@ mod test_fmt {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn multiline_inside_empty_record_annotation() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
|
@ -1296,6 +1298,7 @@ mod test_fmt {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn empty_record_with_comment() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
|
@ -1306,6 +1309,7 @@ mod test_fmt {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn empty_record_with_newline() {
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
|
|
|
@ -51,6 +51,9 @@ mod gen_num {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
i : I64
|
||||
i = 64
|
||||
|
||||
|
@ -446,24 +449,6 @@ mod gen_num {
|
|||
-1,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
limitedNegate = \num ->
|
||||
if num == 1 then
|
||||
-1
|
||||
else if num == -1 then
|
||||
1
|
||||
else
|
||||
num
|
||||
|
||||
limitedNegate 1
|
||||
"#
|
||||
),
|
||||
-1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -18,6 +18,7 @@ roc_unify = { path = "../unify" }
|
|||
roc_parse = { path = "../parse" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
inlinable_string = "0.1"
|
||||
parking_lot = { version = "0.11", features = ["deadlock_detection"] }
|
||||
|
|
|
@ -27,7 +27,7 @@ use roc_parse::header::{
|
|||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{self, Fail, Parser};
|
||||
use roc_parse::parser::{self, ParseProblem, Parser};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_solve::module::SolvedModule;
|
||||
use roc_solve::solve;
|
||||
|
@ -762,6 +762,8 @@ enum Msg<'a> {
|
|||
subs: Subs,
|
||||
exposed_to_host: MutMap<Symbol, Variable>,
|
||||
},
|
||||
|
||||
FailedToParse(ParseProblem<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -968,20 +970,20 @@ enum WorkerMsg {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoadingProblem {
|
||||
pub enum LoadingProblem<'a> {
|
||||
FileProblem {
|
||||
filename: PathBuf,
|
||||
error: io::ErrorKind,
|
||||
msg: &'static str,
|
||||
},
|
||||
ParsingFailed {
|
||||
filename: PathBuf,
|
||||
fail: Fail,
|
||||
},
|
||||
ParsingFailed(ParseProblem<'a>),
|
||||
UnexpectedHeader(String),
|
||||
|
||||
MsgChannelDied,
|
||||
ErrJoiningWorkerThreads,
|
||||
TriedToImportAppModule,
|
||||
/// a formatted report of parsing failure
|
||||
ParsingFailedReport(String),
|
||||
}
|
||||
|
||||
pub enum Phases {
|
||||
|
@ -998,7 +1000,7 @@ fn enqueue_task<'a>(
|
|||
injector: &Injector<BuildTask<'a>>,
|
||||
listeners: &[Sender<WorkerMsg>],
|
||||
task: BuildTask<'a>,
|
||||
) -> Result<(), LoadingProblem> {
|
||||
) -> Result<(), LoadingProblem<'a>> {
|
||||
injector.push(task);
|
||||
|
||||
for listener in listeners {
|
||||
|
@ -1010,14 +1012,14 @@ fn enqueue_task<'a>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_and_typecheck(
|
||||
arena: &Bump,
|
||||
pub fn load_and_typecheck<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
stdlib: &StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<LoadedModule, LoadingProblem> {
|
||||
) -> Result<LoadedModule, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_path(arena, filename, stdlib.mode)?;
|
||||
|
@ -1043,7 +1045,7 @@ pub fn load_and_monomorphize<'a>(
|
|||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem> {
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_path(arena, filename, stdlib.mode)?;
|
||||
|
@ -1070,7 +1072,7 @@ pub fn load_and_monomorphize_from_str<'a>(
|
|||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem> {
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_str(arena, filename, src, stdlib.mode)?;
|
||||
|
@ -1101,7 +1103,7 @@ impl<'a> LoadStart<'a> {
|
|||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
mode: Mode,
|
||||
) -> Result<Self, LoadingProblem> {
|
||||
) -> Result<Self, LoadingProblem<'a>> {
|
||||
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
|
||||
|
@ -1134,7 +1136,7 @@ impl<'a> LoadStart<'a> {
|
|||
filename: PathBuf,
|
||||
src: &'a str,
|
||||
mode: Mode,
|
||||
) -> Result<Self, LoadingProblem> {
|
||||
) -> Result<Self, LoadingProblem<'a>> {
|
||||
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
|
||||
|
@ -1220,7 +1222,7 @@ fn load<'a>(
|
|||
exposed_types: SubsByModule,
|
||||
goal_phase: Phase,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<LoadResult<'a>, LoadingProblem>
|
||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>>
|
||||
where
|
||||
{
|
||||
let LoadStart {
|
||||
|
@ -1310,7 +1312,7 @@ where
|
|||
let injector = &injector;
|
||||
|
||||
// Record this thread's handle so the main thread can join it later.
|
||||
thread_scope
|
||||
let res_join_handle = thread_scope
|
||||
.builder()
|
||||
.stack_size(EXPANDED_STACK_SIZE)
|
||||
.spawn(move |_| {
|
||||
|
@ -1322,7 +1324,7 @@ where
|
|||
// shut down the thread, so when the main thread
|
||||
// blocks on joining with all the worker threads,
|
||||
// it can finally exit too!
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
WorkerMsg::TaskAdded => {
|
||||
// Find a task - either from this thread's queue,
|
||||
|
@ -1335,14 +1337,26 @@ where
|
|||
// added. In that case, do nothing, and keep waiting
|
||||
// until we receive a Shutdown message.
|
||||
if let Some(task) = find_task(&worker, injector, stealers) {
|
||||
run_task(
|
||||
let result = run_task(
|
||||
task,
|
||||
worker_arena,
|
||||
src_dir,
|
||||
msg_tx.clone(),
|
||||
ptr_bytes,
|
||||
)
|
||||
.expect("Msg channel closed unexpectedly.");
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(()) => {}
|
||||
Err(LoadingProblem::MsgChannelDied) => {
|
||||
panic!("Msg channel closed unexpectedly.")
|
||||
}
|
||||
Err(LoadingProblem::ParsingFailed(problem)) => {
|
||||
msg_tx.send(Msg::FailedToParse(problem)).unwrap();
|
||||
}
|
||||
Err(other) => {
|
||||
return Err(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1351,8 +1365,11 @@ where
|
|||
// Needed to prevent a borrow checker error about this closure
|
||||
// outliving its enclosing function.
|
||||
drop(worker_msg_rx);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
res_join_handle.unwrap();
|
||||
}
|
||||
|
||||
let mut state = State {
|
||||
|
@ -1440,6 +1457,51 @@ where
|
|||
exposed_to_host,
|
||||
)));
|
||||
}
|
||||
Msg::FailedToParse(problem) => {
|
||||
// Shut down all the worker threads.
|
||||
for listener in worker_listeners {
|
||||
listener
|
||||
.send(WorkerMsg::Shutdown)
|
||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||
}
|
||||
|
||||
use roc_reporting::report::{
|
||||
parse_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||
};
|
||||
|
||||
// TODO this is not in fact safe
|
||||
let src = unsafe { from_utf8_unchecked(problem.bytes) };
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
|
||||
let palette = DEFAULT_PALETTE;
|
||||
|
||||
let mut module_ids = Arc::try_unwrap(state.arc_modules)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("There were still outstanding Arc references to module_ids")
|
||||
})
|
||||
.into_inner()
|
||||
.into_module_ids();
|
||||
|
||||
let module_id =
|
||||
module_ids.get_or_insert(&"find module name somehow?".into());
|
||||
|
||||
let interns = Interns {
|
||||
module_ids,
|
||||
all_ident_ids: state.constrained_ident_ids,
|
||||
};
|
||||
|
||||
// Report parsing and canonicalization problems
|
||||
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
|
||||
|
||||
let starting_line = 0;
|
||||
let report =
|
||||
parse_problem(&alloc, problem.filename.clone(), starting_line, problem);
|
||||
let mut buf = String::new();
|
||||
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
return Err(LoadingProblem::ParsingFailedReport(buf));
|
||||
}
|
||||
msg => {
|
||||
// This is where most of the main thread's work gets done.
|
||||
// Everything up to this point has been setting up the threading
|
||||
|
@ -1468,7 +1530,7 @@ fn start_tasks<'a>(
|
|||
state: &mut State<'a>,
|
||||
injector: &Injector<BuildTask<'a>>,
|
||||
worker_listeners: &'a [Sender<WorkerMsg>],
|
||||
) -> Result<(), LoadingProblem> {
|
||||
) -> Result<(), LoadingProblem<'a>> {
|
||||
for (module_id, phase) in work {
|
||||
for task in start_phase(module_id, phase, state) {
|
||||
enqueue_task(&injector, worker_listeners, task)?
|
||||
|
@ -1485,7 +1547,7 @@ fn update<'a>(
|
|||
injector: &Injector<BuildTask<'a>>,
|
||||
worker_listeners: &'a [Sender<WorkerMsg>],
|
||||
arena: &'a Bump,
|
||||
) -> Result<State<'a>, LoadingProblem> {
|
||||
) -> Result<State<'a>, LoadingProblem<'a>> {
|
||||
use self::Msg::*;
|
||||
|
||||
match msg {
|
||||
|
@ -1942,6 +2004,9 @@ fn update<'a>(
|
|||
Msg::FinishedAllSpecialization { .. } => {
|
||||
unreachable!();
|
||||
}
|
||||
Msg::FailedToParse(_) => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2064,7 +2129,7 @@ fn load_pkg_config<'a>(
|
|||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
) -> Result<Msg<'a>, LoadingProblem> {
|
||||
) -> Result<Msg<'a>, LoadingProblem<'a>> {
|
||||
let module_start_time = SystemTime::now();
|
||||
|
||||
let filename = PathBuf::from(src_dir);
|
||||
|
@ -2074,9 +2139,10 @@ fn load_pkg_config<'a>(
|
|||
let file_io_duration = file_io_start.elapsed().unwrap();
|
||||
|
||||
match file {
|
||||
Ok(bytes) => {
|
||||
Ok(bytes_vec) => {
|
||||
let parse_start = SystemTime::now();
|
||||
let parse_state = parser::State::new(arena.alloc(bytes), Attempting::Module);
|
||||
let bytes = arena.alloc(bytes_vec);
|
||||
let parse_state = parser::State::new_in(arena, bytes, Attempting::Module);
|
||||
let parsed = roc_parse::module::header().parse(&arena, parse_state);
|
||||
let parse_header_duration = parse_start.elapsed().unwrap();
|
||||
|
||||
|
@ -2091,19 +2157,19 @@ fn load_pkg_config<'a>(
|
|||
effect_module_timing.parse_header = parse_header_duration;
|
||||
|
||||
match parsed {
|
||||
Ok((ast::Module::Interface { header }, _parse_state)) => {
|
||||
Ok((_, ast::Module::Interface { header }, _parse_state)) => {
|
||||
Err(LoadingProblem::UnexpectedHeader(format!(
|
||||
"expected platform/package module, got Interface with header\n{:?}",
|
||||
header
|
||||
)))
|
||||
}
|
||||
Ok((ast::Module::App { header }, _parse_state)) => {
|
||||
Ok((_, ast::Module::App { header }, _parse_state)) => {
|
||||
Err(LoadingProblem::UnexpectedHeader(format!(
|
||||
"expected platform/package module, got App with header\n{:?}",
|
||||
header
|
||||
)))
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, parser_state)) => {
|
||||
Ok((_, ast::Module::Platform { header }, parser_state)) => {
|
||||
// make a Pkg-Config module that ultimately exposes `main` to the host
|
||||
let pkg_config_module_msg = fabricate_pkg_config_module(
|
||||
arena,
|
||||
|
@ -2131,7 +2197,9 @@ fn load_pkg_config<'a>(
|
|||
|
||||
Ok(Msg::Many(vec![effects_module_msg, pkg_config_module_msg]))
|
||||
}
|
||||
Err((fail, _)) => Err(LoadingProblem::ParsingFailed { filename, fail }),
|
||||
Err((_, fail, _)) => Err(LoadingProblem::ParsingFailed(
|
||||
fail.into_parse_problem(filename, bytes),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2152,7 +2220,7 @@ fn load_module<'a>(
|
|||
arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let module_start_time = SystemTime::now();
|
||||
let mut filename = PathBuf::new();
|
||||
|
||||
|
@ -2240,9 +2308,9 @@ fn parse_header<'a>(
|
|||
mode: Mode,
|
||||
src_bytes: &'a [u8],
|
||||
start_time: SystemTime,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let parse_start = SystemTime::now();
|
||||
let parse_state = parser::State::new(src_bytes, Attempting::Module);
|
||||
let parse_state = parser::State::new_in(arena, src_bytes, Attempting::Module);
|
||||
let parsed = roc_parse::module::header().parse(&arena, parse_state);
|
||||
let parse_header_duration = parse_start.elapsed().unwrap();
|
||||
|
||||
|
@ -2253,7 +2321,7 @@ fn parse_header<'a>(
|
|||
module_timing.parse_header = parse_header_duration;
|
||||
|
||||
match parsed {
|
||||
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||
Ok((_, ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: ModuleNameEnum::Interface(header.name.value),
|
||||
|
@ -2269,7 +2337,7 @@ fn parse_header<'a>(
|
|||
ident_ids_by_module,
|
||||
module_timing,
|
||||
)),
|
||||
Ok((ast::Module::App { header }, parse_state)) => {
|
||||
Ok((_, ast::Module::App { header }, parse_state)) => {
|
||||
let mut pkg_config_dir = filename.clone();
|
||||
pkg_config_dir.pop();
|
||||
|
||||
|
@ -2367,7 +2435,7 @@ fn parse_header<'a>(
|
|||
},
|
||||
}
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
||||
Ok((_, ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
||||
arena,
|
||||
&"",
|
||||
module_ids,
|
||||
|
@ -2376,7 +2444,9 @@ fn parse_header<'a>(
|
|||
header,
|
||||
module_timing,
|
||||
),
|
||||
Err((fail, _)) => Err(LoadingProblem::ParsingFailed { filename, fail }),
|
||||
Err((_, fail, _)) => Err(LoadingProblem::ParsingFailed(
|
||||
fail.into_parse_problem(filename, src_bytes),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2389,7 +2459,7 @@ fn load_filename<'a>(
|
|||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_start_time: SystemTime,
|
||||
mode: Mode,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let file_io_start = SystemTime::now();
|
||||
let file = fs::read(&filename);
|
||||
let file_io_duration = file_io_start.elapsed().unwrap();
|
||||
|
@ -2425,7 +2495,7 @@ fn load_from_str<'a>(
|
|||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_start_time: SystemTime,
|
||||
mode: Mode,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let file_io_start = SystemTime::now();
|
||||
let file_io_duration = file_io_start.elapsed().unwrap();
|
||||
|
||||
|
@ -2993,7 +3063,7 @@ fn fabricate_pkg_config_module<'a>(
|
|||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
header: &PlatformHeader<'a>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let provides: &'a [Located<ExposesEntry<'a, &'a str>>] =
|
||||
header.provides.clone().into_bump_slice();
|
||||
|
||||
|
@ -3022,7 +3092,7 @@ fn fabricate_effects_module<'a>(
|
|||
mode: Mode,
|
||||
header: PlatformHeader<'a>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem<'a>> {
|
||||
let num_exposes = header.provides.len() + 1;
|
||||
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
|
||||
|
||||
|
@ -3300,7 +3370,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
aliases: MutMap<Symbol, Alias>,
|
||||
mode: Mode,
|
||||
parsed: ParsedModule<'a>,
|
||||
) -> Result<Msg<'a>, LoadingProblem> {
|
||||
) -> Result<Msg<'a>, LoadingProblem<'a>> {
|
||||
let canonicalize_start = SystemTime::now();
|
||||
|
||||
let ParsedModule {
|
||||
|
@ -3381,13 +3451,18 @@ fn canonicalize_and_constrain<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, LoadingProblem> {
|
||||
fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, LoadingProblem<'a>> {
|
||||
let mut module_timing = header.module_timing;
|
||||
let parse_start = SystemTime::now();
|
||||
let parse_state = parser::State::new(&header.src, Attempting::Module);
|
||||
let (parsed_defs, _) = module_defs()
|
||||
.parse(&arena, parse_state)
|
||||
.expect("TODO gracefully handle parse error on module defs. IMPORTANT: Bail out entirely if there are any BadUtf8 problems! That means the whole source file is not valid UTF-8 and any other errors we report may get mis-reported. We rely on this for safety in an `unsafe` block later on in this function.");
|
||||
let parse_state = parser::State::new_in(arena, &header.src, Attempting::Module);
|
||||
let parsed_defs = match module_defs().parse(&arena, parse_state) {
|
||||
Ok((_, success, _state)) => success,
|
||||
Err((_, fail, _)) => {
|
||||
return Err(LoadingProblem::ParsingFailed(
|
||||
fail.into_parse_problem(header.module_path, header.src),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let parsed_defs = parsed_defs.into_bump_slice();
|
||||
|
||||
|
@ -3767,7 +3842,7 @@ fn run_task<'a>(
|
|||
src_dir: &Path,
|
||||
msg_tx: MsgSender<'a>,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<(), LoadingProblem> {
|
||||
) -> Result<(), LoadingProblem<'a>> {
|
||||
use BuildTask::*;
|
||||
|
||||
let msg = match task {
|
||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::ident::Ident;
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_solve::solve;
|
||||
|
@ -62,19 +62,22 @@ where
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -124,7 +124,7 @@ mod test_load {
|
|||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
arena.alloc(roc_builtins::std::standard_stdlib()),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
|
@ -287,7 +287,7 @@ mod test_load {
|
|||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
arena.alloc(roc_builtins::std::standard_stdlib()),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::ident::Ident;
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_solve::solve;
|
||||
|
@ -47,19 +47,22 @@ pub fn infer_expr(
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -611,6 +611,7 @@ pub enum Attempting {
|
|||
TypeVariable,
|
||||
WhenCondition,
|
||||
WhenBranch,
|
||||
TODO,
|
||||
}
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::ast::CommentOrNewline::{self, *};
|
||||
use crate::ast::{Attempting, Spaceable};
|
||||
use crate::parser::{
|
||||
self, and, ascii_char, ascii_string, optional, parse_utf8, peek_utf8_char, then, unexpected,
|
||||
unexpected_eof, FailReason, Parser, State,
|
||||
self, and, ascii_char, ascii_string, backtrackable, optional, parse_utf8, peek_utf8_char, then,
|
||||
unexpected, unexpected_eof, FailReason, Parser,
|
||||
Progress::{self, *},
|
||||
State,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -130,7 +132,7 @@ where
|
|||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
and!(space1(min_indent), parser),
|
||||
and!(backtrackable(space1(min_indent)), parser),
|
||||
|arena, (space_list, loc_expr)| {
|
||||
if space_list.is_empty() {
|
||||
loc_expr
|
||||
|
@ -215,9 +217,9 @@ enum LineState {
|
|||
pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
||||
then(
|
||||
and!(ascii_char(b'#'), optional(ascii_string("# "))),
|
||||
|_arena: &'a Bump, state: State<'a>, (_, opt_doc)| {
|
||||
|arena: &'a Bump, state: State<'a>, _, (_, opt_doc)| {
|
||||
if opt_doc != None {
|
||||
return Err(unexpected(3, state, Attempting::LineComment));
|
||||
return Err(unexpected(arena, 3, Attempting::LineComment, state));
|
||||
}
|
||||
let mut length = 0;
|
||||
|
||||
|
@ -230,10 +232,10 @@ pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
|||
}
|
||||
|
||||
let comment = &state.bytes[..length];
|
||||
let state = state.advance_without_indenting(length + 1)?;
|
||||
let state = state.advance_without_indenting(arena, length + 1)?;
|
||||
match parse_utf8(comment) {
|
||||
Ok(comment_str) => Ok((comment_str, state)),
|
||||
Err(reason) => state.fail(reason),
|
||||
Ok(comment_str) => Ok((MadeProgress, comment_str, state)),
|
||||
Err(reason) => state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -241,9 +243,9 @@ pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
||||
move |_arena: &'a Bump, state: State<'a>| {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
if spaces_expected == 0 {
|
||||
return Ok(((), state));
|
||||
return Ok((NoProgress, (), state));
|
||||
}
|
||||
|
||||
let mut state = state;
|
||||
|
@ -253,31 +255,34 @@ pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
|||
match peek_utf8_char(&state) {
|
||||
Ok((' ', _)) => {
|
||||
spaces_seen += 1;
|
||||
state = state.advance_spaces(1)?;
|
||||
state = state.advance_spaces(arena, 1)?;
|
||||
if spaces_seen == spaces_expected {
|
||||
return Ok(((), state));
|
||||
return Ok((MadeProgress, (), state));
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
return Err(unexpected(
|
||||
arena,
|
||||
spaces_seen.into(),
|
||||
Attempting::TODO,
|
||||
state.clone(),
|
||||
state.attempting,
|
||||
));
|
||||
}
|
||||
|
||||
Err(FailReason::BadUtf8) => {
|
||||
// If we hit an invalid UTF-8 character, bail out immediately.
|
||||
return state.fail(FailReason::BadUtf8);
|
||||
let progress = Progress::progress_when(spaces_seen != 0);
|
||||
return state.fail(arena, progress, FailReason::BadUtf8);
|
||||
}
|
||||
Err(_) => {
|
||||
if spaces_seen == 0 {
|
||||
return Err(unexpected_eof(0, state.attempting, state));
|
||||
return Err(unexpected_eof(arena, state, 0));
|
||||
} else {
|
||||
return Err(unexpected(
|
||||
arena,
|
||||
spaces_seen.into(),
|
||||
Attempting::TODO,
|
||||
state.clone(),
|
||||
state.attempting,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -285,12 +290,13 @@ pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
|||
}
|
||||
|
||||
if spaces_seen == 0 {
|
||||
Err(unexpected_eof(0, state.attempting, state))
|
||||
Err(unexpected_eof(arena, state, 0))
|
||||
} else {
|
||||
Err(unexpected(
|
||||
arena,
|
||||
spaces_seen.into(),
|
||||
state.clone(),
|
||||
state.attempting,
|
||||
Attempting::TODO,
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -310,6 +316,8 @@ fn spaces<'a>(
|
|||
let mut state = state;
|
||||
let mut any_newlines = false;
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((ch, utf8_len)) => {
|
||||
|
@ -321,15 +329,17 @@ fn spaces<'a>(
|
|||
' ' => {
|
||||
// Don't check indentation here; it might not be enough
|
||||
// indentation yet, but maybe it will be after more spaces happen!
|
||||
state = state.advance_spaces(1)?;
|
||||
state = state.advance_spaces(arena, 1)?;
|
||||
}
|
||||
'\r' => {
|
||||
// Ignore carriage returns.
|
||||
state = state.advance_spaces(1)?;
|
||||
state = state.advance_spaces(arena, 1)?;
|
||||
}
|
||||
'\n' => {
|
||||
// No need to check indentation because we're about to reset it anyway.
|
||||
state = state.newline()?;
|
||||
// don't need to check the indent here since we'll reset it
|
||||
// anyway
|
||||
|
||||
state = state.newline(arena)?;
|
||||
|
||||
// Newlines only get added to the list when they're outside comments.
|
||||
space_list.push(Newline);
|
||||
|
@ -339,10 +349,14 @@ fn spaces<'a>(
|
|||
'#' => {
|
||||
// Check indentation to make sure we were indented enough
|
||||
// before this comment began.
|
||||
let progress =
|
||||
Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
state = state
|
||||
.check_indent(min_indent)
|
||||
.map_err(|(fail, _)| (fail, original_state.clone()))?
|
||||
.advance_without_indenting(1)?;
|
||||
.check_indent(arena, min_indent)
|
||||
.map_err(|(fail, _)| {
|
||||
(progress, fail, original_state.clone())
|
||||
})?
|
||||
.advance_without_indenting(arena, 1)?;
|
||||
|
||||
// We're now parsing a line comment!
|
||||
line_state = LineState::Comment;
|
||||
|
@ -351,7 +365,7 @@ fn spaces<'a>(
|
|||
return if require_at_least_one && bytes_parsed <= 1 {
|
||||
// We've parsed 1 char and it was not a space,
|
||||
// but we require parsing at least one space!
|
||||
Err(unexpected(0, state.clone(), state.attempting))
|
||||
Err(unexpected(arena, 0, Attempting::TODO, state.clone()))
|
||||
} else {
|
||||
// First make sure we were indented enough!
|
||||
//
|
||||
|
@ -360,13 +374,19 @@ fn spaces<'a>(
|
|||
// It's actively important for correctness that we skip
|
||||
// this check if there are no newlines, because otherwise
|
||||
// we would have false positives for single-line defs.)
|
||||
let progress = Progress::from_lengths(
|
||||
start_bytes_len,
|
||||
state.bytes.len(),
|
||||
);
|
||||
if any_newlines {
|
||||
state = state
|
||||
.check_indent(min_indent)
|
||||
.map_err(|(fail, _)| (fail, original_state))?;
|
||||
state = state.check_indent(arena, min_indent).map_err(
|
||||
|(fail, _)| {
|
||||
(progress, fail, original_state.clone())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok((space_list.into_bump_slice(), state))
|
||||
Ok((progress, space_list.into_bump_slice(), state))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +395,7 @@ fn spaces<'a>(
|
|||
match ch {
|
||||
' ' => {
|
||||
// If we're in a line comment, this won't affect indentation anyway.
|
||||
state = state.advance_without_indenting(1)?;
|
||||
state = state.advance_without_indenting(arena, 1)?;
|
||||
|
||||
if comment_line_buf.len() == 1 {
|
||||
match comment_line_buf.chars().next() {
|
||||
|
@ -400,7 +420,7 @@ fn spaces<'a>(
|
|||
}
|
||||
}
|
||||
'\n' => {
|
||||
state = state.newline()?;
|
||||
state = state.newline(arena)?;
|
||||
|
||||
match (comment_line_buf.len(), comment_line_buf.chars().next())
|
||||
{
|
||||
|
@ -425,7 +445,8 @@ fn spaces<'a>(
|
|||
}
|
||||
nonblank => {
|
||||
// Chars can have btye lengths of more than 1!
|
||||
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
||||
state = state
|
||||
.advance_without_indenting(arena, nonblank.len_utf8())?;
|
||||
|
||||
comment_line_buf.push(nonblank);
|
||||
}
|
||||
|
@ -435,12 +456,12 @@ fn spaces<'a>(
|
|||
match ch {
|
||||
' ' => {
|
||||
// If we're in a doc comment, this won't affect indentation anyway.
|
||||
state = state.advance_without_indenting(1)?;
|
||||
state = state.advance_without_indenting(arena, 1)?;
|
||||
|
||||
comment_line_buf.push(ch);
|
||||
}
|
||||
'\n' => {
|
||||
state = state.newline()?;
|
||||
state = state.newline(arena)?;
|
||||
|
||||
// This was a newline, so end this doc comment.
|
||||
space_list.push(DocComment(comment_line_buf.into_bump_str()));
|
||||
|
@ -449,7 +470,7 @@ fn spaces<'a>(
|
|||
line_state = LineState::Normal;
|
||||
}
|
||||
nonblank => {
|
||||
state = state.advance_without_indenting(utf8_len)?;
|
||||
state = state.advance_without_indenting(arena, utf8_len)?;
|
||||
|
||||
comment_line_buf.push(nonblank);
|
||||
}
|
||||
|
@ -459,11 +480,12 @@ fn spaces<'a>(
|
|||
}
|
||||
Err(FailReason::BadUtf8) => {
|
||||
// If we hit an invalid UTF-8 character, bail out immediately.
|
||||
return state.fail(FailReason::BadUtf8);
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, FailReason::BadUtf8);
|
||||
}
|
||||
Err(_) => {
|
||||
if require_at_least_one && bytes_parsed == 0 {
|
||||
return Err(unexpected_eof(0, state.attempting, state));
|
||||
return Err(unexpected_eof(arena, state, 0));
|
||||
} else {
|
||||
let space_slice = space_list.into_bump_slice();
|
||||
|
||||
|
@ -474,16 +496,18 @@ fn spaces<'a>(
|
|||
// It's actively important for correctness that we skip
|
||||
// this check if there are no newlines, because otherwise
|
||||
// we would have false positives for single-line defs.)
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
if any_newlines {
|
||||
return Ok((
|
||||
progress,
|
||||
space_slice,
|
||||
state
|
||||
.check_indent(min_indent)
|
||||
.map_err(|(fail, _)| (fail, original_state))?,
|
||||
.check_indent(arena, min_indent)
|
||||
.map_err(|(fail, _)| (progress, fail, original_state))?,
|
||||
));
|
||||
}
|
||||
|
||||
return Ok((space_slice, state));
|
||||
return Ok((progress, space_slice, state));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -491,7 +515,7 @@ fn spaces<'a>(
|
|||
|
||||
// If we didn't parse anything, return unexpected EOF
|
||||
if require_at_least_one && original_state.bytes.len() == state.bytes.len() {
|
||||
Err(unexpected_eof(0, state.attempting, state))
|
||||
Err(unexpected_eof(arena, state, 0))
|
||||
} else {
|
||||
// First make sure we were indented enough!
|
||||
//
|
||||
|
@ -500,13 +524,14 @@ fn spaces<'a>(
|
|||
// It's actively important for correctness that we skip
|
||||
// this check if there are no newlines, because otherwise
|
||||
// we would have false positives for single-line defs.)
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
if any_newlines {
|
||||
state = state
|
||||
.check_indent(min_indent)
|
||||
.map_err(|(fail, _)| (fail, original_state))?;
|
||||
.check_indent(arena, min_indent)
|
||||
.map_err(|(fail, _)| (progress, fail, original_state))?;
|
||||
}
|
||||
|
||||
Ok((space_list.into_bump_slice(), state))
|
||||
Ok((progress, space_list.into_bump_slice(), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@ use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
|||
use crate::blankspace::space0;
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::module::package_name;
|
||||
use crate::parser::{ascii_char, optional, Either, Parser};
|
||||
use crate::parser::{ascii_char, optional, Either, Parser, Progress::*, State};
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use inlinable_string::InlinableString;
|
||||
|
@ -245,12 +245,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
|
|||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||
//
|
||||
// (Indirect dependencies don't have a shorthand.)
|
||||
let (opt_shorthand, state) = optional(and!(
|
||||
let (_, opt_shorthand, state) = optional(and!(
|
||||
skip_second!(lowercase_ident(), ascii_char(b':')),
|
||||
space0(1)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
let (package_or_path, state) = loc!(package_or_path()).parse(arena, state)?;
|
||||
let (_, package_or_path, state) = loc!(package_or_path()).parse(arena, state)?;
|
||||
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
||||
shorthand,
|
||||
|
@ -264,7 +265,7 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
|
|||
},
|
||||
};
|
||||
|
||||
Ok((entry, state))
|
||||
Ok((MadeProgress, entry, state))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::ast::Attempting;
|
||||
use crate::keyword;
|
||||
use crate::parser::{peek_utf8_char, unexpected, Fail, FailReason, ParseResult, Parser, State};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{peek_utf8_char, unexpected, Bag, FailReason, ParseResult, Parser, State};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -78,6 +79,8 @@ pub fn parse_ident<'a>(
|
|||
let is_accessor_fn;
|
||||
let mut is_private_tag = false;
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
||||
// Identifiers and accessor functions must start with either a letter or a dot.
|
||||
// If this starts with neither, it must be something else!
|
||||
match peek_utf8_char(&state) {
|
||||
|
@ -88,20 +91,20 @@ pub fn parse_ident<'a>(
|
|||
is_capitalized = first_ch.is_uppercase();
|
||||
is_accessor_fn = false;
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
} else if first_ch == '.' {
|
||||
is_capitalized = false;
|
||||
is_accessor_fn = true;
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
} else if first_ch == '@' {
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
|
||||
// '@' must always be followed by a capital letter!
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((next_ch, next_bytes_parsed)) => {
|
||||
if next_ch.is_uppercase() {
|
||||
state = state.advance_without_indenting(next_bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, next_bytes_parsed)?;
|
||||
|
||||
part_buf.push('@');
|
||||
part_buf.push(next_ch);
|
||||
|
@ -111,19 +114,26 @@ pub fn parse_ident<'a>(
|
|||
is_accessor_fn = false;
|
||||
} else {
|
||||
return Err(unexpected(
|
||||
arena,
|
||||
bytes_parsed + next_bytes_parsed,
|
||||
state,
|
||||
Attempting::Identifier,
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(unexpected(0, state, Attempting::Identifier));
|
||||
return Err(unexpected(arena, 0, Attempting::Identifier, state));
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
}
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
|
@ -183,9 +193,12 @@ pub fn parse_ident<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +254,7 @@ pub fn parse_ident<'a>(
|
|||
// We had neither capitalized nor noncapitalized parts,
|
||||
// yet we made it this far. The only explanation is that this was
|
||||
// a stray '.' drifting through the cosmos.
|
||||
return Err(unexpected(1, state, Attempting::Identifier));
|
||||
return Err(unexpected(arena, 1, Attempting::Identifier, state));
|
||||
}
|
||||
}
|
||||
} else if is_private_tag {
|
||||
|
@ -255,7 +268,9 @@ pub fn parse_ident<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
Ok(((answer, None), state))
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
debug_assert_eq!(progress, Progress::MadeProgress,);
|
||||
Ok((Progress::MadeProgress, (answer, None), state))
|
||||
}
|
||||
|
||||
fn malformed<'a>(
|
||||
|
@ -293,13 +308,14 @@ fn malformed<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
(Ident::Malformed(full_string.into_bump_str()), next_char),
|
||||
state,
|
||||
))
|
||||
|
@ -308,9 +324,9 @@ fn malformed<'a>(
|
|||
pub fn ident<'a>() -> impl Parser<'a, Ident<'a>> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
// Discard next_char; we don't need it.
|
||||
let ((string, _), state) = parse_ident(arena, state)?;
|
||||
let (progress, (string, _), state) = parse_ident(arena, state)?;
|
||||
|
||||
Ok((string, state))
|
||||
Ok((progress, string, state))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,19 +339,19 @@ where
|
|||
let (first_letter, bytes_parsed) = match peek_utf8_char(&state) {
|
||||
Ok((first_letter, bytes_parsed)) => {
|
||||
if !pred(first_letter) {
|
||||
return Err(unexpected(0, state, Attempting::RecordFieldLabel));
|
||||
return Err(unexpected(arena, 0, Attempting::RecordFieldLabel, state));
|
||||
}
|
||||
|
||||
(first_letter, bytes_parsed)
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, NoProgress, reason),
|
||||
};
|
||||
|
||||
let mut buf = String::with_capacity_in(1, arena);
|
||||
|
||||
buf.push(first_letter);
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
match peek_utf8_char(&state) {
|
||||
|
@ -348,17 +364,17 @@ where
|
|||
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||
buf.push(ch);
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
} else {
|
||||
// This is the end of the field. We're done!
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
};
|
||||
}
|
||||
|
||||
Ok((buf.into_bump_str(), state))
|
||||
Ok((MadeProgress, buf.into_bump_str(), state))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,9 +384,12 @@ where
|
|||
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
|
||||
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||
move |arena, state| {
|
||||
let (ident, state) =
|
||||
let (progress, ident, state) =
|
||||
global_tag_or_ident(|first_char| first_char.is_lowercase()).parse(arena, state)?;
|
||||
|
||||
// to parse a valid ident, progress must be made
|
||||
debug_assert_eq!(progress, MadeProgress);
|
||||
|
||||
if (ident == keyword::IF)
|
||||
|| (ident == keyword::THEN)
|
||||
|| (ident == keyword::ELSE)
|
||||
|
@ -381,14 +400,12 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
|||
// TODO Calculate the correct region based on state
|
||||
let region = Region::zero();
|
||||
Err((
|
||||
Fail {
|
||||
reason: FailReason::ReservedKeyword(region),
|
||||
attempting: Attempting::Identifier,
|
||||
},
|
||||
MadeProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ReservedKeyword(region)),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
Ok((ident, state))
|
||||
Ok((MadeProgress, ident, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ use crate::header::{
|
|||
TypedIdent,
|
||||
};
|
||||
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
|
||||
unexpected_eof, Either, ParseResult, Parser, State,
|
||||
self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char,
|
||||
peek_utf8_char_at, unexpected, unexpected_eof, Either, ParseResult, Parser, State,
|
||||
};
|
||||
use crate::string_literal;
|
||||
use crate::type_annotation;
|
||||
|
@ -95,16 +96,20 @@ pub fn parse_package_part<'a>(arena: &'a Bump, mut state: State<'a>) -> ParseRes
|
|||
if ch == '-' || ch.is_ascii_alphanumeric() {
|
||||
part_buf.push(ch);
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
} else {
|
||||
return Ok((part_buf.into_bump_str(), state));
|
||||
let progress = Progress::progress_when(!part_buf.is_empty());
|
||||
return Ok((progress, part_buf.into_bump_str(), state));
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => {
|
||||
let progress = Progress::progress_when(!part_buf.is_empty());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(unexpected_eof(0, state.attempting, state))
|
||||
Err(unexpected_eof(arena, state, 0))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -113,14 +118,14 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
match peek_utf8_char(&state) {
|
||||
Ok((first_letter, bytes_parsed)) => {
|
||||
if !first_letter.is_uppercase() {
|
||||
return Err(unexpected(0, state, Attempting::Module));
|
||||
return Err(unexpected(arena, 0, Attempting::Module, state));
|
||||
};
|
||||
|
||||
let mut buf = String::with_capacity_in(4, arena);
|
||||
|
||||
buf.push(first_letter);
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
match peek_utf8_char(&state) {
|
||||
|
@ -131,7 +136,7 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
// * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric()
|
||||
// * A '.' separating module parts
|
||||
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
|
||||
buf.push(ch);
|
||||
} else if ch == '.' {
|
||||
|
@ -143,6 +148,7 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
buf.push(next);
|
||||
|
||||
state = state.advance_without_indenting(
|
||||
arena,
|
||||
bytes_parsed + next_bytes_parsed,
|
||||
)?;
|
||||
} else {
|
||||
|
@ -151,25 +157,26 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
// There may be an identifier after this '.',
|
||||
// e.g. "baz" in `Foo.Bar.baz`
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
ModuleName::new(buf.into_bump_str()),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
} else {
|
||||
// This is the end of the module name. We're done!
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((ModuleName::new(buf.into_bump_str()), state))
|
||||
Ok((MadeProgress, ModuleName::new(buf.into_bump_str()), state))
|
||||
}
|
||||
Err(reason) => state.fail(reason),
|
||||
Err(reason) => state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +297,8 @@ pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
|
||||
zero_or_more!(space0_around(loc(def(0)), 0))
|
||||
// force that we pare until the end of the input
|
||||
skip_second!(zero_or_more!(space0_around(loc(def(0)), 0)), end_of_file())
|
||||
}
|
||||
|
||||
struct ProvidesTo<'a> {
|
||||
|
@ -307,7 +315,10 @@ struct ProvidesTo<'a> {
|
|||
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||
and!(
|
||||
skip_second!(backtrackable(space1(1)), ascii_string("provides")),
|
||||
space1(1)
|
||||
),
|
||||
and!(
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
|
@ -434,6 +445,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Packages<'a> {
|
||||
entries: Vec<'a, Located<PackageEntry<'a>>>,
|
||||
|
||||
|
@ -445,7 +457,10 @@ struct Packages<'a> {
|
|||
fn packages<'a>() -> impl Parser<'a, Packages<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("packages")), space1(1)),
|
||||
and!(
|
||||
skip_second!(backtrackable(space1(1)), ascii_string("packages")),
|
||||
space1(1)
|
||||
),
|
||||
collection!(
|
||||
ascii_char(b'{'),
|
||||
loc!(package_entry()),
|
||||
|
@ -473,7 +488,10 @@ fn imports<'a>() -> impl Parser<
|
|||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("imports")), space1(1)),
|
||||
and!(
|
||||
skip_second!(backtrackable(space1(1)), ascii_string("imports")),
|
||||
space1(1)
|
||||
),
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
loc!(imports_entry()),
|
||||
|
@ -487,17 +505,17 @@ fn imports<'a>() -> impl Parser<
|
|||
#[inline(always)]
|
||||
fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
||||
move |arena, state| {
|
||||
let (spaces_before_effects_keyword, state) =
|
||||
let (_, spaces_before_effects_keyword, state) =
|
||||
skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?;
|
||||
let (spaces_after_effects_keyword, state) = space1(0).parse(arena, state)?;
|
||||
let (_, spaces_after_effects_keyword, state) = space1(0).parse(arena, state)?;
|
||||
|
||||
// e.g. `fx.`
|
||||
let (type_shortname, state) =
|
||||
let (_, type_shortname, state) =
|
||||
skip_second!(lowercase_ident(), ascii_char(b'.')).parse(arena, state)?;
|
||||
|
||||
let ((type_name, spaces_after_type_name), state) =
|
||||
let (_, (type_name, spaces_after_type_name), state) =
|
||||
and!(uppercase_ident(), space1(0)).parse(arena, state)?;
|
||||
let (entries, state) = collection!(
|
||||
let (_, entries, state) = collection!(
|
||||
ascii_char(b'{'),
|
||||
loc!(typed_ident()),
|
||||
ascii_char(b','),
|
||||
|
@ -507,6 +525,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
|||
.parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Effects {
|
||||
spaces_before_effects_keyword,
|
||||
spaces_after_effects_keyword,
|
||||
|
@ -524,11 +543,11 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
|||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
||||
move |arena, state| {
|
||||
// You must have a field name, e.g. "email"
|
||||
let (ident, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
||||
let (_, ident, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
||||
|
||||
let (spaces_before_colon, state) = space0(0).parse(arena, state)?;
|
||||
let (_, spaces_before_colon, state) = space0(0).parse(arena, state)?;
|
||||
|
||||
let (ann, state) = skip_first!(
|
||||
let (_, ann, state) = skip_first!(
|
||||
ascii_char(b':'),
|
||||
space0_before(type_annotation::located(0), 0)
|
||||
)
|
||||
|
@ -539,6 +558,7 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
|||
// printLine : Str -> Effect {}
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
TypedIdent::Entry {
|
||||
ident,
|
||||
spaces_before_colon,
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
use crate::ast::{Attempting, Base, Expr};
|
||||
use crate::parser::{parse_utf8, unexpected, unexpected_eof, ParseResult, Parser, State};
|
||||
use crate::parser::{parse_utf8, unexpected, unexpected_eof, ParseResult, Parser, Progress, State};
|
||||
use bumpalo::Bump;
|
||||
use std::char;
|
||||
use std::str::from_utf8_unchecked;
|
||||
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||
move |_arena, state: State<'a>| {
|
||||
move |arena, state: State<'a>| {
|
||||
let bytes = &mut state.bytes.iter();
|
||||
|
||||
match bytes.next() {
|
||||
Some(&first_byte) => {
|
||||
// Number literals must start with either an '-' or a digit.
|
||||
if first_byte == b'-' || (first_byte as char).is_ascii_digit() {
|
||||
parse_number_literal(first_byte as char, bytes, state)
|
||||
parse_number_literal(first_byte as char, bytes, arena, state)
|
||||
} else {
|
||||
Err(unexpected(1, state, Attempting::NumberLiteral))
|
||||
Err(unexpected(arena, 1, Attempting::NumberLiteral, state))
|
||||
}
|
||||
}
|
||||
None => Err(unexpected_eof(0, state.attempting, state)),
|
||||
None => Err(unexpected_eof(arena, state, 0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +26,7 @@ pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
|||
fn parse_number_literal<'a, I>(
|
||||
first_ch: char,
|
||||
bytes: &mut I,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>>
|
||||
where
|
||||
|
@ -42,9 +44,10 @@ where
|
|||
for &next_byte in bytes {
|
||||
let err_unexpected = || {
|
||||
Err(unexpected(
|
||||
arena,
|
||||
bytes_parsed,
|
||||
state.clone(),
|
||||
Attempting::NumberLiteral,
|
||||
state.clone(),
|
||||
))
|
||||
};
|
||||
|
||||
|
@ -126,21 +129,23 @@ where
|
|||
// we'll succeed with an appropriate Expr which records that.
|
||||
match typ {
|
||||
Num => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
// SAFETY: it's safe to use from_utf8_unchecked here, because we've
|
||||
// already validated that this range contains only ASCII digits
|
||||
Expr::Num(unsafe { from_utf8_unchecked(&state.bytes[0..bytes_parsed]) }),
|
||||
state.advance_without_indenting(bytes_parsed)?,
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
Float => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
// SAFETY: it's safe to use from_utf8_unchecked here, because we've
|
||||
// already validated that this range contains only ASCII digits
|
||||
Expr::Float(unsafe { from_utf8_unchecked(&state.bytes[0..bytes_parsed]) }),
|
||||
state.advance_without_indenting(bytes_parsed)?,
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
// For these we trim off the 0x/0o/0b part
|
||||
Hex => from_base(Base::Hex, first_ch, bytes_parsed, state),
|
||||
Octal => from_base(Base::Octal, first_ch, bytes_parsed, state),
|
||||
Binary => from_base(Base::Binary, first_ch, bytes_parsed, state),
|
||||
Hex => from_base(Base::Hex, first_ch, bytes_parsed, arena, state),
|
||||
Octal => from_base(Base::Octal, first_ch, bytes_parsed, arena, state),
|
||||
Binary => from_base(Base::Binary, first_ch, bytes_parsed, arena, state),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,12 +158,13 @@ enum LiteralType {
|
|||
Binary,
|
||||
}
|
||||
|
||||
fn from_base(
|
||||
fn from_base<'a>(
|
||||
base: Base,
|
||||
first_ch: char,
|
||||
bytes_parsed: usize,
|
||||
state: State<'_>,
|
||||
) -> ParseResult<'_, Expr<'_>> {
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>> {
|
||||
let is_negative = first_ch == '-';
|
||||
let bytes = if is_negative {
|
||||
&state.bytes[3..bytes_parsed]
|
||||
|
@ -168,13 +174,14 @@ fn from_base(
|
|||
|
||||
match parse_utf8(bytes) {
|
||||
Ok(string) => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
Expr::NonBase10Int {
|
||||
is_negative,
|
||||
string,
|
||||
base,
|
||||
},
|
||||
state.advance_without_indenting(bytes_parsed)?,
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
Err(reason) => state.fail(reason),
|
||||
Err(reason) => state.fail(arena, Progress::from_consumed(bytes_parsed), reason),
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,8 @@
|
|||
use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment};
|
||||
use crate::expr;
|
||||
use crate::parser::Progress::*;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Fail,
|
||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Bag,
|
||||
FailReason, ParseResult, Parser, State,
|
||||
};
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -17,16 +18,16 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
match bytes.next() {
|
||||
Some(&byte) => {
|
||||
if byte != b'"' {
|
||||
return Err(unexpected(0, state, Attempting::StrLiteral));
|
||||
return Err(unexpected(arena, 0, Attempting::StrLiteral, state));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(unexpected_eof(0, Attempting::StrLiteral, state));
|
||||
return Err(unexpected_eof(arena, state, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Advance past the opening quotation mark.
|
||||
state = state.advance_without_indenting(1)?;
|
||||
state = state.advance_without_indenting(arena, 1)?;
|
||||
|
||||
// At the parsing stage we keep the entire raw string, because the formatter
|
||||
// needs the raw string. (For example, so it can "remember" whether you
|
||||
|
@ -43,7 +44,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
segments.push(StrSegment::EscapedChar($ch));
|
||||
|
||||
// Advance past the segment we just added
|
||||
state = state.advance_without_indenting(segment_parsed_bytes)?;
|
||||
state = state.advance_without_indenting(arena, segment_parsed_bytes)?;
|
||||
|
||||
// Reset the segment
|
||||
segment_parsed_bytes = 0;
|
||||
|
@ -62,12 +63,12 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
|
||||
match parse_utf8(string_bytes) {
|
||||
Ok(string) => {
|
||||
state = state.advance_without_indenting(string.len())?;
|
||||
state = state.advance_without_indenting(arena, string.len())?;
|
||||
|
||||
segments.push($transform(string));
|
||||
}
|
||||
Err(reason) => {
|
||||
return state.fail(reason);
|
||||
return state.fail(arena, MadeProgress, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +102,11 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
}
|
||||
_ => {
|
||||
// Advance 1 for the close quote
|
||||
return Ok((PlainLine(""), state.advance_without_indenting(1)?));
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
PlainLine(""),
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -123,7 +128,11 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
};
|
||||
|
||||
// Advance the state 1 to account for the closing `"`
|
||||
return Ok((expr, state.advance_without_indenting(1)?));
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
expr,
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
));
|
||||
};
|
||||
}
|
||||
b'\n' => {
|
||||
|
@ -133,9 +142,10 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
// it should make it easiest to debug; the file will be a giant
|
||||
// error starting from where the open quote appeared.
|
||||
return Err(unexpected(
|
||||
arena,
|
||||
state.bytes.len() - 1,
|
||||
state,
|
||||
Attempting::StrLiteral,
|
||||
state,
|
||||
));
|
||||
}
|
||||
b'\\' => {
|
||||
|
@ -153,7 +163,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
match bytes.next() {
|
||||
Some(b'(') => {
|
||||
// Advance past the `\(` before using the expr parser
|
||||
state = state.advance_without_indenting(2)?;
|
||||
state = state.advance_without_indenting(arena, 2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
|
||||
|
@ -161,7 +171,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
// Parse an arbitrary expression, then give a
|
||||
// canonicalization error if that expression variant
|
||||
// is not allowed inside a string interpolation.
|
||||
let (loc_expr, new_state) =
|
||||
let (_progress, loc_expr, new_state) =
|
||||
skip_second!(loc(allocated(expr::expr(0))), ascii_char(b')'))
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
@ -178,14 +188,14 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
}
|
||||
Some(b'u') => {
|
||||
// Advance past the `\u` before using the expr parser
|
||||
state = state.advance_without_indenting(2)?;
|
||||
state = state.advance_without_indenting(arena, 2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
|
||||
// Parse the hex digits, surrounded by parens, then
|
||||
// give a canonicalization error if the digits form
|
||||
// an invalid unicode code point.
|
||||
let (loc_digits, new_state) = between!(
|
||||
let (_progress, loc_digits, new_state) = between!(
|
||||
ascii_char(b'('),
|
||||
loc(ascii_hex_digits()),
|
||||
ascii_char(b')')
|
||||
|
@ -223,9 +233,10 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
// by either an open paren or else one of the
|
||||
// escapable characters (\n, \t, \", \\, etc)
|
||||
return Err(unexpected(
|
||||
arena,
|
||||
state.bytes.len() - 1,
|
||||
state,
|
||||
Attempting::StrLiteral,
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -237,11 +248,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
|||
}
|
||||
|
||||
// We ran out of characters before finding a closed quote
|
||||
Err(unexpected_eof(
|
||||
state.bytes.len(),
|
||||
Attempting::StrLiteral,
|
||||
state.clone(),
|
||||
))
|
||||
Err(unexpected_eof(arena, state.clone(), state.bytes.len()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,17 +290,19 @@ where
|
|||
|
||||
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(format!(
|
||||
MadeProgress,
|
||||
Bag::from_state(
|
||||
arena,
|
||||
&state,
|
||||
FailReason::NotYetImplemented(format!(
|
||||
"TODO parse this line in a block string: {:?}",
|
||||
line
|
||||
)),
|
||||
},
|
||||
),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Err(reason) => state.fail(reason),
|
||||
Err(reason) => state.fail(arena, MadeProgress, reason),
|
||||
};
|
||||
}
|
||||
quotes_seen += 1;
|
||||
|
@ -310,7 +319,7 @@ where
|
|||
line_start = parsed_chars;
|
||||
}
|
||||
Err(reason) => {
|
||||
return state.fail(reason);
|
||||
return state.fail(arena, MadeProgress, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,10 +332,5 @@ where
|
|||
}
|
||||
|
||||
// We ran out of characters before finding 3 closing quotes
|
||||
Err(unexpected_eof(
|
||||
parsed_chars,
|
||||
// TODO custom BlockStrLiteral?
|
||||
Attempting::StrLiteral,
|
||||
state,
|
||||
))
|
||||
Err(unexpected_eof(arena, state, parsed_chars))
|
||||
}
|
||||
|
|
|
@ -2,44 +2,47 @@ use crate::ast::{self, Attempting};
|
|||
use crate::blankspace::space0_before;
|
||||
use crate::expr::expr;
|
||||
use crate::module::{header, module_defs};
|
||||
use crate::parser::{loc, Fail, Parser, State};
|
||||
use crate::parser::{loc, Bag, Parser, State};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = header().parse(arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_defs_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = module_defs().parse(arena, state);
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ use crate::expr::{global_tag, private_tag};
|
|||
use crate::ident::join_module_parts;
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either, Fail,
|
||||
FailReason, ParseResult, Parser, State,
|
||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Bag, Either,
|
||||
FailReason, ParseResult, Parser,
|
||||
Progress::{self, *},
|
||||
State,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -30,7 +32,8 @@ macro_rules! tag_union {
|
|||
),
|
||||
optional(
|
||||
// This could be an open tag union, e.g. `[ Foo, Bar ]a`
|
||||
move |arena, state| allocated(term($min_indent)).parse(arena, state)
|
||||
move |arena: &'a Bump, state: State<'a>| allocated(term($min_indent))
|
||||
.parse(arena, state)
|
||||
)
|
||||
),
|
||||
|((tags, final_comments), ext): (
|
||||
|
@ -57,16 +60,19 @@ pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>>
|
|||
loc!(applied_type(min_indent)),
|
||||
loc!(parse_type_variable)
|
||||
),
|
||||
optional(
|
||||
// Inline type annotation, e.g. [ Nil, Cons a (List a) ] as List a
|
||||
and!(
|
||||
space1(min_indent),
|
||||
skip_first!(
|
||||
ascii_string(keyword::AS),
|
||||
space1_before(term(min_indent), min_indent)
|
||||
)
|
||||
|a, s| {
|
||||
optional(
|
||||
// Inline type annotation, e.g. [ Nil, Cons a (List a) ] as List a
|
||||
and!(
|
||||
space1(min_indent),
|
||||
skip_first!(
|
||||
crate::parser::keyword(keyword::AS, min_indent),
|
||||
space1_before(term(min_indent), min_indent)
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
.parse(a, s)
|
||||
}
|
||||
),
|
||||
|arena: &'a Bump,
|
||||
(loc_ann, opt_as): (
|
||||
|
@ -95,21 +101,32 @@ fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
skip_first!(
|
||||
// Once we hit an "as", stop parsing args
|
||||
not(ascii_string(keyword::AS)),
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_parenthetical_type(min_indent),
|
||||
loc!(record_type(min_indent)),
|
||||
loc!(tag_union!(min_indent)),
|
||||
loc!(parse_concrete_type),
|
||||
loc!(parse_type_variable)
|
||||
// and roll back parsing of preceding spaces
|
||||
not(and!(
|
||||
space1(min_indent),
|
||||
crate::parser::keyword(keyword::AS, min_indent)
|
||||
)),
|
||||
space1_before(
|
||||
one_of!(
|
||||
loc_wildcard(),
|
||||
loc_parenthetical_type(min_indent),
|
||||
loc!(record_type(min_indent)),
|
||||
loc!(tag_union!(min_indent)),
|
||||
loc!(parse_concrete_type),
|
||||
loc!(parse_type_variable)
|
||||
),
|
||||
min_indent
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_applied_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>> {
|
||||
zero_or_more!(loc_applied_arg(min_indent))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
between!(
|
||||
|
@ -130,10 +147,7 @@ fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>> {
|
|||
either!(loc!(private_tag()), loc!(global_tag())),
|
||||
// Optionally parse space-separated arguments for the constructor,
|
||||
// e.g. `ok err` in `Result ok err`
|
||||
zero_or_more!(space1_before(
|
||||
move |arena, state| loc_applied_arg(min_indent).parse(arena, state),
|
||||
min_indent,
|
||||
))
|
||||
loc_applied_args(min_indent)
|
||||
),
|
||||
|(either_name, args): (
|
||||
Either<Located<&'a str>, Located<&'a str>>,
|
||||
|
@ -185,10 +199,7 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
|||
parse_concrete_type,
|
||||
// Optionally parse space-separated arguments for the constructor,
|
||||
// e.g. `Str Float` in `Map Str Float`
|
||||
zero_or_more!(space1_before(
|
||||
move |arena, state| loc_applied_arg(min_indent).parse(arena, state),
|
||||
min_indent,
|
||||
))
|
||||
loc_applied_args(min_indent)
|
||||
),
|
||||
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Located<TypeAnnotation<'a>>>)| {
|
||||
match &ctor {
|
||||
|
@ -210,8 +221,8 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
|||
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
use crate::blankspace::space0;
|
||||
move |arena, state: State<'a>| {
|
||||
let (first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||
let (rest, state) = zero_or_more!(skip_first!(
|
||||
let (p1, first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||
let (p2, rest, state) = zero_or_more!(skip_first!(
|
||||
ascii_char(b','),
|
||||
space0_around(term(min_indent), min_indent)
|
||||
))
|
||||
|
@ -219,11 +230,11 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
|
||||
// TODO this space0 is dropped, so newlines just before the function arrow when there
|
||||
// is only one argument are not seen by the formatter. Can we do better?
|
||||
let (is_function, state) =
|
||||
let (p3, is_function, state) =
|
||||
optional(skip_first!(space0(min_indent), ascii_string("->"))).parse(arena, state)?;
|
||||
|
||||
if is_function.is_some() {
|
||||
let (return_type, state) =
|
||||
let (p4, return_type, state) =
|
||||
space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||
|
||||
// prepare arguments
|
||||
|
@ -236,18 +247,21 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
region: return_type.region,
|
||||
value: TypeAnnotation::Function(output, arena.alloc(return_type)),
|
||||
};
|
||||
Ok((result, state))
|
||||
let progress = p1.or(p2).or(p3).or(p4);
|
||||
Ok((progress, result, state))
|
||||
} else {
|
||||
let progress = p1.or(p2).or(p3);
|
||||
// if there is no function arrow, there cannot be more than 1 "argument"
|
||||
if rest.is_empty() {
|
||||
Ok((first, state))
|
||||
Ok((progress, first, state))
|
||||
} else {
|
||||
// e.g. `Int,Int` without an arrow and return type
|
||||
let msg =
|
||||
"TODO: Decide the correct error to return for 'Invalid function signature'"
|
||||
.to_string();
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented("TODO: Decide the correct error to return for 'Invalid function signature'".to_string()),
|
||||
},
|
||||
progress,
|
||||
Bag::from_state(arena, &state, FailReason::NotYetImplemented(msg)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
@ -278,18 +292,20 @@ fn parse_concrete_type<'a>(
|
|||
let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.)
|
||||
let mut parts: Vec<&'a str> = Vec::new_in(arena);
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
||||
// Qualified types must start with a capitalized letter.
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((first_letter, bytes_parsed)) => {
|
||||
if first_letter.is_alphabetic() && first_letter.is_uppercase() {
|
||||
part_buf.push(first_letter);
|
||||
} else {
|
||||
return Err(unexpected(0, state, Attempting::ConcreteType));
|
||||
return Err(unexpected(arena, 0, Attempting::ConcreteType, state));
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, NoProgress, reason),
|
||||
}
|
||||
|
||||
let mut next_char = None;
|
||||
|
@ -333,9 +349,13 @@ fn parse_concrete_type<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,7 +373,7 @@ fn parse_concrete_type<'a>(
|
|||
// We had neither capitalized nor noncapitalized parts,
|
||||
// yet we made it this far. The only explanation is that this was
|
||||
// a stray '.' drifting through the cosmos.
|
||||
return Err(unexpected(1, state, Attempting::Identifier));
|
||||
return Err(unexpected(arena, 1, Attempting::Identifier, state));
|
||||
}
|
||||
|
||||
let answer = TypeAnnotation::Apply(
|
||||
|
@ -362,7 +382,8 @@ fn parse_concrete_type<'a>(
|
|||
&[],
|
||||
);
|
||||
|
||||
Ok((answer, state))
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
Ok((progress, answer, state))
|
||||
}
|
||||
|
||||
fn parse_type_variable<'a>(
|
||||
|
@ -371,18 +392,23 @@ fn parse_type_variable<'a>(
|
|||
) -> ParseResult<'a, TypeAnnotation<'a>> {
|
||||
let mut buf = String::new_in(arena);
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((first_letter, bytes_parsed)) => {
|
||||
// Type variables must start with a lowercase letter.
|
||||
if first_letter.is_alphabetic() && first_letter.is_lowercase() {
|
||||
buf.push(first_letter);
|
||||
} else {
|
||||
return Err(unexpected(0, state, Attempting::TypeVariable));
|
||||
return Err(unexpected(arena, 0, Attempting::TypeVariable, state));
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
}
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
|
@ -399,15 +425,19 @@ fn parse_type_variable<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, reason);
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
}
|
||||
}
|
||||
|
||||
let answer = TypeAnnotation::BoundVariable(buf.into_bump_str());
|
||||
|
||||
Ok((answer, state))
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
Ok((progress, answer, state))
|
||||
}
|
||||
|
||||
fn malformed<'a>(
|
||||
|
@ -416,6 +446,8 @@ fn malformed<'a>(
|
|||
mut state: State<'a>,
|
||||
parts: Vec<&'a str>,
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>> {
|
||||
// assumption: progress was made to conclude that the annotation is malformed
|
||||
|
||||
// Reconstruct the original string that we've been parsing.
|
||||
let mut full_string = String::new_in(arena);
|
||||
|
||||
|
@ -437,13 +469,14 @@ fn malformed<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => return state.fail(reason),
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
TypeAnnotation::Malformed(full_string.into_bump_str()),
|
||||
state,
|
||||
))
|
||||
|
|
|
@ -31,7 +31,7 @@ mod test_parse {
|
|||
PackageName, PackageOrPath, PlatformHeader, To,
|
||||
};
|
||||
use roc_parse::module::{app_header, interface_header, module_defs, platform_header};
|
||||
use roc_parse::parser::{Fail, FailReason, Parser, State};
|
||||
use roc_parse::parser::{FailReason, Parser, State};
|
||||
use roc_parse::test_helpers::parse_expr_with;
|
||||
use roc_region::all::{Located, Region};
|
||||
use std::{f64, i64};
|
||||
|
@ -43,12 +43,12 @@ mod test_parse {
|
|||
assert_eq!(Ok(expected_expr), actual);
|
||||
}
|
||||
|
||||
fn assert_parsing_fails<'a>(input: &'a str, reason: FailReason, attempting: Attempting) {
|
||||
fn assert_parsing_fails<'a>(input: &'a str, _reason: FailReason, _attempting: Attempting) {
|
||||
let arena = Bump::new();
|
||||
let actual = parse_expr_with(&arena, input);
|
||||
let expected_fail = Fail { reason, attempting };
|
||||
// let expected_fail = Fail { reason, attempting };
|
||||
|
||||
assert_eq!(Err(expected_fail), actual);
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
|
||||
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
|
||||
|
@ -2410,8 +2410,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2448,8 +2451,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2499,9 +2505,13 @@ mod test_parse {
|
|||
provides [ quicksort ] to base
|
||||
"#
|
||||
);
|
||||
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2544,8 +2554,11 @@ mod test_parse {
|
|||
|
||||
let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects fx.Blah {}";
|
||||
let actual = platform_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2612,8 +2625,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = platform_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2641,8 +2657,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = interface_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2670,8 +2689,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = interface_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2697,8 +2719,11 @@ mod test_parse {
|
|||
"#
|
||||
);
|
||||
let actual = module_defs()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
// It should occur twice in the debug output - once for the pattern,
|
||||
// and then again for the lookup.
|
||||
|
@ -2745,6 +2770,7 @@ mod test_parse {
|
|||
Located::new(2, 2, 0, 10, def2),
|
||||
Located::new(3, 3, 0, 13, def3),
|
||||
];
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
foo = 1
|
||||
|
@ -2753,13 +2779,97 @@ mod test_parse {
|
|||
baz = "stuff"
|
||||
"#
|
||||
);
|
||||
|
||||
let actual = module_defs()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_def_newline() {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
main =
|
||||
i = 64
|
||||
|
||||
i
|
||||
"#
|
||||
);
|
||||
|
||||
let actual = module_defs()
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert!(actual.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_def_annotation() {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
main =
|
||||
wrappedNotEq : a, a -> Bool
|
||||
wrappedNotEq = \num1, num2 ->
|
||||
num1 != num2
|
||||
|
||||
wrappedNotEq 2 3
|
||||
"#
|
||||
);
|
||||
|
||||
let actual = module_defs()
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
assert!(actual.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outdenting_newline_after_else() {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
// highlights a problem with the else branch demanding a newline after its expression
|
||||
let src = indoc!(
|
||||
r#"
|
||||
main =
|
||||
v = \y -> if x then y else z
|
||||
|
||||
1
|
||||
"#
|
||||
);
|
||||
|
||||
let actual = module_defs()
|
||||
.parse(
|
||||
&arena,
|
||||
State::new_in(&arena, src.as_bytes(), Attempting::Module),
|
||||
)
|
||||
.map(|tuple| tuple.1);
|
||||
|
||||
dbg!(&actual);
|
||||
|
||||
assert!(actual.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newline_after_equals() {
|
||||
// Regression test for https://github.com/rtfeldman/roc/issues/51
|
||||
|
|
|
@ -12,7 +12,6 @@ roc_module = { path = "../module" }
|
|||
roc_parse = { path = "../parse" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
|
|
|
@ -502,7 +502,7 @@ fn pretty_runtime_error<'b>(
|
|||
]),
|
||||
alloc.region(region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Roc uses signed 64-bit floating points, allowing values between"),
|
||||
alloc.reflow("Roc uses signed 64-bit floating points, allowing values between "),
|
||||
alloc.text(format!("{:e}", f64::MIN)),
|
||||
alloc.reflow(" and "),
|
||||
alloc.text(format!("{:e}", f64::MAX)),
|
||||
|
|
|
@ -1,18 +1,69 @@
|
|||
use roc_parse::parser::{Fail, FailReason};
|
||||
use roc_parse::parser::{ContextItem, FailReason, ParseProblem};
|
||||
use roc_region::all::Region;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::report::{Report, RocDocAllocator};
|
||||
use crate::report::{Report, RocDocAllocator, RocDocBuilder};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
fn context<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
context_stack: &[ContextItem],
|
||||
default: &'a str,
|
||||
) -> RocDocBuilder<'a> {
|
||||
match context_stack.last() {
|
||||
Some(context_item) => {
|
||||
// assign string to `Attempting`
|
||||
use roc_parse::ast::Attempting::*;
|
||||
match context_item.context {
|
||||
Def => alloc.text("while parsing a definition"),
|
||||
_ => {
|
||||
// use the default
|
||||
alloc.text(default)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// use the default
|
||||
alloc.text(default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_problem<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
filename: PathBuf,
|
||||
problem: Fail,
|
||||
starting_line: u32,
|
||||
parse_problem: ParseProblem,
|
||||
) -> Report<'b> {
|
||||
use FailReason::*;
|
||||
let line = starting_line + parse_problem.line;
|
||||
let region = Region {
|
||||
start_line: line,
|
||||
end_line: line,
|
||||
start_col: parse_problem.column,
|
||||
end_col: parse_problem.column + 1,
|
||||
};
|
||||
|
||||
match problem.reason {
|
||||
ArgumentsBeforeEquals(region) => {
|
||||
let report = |doc| Report {
|
||||
filename: filename.clone(),
|
||||
doc,
|
||||
title: "PARSE PROBLEM".to_string(),
|
||||
};
|
||||
|
||||
use FailReason::*;
|
||||
match parse_problem.problem {
|
||||
FailReason::ConditionFailed => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow("A condition failed:"),
|
||||
alloc.region(region),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "PARSE PROBLEM".to_string(),
|
||||
}
|
||||
}
|
||||
FailReason::ArgumentsBeforeEquals(region) => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow("Unexpected tokens in front of the `=` symbol:"),
|
||||
alloc.region(region),
|
||||
|
@ -24,19 +75,22 @@ pub fn parse_problem<'b>(
|
|||
title: "PARSE PROBLEM".to_string(),
|
||||
}
|
||||
}
|
||||
other => {
|
||||
//
|
||||
// Unexpected(char, Region),
|
||||
// OutdentedTooFar,
|
||||
// ConditionFailed,
|
||||
// LineTooLong(u32 /* which line was too long */),
|
||||
// TooManyLines,
|
||||
// Eof(Region),
|
||||
// InvalidPattern,
|
||||
// ReservedKeyword(Region),
|
||||
// ArgumentsBeforeEquals,
|
||||
//}
|
||||
todo!("unhandled parse error: {:?}", other)
|
||||
Unexpected(mut region) => {
|
||||
if region.start_col == region.end_col {
|
||||
region.end_col += 1;
|
||||
}
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Unexpected token "),
|
||||
context(alloc, &parse_problem.context_stack, "here"),
|
||||
alloc.text(":"),
|
||||
]),
|
||||
alloc.region(region),
|
||||
]);
|
||||
|
||||
report(doc)
|
||||
}
|
||||
_ => todo!("unhandled parse error: {:?}", parse_problem.problem),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -526,6 +526,7 @@ impl<'a> RocDocAllocator<'a> {
|
|||
if error_highlight_line {
|
||||
let highlight_text =
|
||||
ERROR_UNDERLINE.repeat((sub_region.end_col - sub_region.start_col) as usize);
|
||||
|
||||
let highlight_line = self
|
||||
.line()
|
||||
// Omit the gutter bar when we know there are no further
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_constrain::module::{constrain_imported_values, Import};
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::Located;
|
||||
use roc_solve::solve;
|
||||
|
@ -85,24 +85,8 @@ where
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn can_expr(expr_str: &str) -> Result<CanExprOut, ParseErrOut> {
|
||||
can_expr_with(&Bump::new(), test_home(), expr_str)
|
||||
pub fn can_expr<'a>(arena: &'a Bump, expr_str: &'a str) -> Result<CanExprOut, ParseErrOut<'a>> {
|
||||
can_expr_with(arena, test_home(), expr_str)
|
||||
}
|
||||
|
||||
pub struct CanExprOut {
|
||||
|
@ -116,19 +100,38 @@ pub struct CanExprOut {
|
|||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseErrOut {
|
||||
pub fail: Fail,
|
||||
pub struct ParseErrOut<'a> {
|
||||
pub fail: Bag<'a>,
|
||||
pub home: ModuleId,
|
||||
pub interns: Interns,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn can_expr_with(
|
||||
arena: &Bump,
|
||||
pub fn can_expr_with<'a>(
|
||||
arena: &'a Bump,
|
||||
home: ModuleId,
|
||||
expr_str: &str,
|
||||
) -> Result<CanExprOut, ParseErrOut> {
|
||||
expr_str: &'a str,
|
||||
) -> Result<CanExprOut, ParseErrOut<'a>> {
|
||||
let loc_expr = match parse_loc_with(&arena, expr_str) {
|
||||
Ok(e) => e,
|
||||
Err(fail) => {
|
||||
|
|
|
@ -41,8 +41,9 @@ mod test_reporting {
|
|||
}
|
||||
}
|
||||
|
||||
fn infer_expr_help(
|
||||
expr_src: &str,
|
||||
fn infer_expr_help<'a>(
|
||||
arena: &'a Bump,
|
||||
expr_src: &'a str,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<solve::TypeError>,
|
||||
|
@ -51,7 +52,7 @@ mod test_reporting {
|
|||
ModuleId,
|
||||
Interns,
|
||||
),
|
||||
ParseErrOut,
|
||||
ParseErrOut<'a>,
|
||||
> {
|
||||
let CanExprOut {
|
||||
loc_expr,
|
||||
|
@ -63,7 +64,7 @@ mod test_reporting {
|
|||
mut interns,
|
||||
problems: can_problems,
|
||||
..
|
||||
} = can_expr(expr_src)?;
|
||||
} = can_expr(arena, expr_src)?;
|
||||
let mut subs = Subs::new(var_store.into());
|
||||
|
||||
for (var, name) in output.introduced_variables.name_by_var {
|
||||
|
@ -108,7 +109,7 @@ mod test_reporting {
|
|||
Ok((unify_problems, can_problems, mono_problems, home, interns))
|
||||
}
|
||||
|
||||
fn list_reports<F>(src: &str, buf: &mut String, callback: F)
|
||||
fn list_reports<F>(arena: &Bump, src: &str, buf: &mut String, callback: F)
|
||||
where
|
||||
F: FnOnce(RocDocBuilder<'_>, &mut String),
|
||||
{
|
||||
|
@ -118,7 +119,7 @@ mod test_reporting {
|
|||
|
||||
let filename = filename_from_string(r"\code\proj\Main.roc");
|
||||
|
||||
match infer_expr_help(src) {
|
||||
match infer_expr_help(arena, src) {
|
||||
Err(parse_err) => {
|
||||
let ParseErrOut {
|
||||
fail,
|
||||
|
@ -128,7 +129,8 @@ mod test_reporting {
|
|||
|
||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||
|
||||
let doc = parse_problem(&alloc, filename, fail);
|
||||
let problem = fail.into_parse_problem(filename.clone(), src.as_bytes());
|
||||
let doc = parse_problem(&alloc, filename, 0, problem);
|
||||
|
||||
callback(doc.pretty(&alloc).append(alloc.line()), buf)
|
||||
}
|
||||
|
@ -169,6 +171,7 @@ mod test_reporting {
|
|||
|
||||
fn report_problem_as(src: &str, expected_rendering: &str) {
|
||||
let mut buf: String = String::new();
|
||||
let arena = Bump::new();
|
||||
|
||||
let callback = |doc: RocDocBuilder<'_>, buf: &mut String| {
|
||||
doc.1
|
||||
|
@ -176,13 +179,23 @@ mod test_reporting {
|
|||
.expect("list_reports")
|
||||
};
|
||||
|
||||
list_reports(src, &mut buf, callback);
|
||||
list_reports(&arena, src, &mut buf, callback);
|
||||
|
||||
// convenient to copy-paste the generated message
|
||||
if true {
|
||||
if buf != expected_rendering {
|
||||
for line in buf.split("\n") {
|
||||
println!(" {}", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(buf, expected_rendering);
|
||||
}
|
||||
|
||||
fn color_report_problem_as(src: &str, expected_rendering: &str) {
|
||||
let mut buf: String = String::new();
|
||||
let arena = Bump::new();
|
||||
|
||||
let callback = |doc: RocDocBuilder<'_>, buf: &mut String| {
|
||||
doc.1
|
||||
|
@ -196,7 +209,7 @@ mod test_reporting {
|
|||
.expect("list_reports")
|
||||
};
|
||||
|
||||
list_reports(src, &mut buf, callback);
|
||||
list_reports(&arena, src, &mut buf, callback);
|
||||
|
||||
let readable = human_readable(&buf);
|
||||
|
||||
|
@ -572,8 +585,9 @@ mod test_reporting {
|
|||
"#
|
||||
);
|
||||
|
||||
let arena = Bump::new();
|
||||
let (_type_problems, _can_problems, _mono_problems, home, interns) =
|
||||
infer_expr_help(src).expect("parse error");
|
||||
infer_expr_help(&arena, src).expect("parse error");
|
||||
|
||||
let mut buf = String::new();
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
|
@ -602,8 +616,9 @@ mod test_reporting {
|
|||
"#
|
||||
);
|
||||
|
||||
let arena = Bump::new();
|
||||
let (_type_problems, _can_problems, _mono_problems, home, mut interns) =
|
||||
infer_expr_help(src).expect("parse error");
|
||||
infer_expr_help(&arena, src).expect("parse error");
|
||||
|
||||
let mut buf = String::new();
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
|
@ -3304,16 +3319,15 @@ mod test_reporting {
|
|||
|
||||
#[test]
|
||||
fn float_out_of_range() {
|
||||
// have to deal with some whitespace issues because of the format! macro
|
||||
report_problem_as(
|
||||
&format!(
|
||||
indoc!(
|
||||
r#"
|
||||
overflow = 1{:e}
|
||||
underflow = -1{:e}
|
||||
overflow = 11.7976931348623157e308
|
||||
underflow = -11.7976931348623157e308
|
||||
|
||||
overflow + underflow
|
||||
"#,
|
||||
f64::MAX,
|
||||
f64::MAX,
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -3321,11 +3335,11 @@ mod test_reporting {
|
|||
|
||||
This float literal is too big:
|
||||
|
||||
2│ overflow = 11.7976931348623157e308
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
1│ overflow = 11.7976931348623157e308
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit floating points, allowing values
|
||||
between-1.7976931348623157e308 and 1.7976931348623157e308
|
||||
Roc uses signed 64-bit floating points, allowing values between
|
||||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
|
||||
|
@ -3333,11 +3347,11 @@ mod test_reporting {
|
|||
|
||||
This float literal is too small:
|
||||
|
||||
3│ underflow = -11.7976931348623157e308
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
2│ underflow = -11.7976931348623157e308
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit floating points, allowing values
|
||||
between-1.7976931348623157e308 and 1.7976931348623157e308
|
||||
Roc uses signed 64-bit floating points, allowing values between
|
||||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
"#
|
||||
|
@ -4011,4 +4025,83 @@ mod test_reporting {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_annotation_dubble_colon() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f :: I64
|
||||
f = 42
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── PARSE PROBLEM ───────────────────────────────────────────────────────────────
|
||||
|
||||
Unexpected token while parsing a definition:
|
||||
|
||||
1│ f :: I64
|
||||
^
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_equals_in_def() {
|
||||
// NOTE: VERY BAD ERROR MESSAGE
|
||||
//
|
||||
// looks like `x y` are considered argument to the add, even though they are
|
||||
// on a lower indentation level
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 3
|
||||
y =
|
||||
x == 5
|
||||
Num.add 1 2
|
||||
|
||||
x y
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── TOO MANY ARGS ───────────────────────────────────────────────────────────────
|
||||
|
||||
The `add` function expects 2 arguments, but it got 4 instead:
|
||||
|
||||
4│ Num.add 1 2
|
||||
^^^^^^^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_operator() {
|
||||
// NOTE: VERY BAD ERROR MESSAGE
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
main =
|
||||
5 ** 3
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── PARSE PROBLEM ───────────────────────────────────────────────────────────────
|
||||
|
||||
Unexpected token here:
|
||||
|
||||
2│ 5 ** 3
|
||||
^
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::ident::Ident;
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_solve::solve;
|
||||
|
@ -87,19 +87,22 @@ where
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -854,7 +854,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\f -> (\a, b -> f b a),
|
||||
\f -> (\a, b -> f b a)
|
||||
"#
|
||||
),
|
||||
"(a, b -> c) -> (b, a -> c)",
|
||||
|
|
|
@ -15,7 +15,7 @@ use roc_module::ident::Ident;
|
|||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_solve::solve;
|
||||
|
@ -87,19 +87,22 @@ where
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map(|(_, loc_expr, _)| loc_expr)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_parse::ast::StrLiteral;
|
|||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::expr::expr;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_parse::parser::{loc, Bag, Parser, State};
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
|
@ -233,15 +233,15 @@ pub fn str_to_expr2<'a>(
|
|||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
region: Region,
|
||||
) -> Result<(Expr2, self::Output), Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
) -> Result<(Expr2, self::Output), Bag<'a>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(expr(0)), 0);
|
||||
let parse_res = parser.parse(&arena, state);
|
||||
|
||||
parse_res
|
||||
.map(|(loc_expr, _)| arena.alloc(loc_expr.value))
|
||||
.map(|(_, loc_expr, _)| arena.alloc(loc_expr.value))
|
||||
.map(|loc_expr_val_ref| to_expr2(env, scope, loc_expr_val_ref, region))
|
||||
.map_err(|(fail, _)| fail)
|
||||
.map_err(|(_, fail, _)| fail)
|
||||
}
|
||||
|
||||
pub fn to_expr2<'a>(
|
||||
|
|
|
@ -19,15 +19,15 @@ pub struct File<'a> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadError {
|
||||
pub enum ReadError<'a> {
|
||||
Read(std::io::Error),
|
||||
ParseDefs(parser::Fail),
|
||||
ParseHeader(parser::Fail),
|
||||
ParseDefs(parser::Bag<'a>),
|
||||
ParseHeader(parser::Bag<'a>),
|
||||
DoesntHaveRocExtension,
|
||||
}
|
||||
|
||||
impl<'a> File<'a> {
|
||||
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError> {
|
||||
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError<'a>> {
|
||||
if path.extension() != Some(OsStr::new("roc")) {
|
||||
return Err(ReadError::DoesntHaveRocExtension);
|
||||
}
|
||||
|
@ -36,23 +36,23 @@ impl<'a> File<'a> {
|
|||
|
||||
let allocation = arena.alloc(bytes);
|
||||
|
||||
let module_parse_state = parser::State::new(allocation, Attempting::Module);
|
||||
let module_parse_state = parser::State::new_in(arena, allocation, Attempting::Module);
|
||||
let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state);
|
||||
|
||||
match parsed_module {
|
||||
Ok((module, state)) => {
|
||||
Ok((_, module, state)) => {
|
||||
let parsed_defs = module_defs().parse(&arena, state);
|
||||
|
||||
match parsed_defs {
|
||||
Ok((defs, _)) => Ok(File {
|
||||
Ok((_, defs, _)) => Ok(File {
|
||||
path,
|
||||
module_header: module,
|
||||
content: defs,
|
||||
}),
|
||||
Err((error, _)) => Err(ReadError::ParseDefs(error)),
|
||||
Err((_, error, _)) => Err(ReadError::ParseDefs(error)),
|
||||
}
|
||||
}
|
||||
Err((error, _)) => Err(ReadError::ParseHeader(error)),
|
||||
Err((_, error, _)) => Err(ReadError::ParseHeader(error)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue