Remove the whole #var1 thing

This commit is contained in:
Richard Feldman 2023-09-17 01:00:15 -04:00
parent f4937e72cc
commit 074401fbdf
No known key found for this signature in database
GPG Key ID: F1F21AA5B1D9E43B
11 changed files with 39 additions and 185 deletions

View File

@ -49,27 +49,19 @@ pub fn main() -> i32 {
editor.add_history_entry(line);
let dimensions = editor.dimensions();
let repl_state = &mut editor
.helper_mut()
.expect("Editor helper was not set")
.state;
arena.reset();
match repl_state.step(&arena, &line, target_info, DEFAULT_PALETTE) {
ReplAction::Eval {
opt_mono,
problems,
opt_var_name,
} => {
let output =
evaluate(opt_mono, problems, opt_var_name, &target, dimensions);
match repl_state.step(&arena, line, target_info, DEFAULT_PALETTE) {
ReplAction::Eval { opt_mono, problems } => {
let output = evaluate(opt_mono, problems, &target);
// If there was no output, don't print a blank line!
// (This happens for something like a type annotation.)
if !output.is_empty() {
// Overwrite the previous line so we can do things like
// print " #val1" after what the user just entered.
println!("\x1B[A{PROMPT}{line}{output}");
println!("{output}");
}
}
ReplAction::Exit => {
@ -104,18 +96,10 @@ pub fn main() -> i32 {
pub fn evaluate(
opt_mono: Option<MonomorphizedModule<'_>>,
problems: Problems,
opt_var_name: Option<String>,
target: &Triple,
dimensions: Option<(usize, usize)>,
) -> String {
let opt_output = opt_mono.and_then(|mono| eval_llvm(mono, target, OptLevel::Normal));
format_output(
ANSI_STYLE_CODES,
opt_output,
problems,
opt_var_name,
dimensions,
)
format_output(ANSI_STYLE_CODES, opt_output, problems)
}
#[derive(Default)]

View File

@ -126,19 +126,13 @@ pub fn expect_success(input: &str, expected: &str) {
assert_multiline_str_eq!("", out.stderr.as_str());
// Don't consider the auto variable name (e.g. "# val1") at the end.
// The state.rs tests do that!
let mut iter = out.stdout.lines().rev();
let line = iter.next().unwrap();
let comment_index = line.rfind('#').unwrap_or(line.len());
let line_without_comment = line[0..comment_index].trim_end();
// Sometimes the "# val1" wraps around to its own line; if this happens,
// we just use the preceding line instead.
if line_without_comment.is_empty() {
if line.is_empty() {
assert_multiline_str_eq!(expected, iter.next().unwrap().trim_end());
} else {
assert_multiline_str_eq!(expected, line_without_comment);
assert_multiline_str_eq!(expected, line);
}
assert!(out.status.success());

View File

@ -14,27 +14,16 @@ use target_lexicon::Triple;
#[test]
fn one_plus_one() {
complete("1 + 1", &mut ReplState::new(), "2 : Num *", "val1");
}
#[test]
fn generated_expr_names() {
let mut state = ReplState::new();
complete("2 * 3", &mut state, "6 : Num *", "val1");
complete("4 - 1", &mut state, "3 : Num *", "val2");
complete("val1 + val2", &mut state, "9 : Num *", "val3");
complete("1 + (val2 * val3)", &mut state, "28 : Num *", "val4");
complete("1 + 1", &mut ReplState::new(), "2 : Num *");
}
#[test]
fn persisted_defs() {
let mut state = ReplState::new();
complete("x = 5", &mut state, "5 : Num *", "x");
complete("7 - 3", &mut state, "4 : Num *", "val1");
complete("y = 6", &mut state, "6 : Num *", "y");
complete("val1 + x + y", &mut state, "15 : Num *", "val2");
complete("x = 5", &mut state, "5 : Num *");
complete("7 - 3", &mut state, "4 : Num *");
complete("y = 6", &mut state, "6 : Num *");
}
#[test]
@ -45,7 +34,7 @@ fn annotated_body() {
input.push_str("t = A");
complete(&input, &mut ReplState::new(), "A : [A, B, C]", "t");
complete(&input, &mut ReplState::new(), "A : [A, B, C]");
}
#[test]
@ -60,7 +49,7 @@ fn exhaustiveness_problem() {
input.push_str("t = A");
complete(&input, &mut state, "A : [A, B, C]", "t");
complete(&input, &mut state, "A : [A, B, C]");
}
// Run a `when` on it that isn't exhaustive
@ -118,7 +107,7 @@ fn standalone_annotation() {
/// validate and step the given input, then check the Result vs the output
/// with ANSI escape codes stripped.
fn complete(input: &str, state: &mut ReplState, expected_start: &str, expected_end: &str) {
fn complete(input: &str, state: &mut ReplState, expected_start: &str) {
assert!(!is_incomplete(input));
let arena = Bump::new();
let target = Triple::host();
@ -127,15 +116,10 @@ fn complete(input: &str, state: &mut ReplState, expected_start: &str, expected_e
let repl_helper = ReplHelper::default();
let mut editor = Editor::<ReplHelper>::new();
editor.set_helper(Some(repl_helper));
let dimensions = editor.dimensions();
match action {
ReplAction::Eval {
opt_mono,
problems,
opt_var_name,
} => {
let string = evaluate(opt_mono, problems, opt_var_name, &target, dimensions);
ReplAction::Eval { opt_mono, problems } => {
let string = evaluate(opt_mono, problems, &target);
let escaped =
std::string::String::from_utf8(strip_ansi_escapes::strip(string.trim()).unwrap())
.unwrap();
@ -143,12 +127,6 @@ fn complete(input: &str, state: &mut ReplState, expected_start: &str, expected_e
let comment_index = escaped.rfind('#').unwrap_or(escaped.len());
assert_eq!(expected_start, (escaped[0..comment_index].trim()));
assert_eq!(
expected_end,
// +1 because we want to skip over the '#' itself
(escaped[comment_index + 1..].trim())
);
}
_ => {
panic!("Unexpected action: {:?}", action);
@ -175,15 +153,10 @@ fn error(input: &str, state: &mut ReplState, expected_step_result: String) {
let repl_helper = ReplHelper::default();
let mut editor = Editor::<ReplHelper>::new();
editor.set_helper(Some(repl_helper));
let dimensions = editor.dimensions();
match action {
ReplAction::Eval {
opt_mono,
problems,
opt_var_name,
} => {
let string = evaluate(opt_mono, problems, opt_var_name, &target, dimensions);
ReplAction::Eval { opt_mono, problems } => {
let string = evaluate(opt_mono, problems, &target);
let escaped =
std::string::String::from_utf8(strip_ansi_escapes::strip(string.trim()).unwrap())
.unwrap();

View File

@ -588,11 +588,7 @@ fn multiline_string_non_wasm() {
);
assert_multiline_str_eq!("", out.stderr.as_str());
// Don't consider the auto variable name ("# val1") at the end.
// The state.rs tests do that!
assert_multiline_str_eq!(expected, out.stdout.replace("# val1", "").trim());
assert_multiline_str_eq!(expected, out.stdout.trim());
assert!(out.status.success());
}
@ -1409,9 +1405,6 @@ fn interpolation_with_nested_interpolation() {
Enter an expression to evaluate, or a definition (like x = 1) to use in future expressions.
Unless there was a compile-time error, expressions get automatically named so you can refer to them later.
For example, if you see # val1 after an output, you can now refer to that expression as val1 in future expressions.
Tips:
- ctrl-v + ctrl-j makes a newline

View File

@ -160,12 +160,8 @@ pub fn expect(input: &'static str, expected: &str) {
// We need to get rid of HTML tags, and we can be quite specific about it!
// If we ever write more complex test cases, we might need regex here.
let without_html = raw_output
.replace("<span class='color-magenta'> : </span>", " : ")
.replace("<span class='color-green'> # val1</span>", "");
let without_html = raw_output.replace("<span class='color-magenta'> : </span>", " : ");
// Whitespace that was originally in front of the `# val1` is now at the end,
// and there's other whitespace at both ends too. Trim it all.
let clean_output = without_html.trim();
assert_eq!(clean_output, expected);

View File

@ -4,7 +4,7 @@ pub mod colors;
pub mod repl_state;
use bumpalo::Bump;
use colors::{BLUE, END_COL, GREEN, PINK};
use colors::{BLUE, END_COL, PINK};
use const_format::concatcp;
use repl_state::{parse_src, ParseOutcome};
use roc_parse::ast::{Expr, ValueDef};
@ -29,16 +29,7 @@ pub const TIPS: &str = concatcp!(
BLUE,
"x = 1",
END_COL,
") to use in future expressions.\n\nUnless there was a compile-time error, expressions get automatically named so you can refer to them later.\nFor example, if you see ",
GREEN,
"# val1",
END_COL,
" after an output, you can now refer to that expression as ",
BLUE,
"val1",
END_COL,
" in future expressions.\n\n",
"Tips:\n\n",
") to use in future expressions.\n\nTips:\n\n",
if cfg!(target_family = "wasm") {
// In the web REPL, the :quit command doesn't make sense. Just close the browser tab!
// We use Shift-Enter for newlines because it's nicer than our workaround for Unix terminals (see below)
@ -121,8 +112,6 @@ pub fn format_output(
style_codes: StyleCodes,
opt_output: Option<ReplOutput>,
problems: Problems,
opt_var_name: Option<String>,
dimensions: Option<(usize, usize)>,
) -> String {
let mut buf = String::new();
@ -145,17 +134,9 @@ pub fn format_output(
if !expr.is_empty() && problems.errors.is_empty() {
const EXPR_TYPE_SEPARATOR: &str = " : "; // e.g. in "5 : Num *"
// Print var_name on the line before the output
if let Some(var_name) = opt_var_name {
buf.push_str(style_codes.green);
buf.push_str(" # ");
buf.push_str(&var_name);
buf.push_str(style_codes.reset);
}
// Print the expr and its type
{
buf.push_str("\n\n");
buf.push('\n');
buf.push_str(&expr);
buf.push_str(style_codes.magenta); // Color for the type separator
buf.push_str(EXPR_TYPE_SEPARATOR);

View File

@ -13,10 +13,6 @@ use roc_repl_eval::gen::{compile_to_mono, Problems};
use roc_reporting::report::Palette;
use roc_target::TargetInfo;
/// The prefix we use for the automatic variable names we assign to each expr,
/// e.g. if the prefix is "val" then the first expr you enter will be named "val1"
pub const AUTO_VAR_PREFIX: &str = "val";
#[derive(Debug, Clone, PartialEq)]
struct PastDef {
ident: String,
@ -26,7 +22,6 @@ struct PastDef {
pub struct ReplState {
past_defs: Vec<PastDef>,
past_def_idents: MutSet<String>,
last_auto_ident: u64,
}
impl Default for ReplState {
@ -41,7 +36,6 @@ pub enum ReplAction<'a> {
Eval {
opt_mono: Option<MonomorphizedModule<'a>>,
problems: Problems,
opt_var_name: Option<String>,
},
Exit,
Help,
@ -53,7 +47,6 @@ impl ReplState {
Self {
past_defs: Default::default(),
past_def_idents: Default::default(),
last_auto_ident: 0,
}
}
@ -65,17 +58,15 @@ impl ReplState {
palette: Palette,
) -> ReplAction<'a> {
let pending_past_def;
let mut opt_var_name;
let src: &str = match parse_src(arena, line) {
ParseOutcome::Empty | ParseOutcome::Help => return ReplAction::Help,
ParseOutcome::Exit => return ReplAction::Exit,
ParseOutcome::Expr(_) | ParseOutcome::Incomplete | ParseOutcome::SyntaxErr => {
pending_past_def = None;
// If it's a SyntaxErr (or Incomplete at this point, meaning it will
// become a SyntaxErr as soon as we evaluate it),
// proceed as normal and let the error reporting happen during eval.
opt_var_name = None;
line
}
ParseOutcome::ValueDef(value_def) => {
@ -110,7 +101,6 @@ impl ReplState {
..
} => {
pending_past_def = Some((ident.to_string(), line.to_string()));
opt_var_name = Some(ident.to_string());
// Recreate the body of the def and then evaluate it as a lookup.
// We do this so that any errors will get reported as part of this expr;
@ -176,61 +166,19 @@ impl ReplState {
}
};
// Record e.g. "val1" as a past def, unless our input was exactly the name of
// an existing identifer (e.g. I just typed "val1" into the prompt - there's no
// need to reassign "val1" to "val2" just because I wanted to see what its value was!)
let (opt_mono, problems) =
match opt_var_name.or_else(|| self.past_def_idents.get(src.trim()).cloned()) {
Some(existing_ident) => {
opt_var_name = Some(existing_ident);
compile_to_mono(
arena,
self.past_defs.iter().map(|def| def.src.as_str()),
src,
target_info,
palette,
)
}
None => {
let (output, problems) = compile_to_mono(
arena,
self.past_defs.iter().map(|def| def.src.as_str()),
src,
target_info,
palette,
);
// Don't persist defs that have compile errors
if problems.errors.is_empty() {
let var_name = format!("{AUTO_VAR_PREFIX}{}", self.next_auto_ident());
let src = format!("{var_name} = {}", src.trim_end());
opt_var_name = Some(var_name.clone());
self.add_past_def(var_name, src);
} else {
opt_var_name = None;
}
(output, problems)
}
};
let (opt_mono, problems) = compile_to_mono(
arena,
self.past_defs.iter().map(|def| def.src.as_str()),
src,
target_info,
palette,
);
if let Some((ident, src)) = pending_past_def {
self.add_past_def(ident, src);
}
ReplAction::Eval {
opt_mono,
problems,
opt_var_name,
}
}
fn next_auto_ident(&mut self) -> u64 {
self.last_auto_ident += 1;
self.last_auto_ident
ReplAction::Eval { opt_mono, problems }
}
fn add_past_def(&mut self, ident: String, src: String) {

View File

@ -203,23 +203,13 @@ pub async fn entrypoint_from_js(src: String) -> String {
"To exit the web version of the REPL, just close the browser tab!".to_string()
}
ReplAction::Nothing => String::new(),
ReplAction::Eval {
opt_mono,
problems,
opt_var_name,
} => {
ReplAction::Eval { opt_mono, problems } => {
let opt_output = match opt_mono {
Some(mono) => eval_wasm(arena, target_info, mono).await,
None => None,
};
let dimensions = None; // TODO: we could get the window dimensions from JS...
format_output(
HTML_STYLE_CODES,
opt_output,
problems,
opt_var_name,
dimensions,
)
format_output(HTML_STYLE_CODES, opt_output, problems)
}
}
}

View File

@ -53,8 +53,7 @@ samp .ann {
color: #f384fd;
}
samp .autovar .comment {
/* automatic variable names in the repl, e.g. # val1 */
samp .comment {
color: #338545;
}

View File

@ -20,7 +20,7 @@ Try typing this in the REPL and pressing Enter:
The REPL should cheerfully display the following:
<pre><samp><span class="literal">"Hello, World!" </span><span class="colon">:</span> Str <span class="comment"> # val1</span></samp></pre>
<pre><samp><span class="literal">"Hello, World!" </span><span class="colon">:</span> Str</samp></pre>
Congratulations! You've just written your first Roc code.
@ -28,8 +28,6 @@ Congratulations! You've just written your first Roc code.
When you entered the _expression_ `"Hello, World!"`, the REPL printed it back out. It also printed `: Str`, because `Str` is that expression's type. We'll talk about types later; for now, let's ignore the `:` and whatever comes after it whenever we see them.
The REPL also printed `# val1` at the end of the line. That means from now on you can use the variable name `val1` to refer to the `"Hello, World!"` expression you just entered.
Let's try that out. Put this into the repl and press Enter:
<pre><samp class="repl-prompt">val1</samp></pre>
@ -55,8 +53,6 @@ You should see this output:
According to the REPL, one plus one equals two. Sounds right!
> Notice that the REPL printed `# val2` here. This works just like `# val1` did before, but it chose the name `val2` for this expression because `val1` was already taken. As we continue entering more expressions into the REPL, you'll see more and more of these generated names—but they won't be mentioned again in this tutorial, since they're just a convenience.
Roc will respect [order of operations](https://en.wikipedia.org/wiki/Order_of_operations) when using multiple arithmetic operators like `+` and `-`, but you can use parentheses to specify exactly how they should be grouped.
<pre><samp><span class="repl-prompt">1 <span class="op">+</span> 2 <span class="op">*</span> (3 <span class="op">-</span> 4)

View File

@ -22,7 +22,7 @@ Try typing this in the REPL and pressing Enter:
The REPL should cheerfully display the following:
<pre><samp><span class="literal">"Hello, World!" </span><span class="colon">:</span> Str <span class="comment"> # val1</span></samp></pre>
<pre><samp><span class="literal">"Hello, World!" </span><span class="colon">:</span> Str</samp></pre>
Congratulations! You've just written your first Roc code.
@ -30,4 +30,4 @@ Congratulations! You've just written your first Roc code.
When you entered the _expression_ `"Hello, World!"`, the REPL printed it back out. It also printed `: Str`, because `Str` is that expression's type. We'll talk about types later; for now, let's ignore the `:` and whatever comes after it whenever we see them.
**TODO -- move tutorial here when this site is migrated.**
**TODO -- move tutorial here when this site is migrated.**