mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Support multiline repl input
This commit is contained in:
parent
1bb99bc490
commit
526d7cb4ba
1 changed files with 110 additions and 46 deletions
156
cli/src/repl.rs
156
cli/src/repl.rs
|
@ -22,7 +22,7 @@ use roc_mono::expr::Procs;
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::Layout;
|
||||||
use roc_parse::ast::{self, Attempting};
|
use roc_parse::ast::{self, Attempting};
|
||||||
use roc_parse::blankspace::space0_before;
|
use roc_parse::blankspace::space0_before;
|
||||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
use roc_parse::parser::{loc, Fail, FailReason, Parser, State};
|
||||||
use roc_problem::can::Problem;
|
use roc_problem::can::Problem;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
|
@ -44,8 +44,15 @@ pub fn main() -> io::Result<()> {
|
||||||
|
|
||||||
// Loop
|
// Loop
|
||||||
|
|
||||||
|
let mut pending_src = String::new();
|
||||||
|
let mut prev_line_blank = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print!("\n\u{001b}[36m▶\u{001b}[0m ");
|
if pending_src.is_empty() {
|
||||||
|
print!("\n\u{001b}[36m▶\u{001b}[0m ");
|
||||||
|
} else {
|
||||||
|
print!("\u{001b}[36m…\u{001b}[0m ");
|
||||||
|
}
|
||||||
|
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
@ -62,17 +69,68 @@ pub fn main() -> io::Result<()> {
|
||||||
println!("Use :exit to exit.");
|
println!("Use :exit to exit.");
|
||||||
}
|
}
|
||||||
"" => {
|
"" => {
|
||||||
println!("\n{}", WELCOME_MESSAGE);
|
if pending_src.is_empty() {
|
||||||
|
println!("\n{}", WELCOME_MESSAGE);
|
||||||
|
} else if prev_line_blank {
|
||||||
|
// After two blank lines in a row, give up and try parsing it
|
||||||
|
// even though it's going to fail. This way you don't get stuck.
|
||||||
|
match print_output(pending_src.as_str()) {
|
||||||
|
Ok(output) => {
|
||||||
|
println!("{}", output);
|
||||||
|
}
|
||||||
|
Err(fail) => {
|
||||||
|
report_parse_error(fail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_src.clear();
|
||||||
|
} else {
|
||||||
|
pending_src.push('\n');
|
||||||
|
|
||||||
|
prev_line_blank = true;
|
||||||
|
continue; // Skip the part where we reset prev_line_blank to false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
":exit" => {
|
":exit" => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line => {
|
line => {
|
||||||
let (answer, answer_type) = gen(line, Triple::host(), OptLevel::Normal);
|
let result = if pending_src.is_empty() {
|
||||||
|
print_output(line)
|
||||||
|
} else {
|
||||||
|
pending_src.push('\n');
|
||||||
|
pending_src.push_str(line);
|
||||||
|
|
||||||
println!("\n{} \u{001b}[35m:\u{001b}[0m {}", answer, answer_type);
|
print_output(pending_src.as_str())
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(output) => {
|
||||||
|
println!("{}", output);
|
||||||
|
pending_src.clear();
|
||||||
|
}
|
||||||
|
Err(Fail {
|
||||||
|
reason: FailReason::Eof(_),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// If we hit an eof, and we're allowed to keep going,
|
||||||
|
// append the str to the src we're building up and continue.
|
||||||
|
// (We only need to append it here if it was empty before;
|
||||||
|
// otherwise, we already appended it before calling print_output.)
|
||||||
|
|
||||||
|
if pending_src.is_empty() {
|
||||||
|
pending_src.push_str(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(fail) => {
|
||||||
|
report_parse_error(fail);
|
||||||
|
pending_src.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_line_blank = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -81,11 +139,21 @@ pub fn main() -> io::Result<()> {
|
||||||
const WELCOME_MESSAGE: &str =
|
const WELCOME_MESSAGE: &str =
|
||||||
"Enter an expression, or :help for a list of commands, or :exit to exit.";
|
"Enter an expression, or :help for a list of commands, or :exit to exit.";
|
||||||
|
|
||||||
|
fn report_parse_error(fail: Fail) {
|
||||||
|
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_output(src: &str) -> Result<String, Fail> {
|
||||||
|
gen(src, Triple::host(), OptLevel::Normal).map(|(answer, answer_type)| {
|
||||||
|
format!("\n{} \u{001b}[35m:\u{001b}[0m {}", answer, answer_type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn repl_home() -> ModuleId {
|
pub fn repl_home() -> ModuleId {
|
||||||
ModuleIds::default().get_or_insert(&"REPL".into())
|
ModuleIds::default().get_or_insert(&"REPL".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen(src: &str, target: Triple, opt_level: OptLevel) -> (String, String) {
|
pub fn gen(src: &str, target: Triple, opt_level: OptLevel) -> Result<(String, String), Fail> {
|
||||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||||
|
|
||||||
// Look up the types and expressions of the `provided` values
|
// Look up the types and expressions of the `provided` values
|
||||||
|
@ -100,7 +168,7 @@ pub fn gen(src: &str, target: Triple, opt_level: OptLevel) -> (String, String) {
|
||||||
interns,
|
interns,
|
||||||
problems: can_problems,
|
problems: can_problems,
|
||||||
..
|
..
|
||||||
} = can_expr(src);
|
} = can_expr(src)?;
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut type_problems = Vec::new();
|
let mut type_problems = Vec::new();
|
||||||
let (content, mut subs) = infer_expr(subs, &mut type_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut type_problems, &constraint, var);
|
||||||
|
@ -271,7 +339,7 @@ pub fn gen(src: &str, target: Triple, opt_level: OptLevel) -> (String, String) {
|
||||||
.ok_or(format!("Unable to JIT compile `{}`", main_fn_name))
|
.ok_or(format!("Unable to JIT compile `{}`", main_fn_name))
|
||||||
.expect("errored");
|
.expect("errored");
|
||||||
|
|
||||||
(format!("{}", main.call()), expr_type_str)
|
Ok((format!("{}", main.call()), expr_type_str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,10 +360,6 @@ pub fn infer_expr(
|
||||||
(content, solved.into_inner())
|
(content, solved.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||||
let state = State::new(&input, Attempting::Module);
|
let state = State::new(&input, Attempting::Module);
|
||||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||||
|
@ -306,22 +370,25 @@ pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast
|
||||||
.map_err(|(fail, _)| fail)
|
.map_err(|(fail, _)| fail)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_expr(expr_str: &str) -> CanExprOut {
|
pub fn can_expr(expr_str: &str) -> Result<CanExprOut, Fail> {
|
||||||
can_expr_with(&Bump::new(), repl_home(), expr_str)
|
can_expr_with(&Bump::new(), repl_home(), expr_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uniq_expr(
|
pub fn uniq_expr(
|
||||||
expr_str: &str,
|
expr_str: &str,
|
||||||
) -> (
|
) -> Result<
|
||||||
Located<roc_can::expr::Expr>,
|
(
|
||||||
Output,
|
Located<roc_can::expr::Expr>,
|
||||||
Vec<Problem>,
|
Output,
|
||||||
Subs,
|
Vec<Problem>,
|
||||||
Variable,
|
Subs,
|
||||||
Constraint,
|
Variable,
|
||||||
ModuleId,
|
Constraint,
|
||||||
Interns,
|
ModuleId,
|
||||||
) {
|
Interns,
|
||||||
|
),
|
||||||
|
Fail,
|
||||||
|
> {
|
||||||
let declared_idents: &ImMap<Ident, (Symbol, Region)> = &ImMap::default();
|
let declared_idents: &ImMap<Ident, (Symbol, Region)> = &ImMap::default();
|
||||||
|
|
||||||
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
|
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
|
||||||
|
@ -331,16 +398,19 @@ pub fn uniq_expr_with(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
expr_str: &str,
|
expr_str: &str,
|
||||||
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
||||||
) -> (
|
) -> Result<
|
||||||
Located<roc_can::expr::Expr>,
|
(
|
||||||
Output,
|
Located<roc_can::expr::Expr>,
|
||||||
Vec<Problem>,
|
Output,
|
||||||
Subs,
|
Vec<Problem>,
|
||||||
Variable,
|
Subs,
|
||||||
Constraint,
|
Variable,
|
||||||
ModuleId,
|
Constraint,
|
||||||
Interns,
|
ModuleId,
|
||||||
) {
|
Interns,
|
||||||
|
),
|
||||||
|
Fail,
|
||||||
|
> {
|
||||||
let home = repl_home();
|
let home = repl_home();
|
||||||
let CanExprOut {
|
let CanExprOut {
|
||||||
loc_expr,
|
loc_expr,
|
||||||
|
@ -350,7 +420,7 @@ pub fn uniq_expr_with(
|
||||||
var,
|
var,
|
||||||
interns,
|
interns,
|
||||||
..
|
..
|
||||||
} = can_expr_with(arena, home, expr_str);
|
} = can_expr_with(arena, home, expr_str)?;
|
||||||
|
|
||||||
// double check
|
// double check
|
||||||
let var_store = VarStore::new(old_var_store.fresh());
|
let var_store = VarStore::new(old_var_store.fresh());
|
||||||
|
@ -389,9 +459,9 @@ pub fn uniq_expr_with(
|
||||||
|
|
||||||
let subs2 = Subs::new(var_store.into());
|
let subs2 = Subs::new(var_store.into());
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
loc_expr, output, problems, subs2, var, constraint, home, interns,
|
loc_expr, output, problems, subs2, var, constraint, home, interns,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CanExprOut {
|
pub struct CanExprOut {
|
||||||
|
@ -405,14 +475,8 @@ pub struct CanExprOut {
|
||||||
pub constraint: Constraint,
|
pub constraint: Constraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut {
|
pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> Result<CanExprOut, Fail> {
|
||||||
let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|e| {
|
let loc_expr = parse_loc_with(&arena, expr_str)?;
|
||||||
panic!(
|
|
||||||
"can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}",
|
|
||||||
expr_str, e
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let var_store = VarStore::default();
|
let var_store = VarStore::default();
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
let expected = Expected::NoExpectation(Type::Variable(var));
|
let expected = Expected::NoExpectation(Type::Variable(var));
|
||||||
|
@ -488,7 +552,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
all_ident_ids,
|
all_ident_ids,
|
||||||
};
|
};
|
||||||
|
|
||||||
CanExprOut {
|
Ok(CanExprOut {
|
||||||
loc_expr,
|
loc_expr,
|
||||||
output,
|
output,
|
||||||
problems: env.problems,
|
problems: env.problems,
|
||||||
|
@ -497,7 +561,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
||||||
interns,
|
interns,
|
||||||
var,
|
var,
|
||||||
constraint,
|
constraint,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mut_map_from_pairs<K, V, I>(pairs: I) -> MutMap<K, V>
|
pub fn mut_map_from_pairs<K, V, I>(pairs: I) -> MutMap<K, V>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue