Fix annotated defs in repl

This commit is contained in:
Richard Feldman 2022-10-30 13:32:33 -04:00
parent daa87093ca
commit 0c8f6a0a72
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
3 changed files with 117 additions and 20 deletions

View file

@ -925,6 +925,8 @@ pub fn parse_single_def<'a>(
}
}
// This is a macro only because trying to make it be a function caused lifetime issues.
#[macro_export]
macro_rules! join_ann_to_body {
($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $ann_pattern:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{
// join this body with the preceding annotation
@ -934,20 +936,24 @@ macro_rules! join_ann_to_body {
ann_type: $arena.alloc(*$ann_type),
comment: $spaces_before_current
.first()
.and_then(CommentOrNewline::comment_str),
.and_then($crate::ast::CommentOrNewline::comment_str),
body_pattern: $arena.alloc($loc_pattern),
body_expr: *$arena.alloc($loc_def_expr),
};
(
value_def,
Region::span_across(&$ann_pattern.region, &$region),
roc_region::all::Region::span_across(&$ann_pattern.region, &$region),
)
}};
}
// This is a macro only because trying to make it be a function caused lifetime issues.
#[macro_export]
macro_rules! join_alias_to_body {
($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $header:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{
use roc_region::all::Region;
// This is a case like
// UserId x : [UserId Int]
// UserId x = UserId 42
@ -966,7 +972,7 @@ macro_rules! join_alias_to_body {
ann_type: $arena.alloc(*$ann_type),
comment: $spaces_before_current
.first()
.and_then(CommentOrNewline::comment_str),
.and_then($crate::ast::CommentOrNewline::comment_str),
body_pattern: $arena.alloc($loc_pattern),
body_expr: *$arena.alloc($loc_def_expr),
};

View file

@ -9,6 +9,7 @@ use roc_parse::expr::{parse_single_def, ExprParseOptions, SingleDef};
use roc_parse::parser::Either;
use roc_parse::parser::{EClosure, EExpr};
use roc_parse::state::State;
use roc_parse::{join_alias_to_body, join_ann_to_body};
use roc_region::all::Loc;
use roc_repl_eval::gen::{Problems, ReplOutput};
use rustyline::highlight::{Highlighter, PromptInfo};
@ -124,18 +125,8 @@ impl ReplState {
},
_,
) => {
// We received a type annotation, like `x : Str`
//
// This might be the beginning of an AnnotatedBody, or it might be
// a standalone annotation.
//
// If the input ends in a newline, that means the user pressed Enter
// twice after the annotation, indicating this is not an AnnotatedBody,
// but rather a standalone Annotation. As such, record it as a PastDef!
if src.ends_with('\n') {
// Record the standalone type annotation for future use
self.add_past_def(ident.trim_end().to_string(), src.to_string());
}
// Record the standalone type annotation for future use.
self.add_past_def(ident.trim_end().to_string(), src.to_string());
// Return early without running eval, since neither standalone annotations
// nor pending potential AnnotatedBody exprs can be evaluated as expressions.
@ -290,6 +281,110 @@ fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> {
&arena,
State::new(src_bytes),
) {
Ok((
_,
Some(SingleDef {
type_or_value: Either::First(TypeDef::Alias { header, ann }),
..
}),
state,
)) => {
// This *could* be an AnnotatedBody, e.g. in a case like this:
//
// UserId x : [UserId Int]
// UserId x = UserId 42
//
// We optimistically parsed the first line as an alias; we might now
// turn it into an annotation.
match parse_single_def(
ExprParseOptions {
accept_multi_backpassing: true,
check_for_arrow: true,
},
0,
&arena,
state,
) {
Ok((
_,
Some(SingleDef {
type_or_value:
Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)),
region,
spaces_before,
}),
_,
)) if spaces_before.len() <= 1 => {
// This was, in fact, an AnnotatedBody! Build and return it.
let (value_def, _) = join_alias_to_body!(
arena,
loc_pattern,
loc_def_expr,
header,
&ann,
spaces_before,
region
);
ParseOutcome::ValueDef(value_def)
}
_ => {
// This was not an AnnotatedBody, so return the alias.
ParseOutcome::TypeDef(TypeDef::Alias { header, ann })
}
}
}
Ok((
_,
Some(SingleDef {
type_or_value:
Either::Second(ValueDef::Annotation(ann_pattern, ann_type)),
..
}),
state,
)) => {
// This *could* be an AnnotatedBody, if the next line is a body.
match parse_single_def(
ExprParseOptions {
accept_multi_backpassing: true,
check_for_arrow: true,
},
0,
&arena,
state,
) {
Ok((
_,
Some(SingleDef {
type_or_value:
Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)),
region,
spaces_before,
}),
_,
)) if spaces_before.len() <= 1 => {
// This was, in fact, an AnnotatedBody! Build and return it.
let (value_def, _) = join_ann_to_body!(
arena,
loc_pattern,
loc_def_expr,
&ann_pattern,
&ann_type,
spaces_before,
region
);
ParseOutcome::ValueDef(value_def)
}
_ => {
// This was not an AnnotatedBody, so return the standalone annotation.
ParseOutcome::ValueDef(ValueDef::Annotation(
ann_pattern,
ann_type,
))
}
}
}
Ok((
_,
Some(SingleDef {

View file

@ -38,11 +38,7 @@ fn annotated_body() {
input.push_str("t = A");
incomplete(&mut input);
let mut state = ReplState::new();
complete(&input, &mut state, Ok(("A : [A]*", "t")));
complete(&input, &mut ReplState::new(), Ok(("A : [A, B, C]", "t")));
}
#[test]