From ab4ff040da3401f67945e935052f0e487cd8630f Mon Sep 17 00:00:00 2001 From: Arthur Paulino Date: Tue, 31 Oct 2023 11:45:39 -0300 Subject: [PATCH] add a demo flag on the load command (#806) --- src/cli/mod.rs | 14 ++++++++--- src/cli/repl.rs | 54 +++++++++++++++++++++++++++++----------- src/cli/repl/meta_cmd.rs | 2 +- src/lem/store.rs | 10 ++++++-- src/parser/position.rs | 8 ++++++ src/syntax.rs | 16 ++++++++++++ tests/lurk-lib-tests.rs | 2 +- 7 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a608e4a6bf..f46d7f7f11 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -110,6 +110,10 @@ struct LoadArgs { /// Path to circom directory #[clap(long, value_parser)] circom_dir: Option, + + /// Flag to load the file in demo mode + #[arg(long)] + demo: bool, } #[derive(Parser, Debug)] @@ -149,6 +153,9 @@ struct LoadCli { #[clap(long, value_parser)] circom_dir: Option, + + #[arg(long)] + demo: bool, } impl LoadArgs { @@ -166,6 +173,7 @@ impl LoadArgs { proofs_dir: self.proofs_dir, commits_dir: self.commits_dir, circom_dir: self.circom_dir, + demo: self.demo, } } } @@ -304,7 +312,7 @@ impl ReplCli { ( $rc: expr, $limit: expr, $field: path, $backend: expr ) => {{ let mut repl = new_repl!(self, $rc, $limit, $field, $backend); if let Some(lurk_file) = &self.load { - repl.load_file(lurk_file)?; + repl.load_file(lurk_file, false)?; } repl.start() }}; @@ -344,7 +352,6 @@ impl ReplCli { backend.validate_field(field)?; match field { LanguageField::Pallas => repl!(rc, limit, pallas::Scalar, backend.clone()), - // LanguageField::Vesta => repl!(rc, limit, vesta::Scalar, backend), LanguageField::Vesta => todo!(), LanguageField::BLS12_381 => todo!(), LanguageField::BN256 => todo!(), @@ -358,7 +365,7 @@ impl LoadCli { macro_rules! load { ( $rc: expr, $limit: expr, $field: path, $backend: expr ) => {{ let mut repl = new_repl!(self, $rc, $limit, $field, $backend); - repl.load_file(&self.lurk_file)?; + repl.load_file(&self.lurk_file, self.demo)?; if self.prove { repl.prove_last_frames()?; } @@ -400,7 +407,6 @@ impl LoadCli { backend.validate_field(field)?; match field { LanguageField::Pallas => load!(rc, limit, pallas::Scalar, backend.clone()), - // LanguageField::Vesta => load!(rc, limit, vesta::Scalar, backend), LanguageField::Vesta => todo!(), LanguageField::BLS12_381 => todo!(), LanguageField::BN256 => todo!(), diff --git a/src/cli/repl.rs b/src/cli/repl.rs index 16560b24ea..743c55cabc 100644 --- a/src/cli/repl.rs +++ b/src/cli/repl.rs @@ -9,7 +9,7 @@ use rustyline::{ Config, Editor, }; use rustyline_derive::{Completer, Helper, Highlighter, Hinter}; -use std::{cell::RefCell, collections::HashMap, fs::read_to_string, rc::Rc, sync::Arc}; +use std::{cell::RefCell, collections::HashMap, fs::read_to_string, io::Write, rc::Rc, sync::Arc}; use tracing::info; use crate::{ @@ -433,23 +433,54 @@ impl Repl { Ok(()) } - fn handle_form<'a>(&mut self, input: parser::Span<'a>) -> Result> { - let (input, ptr, is_meta) = self.store.read_maybe_meta(self.state.clone(), &input)?; + #[inline] + fn input_marker(&self) -> String { + format!( + "{}> ", + self.state + .borrow() + .fmt_to_string(self.state.borrow().get_current_package_name()) + ) + } + + fn handle_form<'a>(&mut self, input: parser::Span<'a>, demo: bool) -> Result> { + let (syntax_start, mut new_input, ptr, is_meta) = + self.store.read_maybe_meta(self.state.clone(), &input)?; + if demo { + let potential_commentaries = &input[..syntax_start]; + let actual_syntax = &input[syntax_start..new_input.location_offset()]; + let input_marker = &self.input_marker(); + if actual_syntax.contains('\n') { + // print the expression on a new line to avoid messing with the user's formatting + print!("{potential_commentaries}{input_marker}\n{actual_syntax}"); + } else { + print!("{potential_commentaries}{input_marker}{actual_syntax}"); + } + std::io::stdout().flush()?; + // wait for ENTER to be pressed + std::io::stdin().read_line(&mut String::new())?; + // ENTER already prints a new line so we can remove it from the start of incoming input + new_input = parser::Span::new(new_input.trim_start_matches('\n')); + } if is_meta { self.handle_meta(ptr)?; } else { self.handle_non_meta(ptr)?; } - Ok(input) + Ok(new_input) } - pub fn load_file(&mut self, file_path: &Utf8Path) -> Result<()> { + pub fn load_file(&mut self, file_path: &Utf8Path, demo: bool) -> Result<()> { let input = read_to_string(file_path)?; - println!("Loading {file_path}"); + if demo { + println!("Loading {file_path} in demo mode"); + } else { + println!("Loading {file_path}"); + } let mut input = parser::Span::new(&input); loop { - match self.handle_form(input) { + match self.handle_form(input, demo) { Ok(new_input) => input = new_input, Err(e) => { if let Some(parser::Error::NoInput) = e.downcast_ref::() { @@ -484,16 +515,11 @@ impl Repl { } loop { - match editor.readline(&format!( - "{}> ", - self.state - .borrow() - .fmt_to_string(self.state.borrow().get_current_package_name()) - )) { + match editor.readline(&self.input_marker()) { Ok(line) => { editor.save_history(history_path)?; match self.store.read_maybe_meta(self.state.clone(), &line) { - Ok((_, expr_ptr, is_meta)) => { + Ok((.., expr_ptr, is_meta)) => { if is_meta { if let Err(e) = self.handle_meta(expr_ptr) { println!("!Error: {e}"); diff --git a/src/cli/repl/meta_cmd.rs b/src/cli/repl/meta_cmd.rs index 9bbf8df42d..70e0814711 100644 --- a/src/cli/repl/meta_cmd.rs +++ b/src/cli/repl/meta_cmd.rs @@ -40,7 +40,7 @@ impl MetaCmd { match repl.store.fetch_string(&first) { Some(path) => { let joined = repl.pwd_path.join(Utf8Path::new(&path)); - repl.load_file(&joined)? + repl.load_file(&joined, false)? } _ => bail!("Argument of `load` must be a string."), } diff --git a/src/lem/store.rs b/src/lem/store.rs index 70730be58f..814e7463a9 100644 --- a/src/lem/store.rs +++ b/src/lem/store.rs @@ -522,11 +522,17 @@ impl Store { &self, state: Rc>, input: &'a str, - ) -> Result<(Span<'a>, Ptr, bool), Error> { + ) -> Result<(usize, Span<'a>, Ptr, bool), Error> { match preceded(syntax::parse_space, syntax::parse_maybe_meta(state, false)) .parse(input.into()) { - Ok((i, Some((is_meta, x)))) => Ok((i, self.intern_syntax(x), is_meta)), + Ok((i, Some((is_meta, x)))) => { + let from_offset = x + .get_pos() + .get_from_offset() + .expect("Parsed syntax should have its Pos set"); + Ok((from_offset, i, self.intern_syntax(x), is_meta)) + } Ok((_, None)) => Err(Error::NoInput), Err(e) => Err(Error::Syntax(format!("{}", e))), } diff --git a/src/parser/position.rs b/src/parser/position.rs index 1aa8f688eb..4a8d2f8775 100644 --- a/src/parser/position.rs +++ b/src/parser/position.rs @@ -76,4 +76,12 @@ impl Pos { upto_column: upto.get_utf8_column(), } } + + /// Retrieves the `from_offset` attribute, if present + pub fn get_from_offset(&self) -> Option { + match self { + Self::No => None, + Self::Pos { from_offset, .. } => Some(*from_offset), + } + } } diff --git a/src/syntax.rs b/src/syntax.rs index 1ba2690b4e..dd9f0b0942 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -36,6 +36,22 @@ pub enum Syntax { Improper(Pos, Vec>, Box>), } +impl Syntax { + /// Retrieves the `Pos` attribute + pub fn get_pos(&self) -> &Pos { + match self { + Self::Num(pos, _) + | Self::UInt(pos, _) + | Self::Symbol(pos, _) + | Self::String(pos, _) + | Self::Char(pos, _) + | Self::Quote(pos, _) + | Self::List(pos, _) + | Self::Improper(pos, ..) => pos, + } + } +} + #[cfg(not(target_arch = "wasm32"))] impl Arbitrary for Syntax { type Parameters = (); diff --git a/tests/lurk-lib-tests.rs b/tests/lurk-lib-tests.rs index 2a9324f8b2..a3cdde9b44 100644 --- a/tests/lurk-lib-tests.rs +++ b/tests/lurk-lib-tests.rs @@ -43,6 +43,6 @@ fn lurk_cli_tests() { let joined_new = Utf8PathBuf::from_path_buf(joined.clone()).unwrap(); repl::>, _, Coproc>(Some(joined), Lang::new()).unwrap(); - let _ = repl_new.load_file(&joined_new); + let _ = repl_new.load_file(&joined_new, false); } }