From b568bb6fdd2bb5cdd65b63c81880fb04426a2908 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 7 Sep 2023 13:13:22 +0900 Subject: [PATCH 1/7] Cleaning up behavior around enter and ctrl-j (force submit) --- repl/src/interactive_helper.rs | 20 ++++++++++++----- repl/src/main.rs | 40 ++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/repl/src/interactive_helper.rs b/repl/src/interactive_helper.rs index 60e7f8fc9..6bf581f03 100644 --- a/repl/src/interactive_helper.rs +++ b/repl/src/interactive_helper.rs @@ -1,5 +1,6 @@ use std::borrow::Cow::{self, Borrowed, Owned}; +use std::sync::{Arc, Mutex}; use std::cell::RefCell; use rustyline::completion::FilenameCompleter; @@ -22,7 +23,8 @@ pub struct ReplHelper { hinter: HistoryHinter, pub colored_prompt: String, cursor_bracket: std::cell::Cell>, // If the cursor is over or near a bracket to match - checked_line: std::cell::RefCell, + pub force_submit: Arc>, // We use this to communicate between the key event handler and the Validator + checked_line: RefCell, style: StyleSettings, } @@ -167,9 +169,13 @@ impl Validator for ReplHelper { fn validate(&self, ctx: &mut ValidationContext) -> Result { //This validator implements the following behavior: - // * if user hits enter and line is valid, it will be submitted. - // * if user hits enter and line is invalid, it will treat it as a newline - // * If user hits enter twice in a row, it will report a syntax error + // * if user hits enter and the line is valid, and the cursor is at the end of the line, it will be submitted. + // * if user hits ctrl-J (force submit) and the line is valid, it will be submitted regardless of cursor position + // * if user hits enter and the line is invalid, a newline will be inserted at the cursor position, unless + // a linefeed has just been added to the end of the line, in which case a syntax error is reported + // * if user hits ctrl-J (force submit) and line is invalid, it will be a syntax error, regardless of cursor position + let force_submit = *self.force_submit.lock().unwrap(); + *self.force_submit.lock().unwrap() = false; let mut validation_result = ValidationResult::Incomplete; self.metta.borrow_mut().inside_env(|metta| { let mut parser = SExprParser::new(ctx.input()); @@ -188,7 +194,8 @@ impl Validator for ReplHelper { if input.len() < 1 { break; } - if *self.checked_line.borrow() != &input[0..input.len()-1] || input.as_bytes()[input.len()-1] != b'\n' { + if !force_submit && + (*self.checked_line.borrow() != &input[0..input.len()-1] || input.as_bytes()[input.len()-1] != b'\n') { *self.checked_line.borrow_mut() = ctx.input().to_string(); } else { validation_result = ValidationResult::Invalid(Some( @@ -216,7 +223,8 @@ impl ReplHelper { hinter: HistoryHinter {}, colored_prompt: "".to_owned(), cursor_bracket: std::cell::Cell::new(None), - checked_line: std::cell::RefCell::new(String::new()), + force_submit: Arc::new(Mutex::new(false)), + checked_line: RefCell::new(String::new()), style, } } diff --git a/repl/src/main.rs b/repl/src/main.rs index e93620a27..d8ceeb0ed 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -2,10 +2,10 @@ use std::path::PathBuf; use std::thread; use std::process::exit; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use rustyline::error::ReadlineError; -use rustyline::{Cmd, CompletionType, Config, EditMode, Editor, KeyEvent, KeyCode, Modifiers}; +use rustyline::{Cmd, CompletionType, Config, EditMode, Editor, KeyEvent, KeyCode, Modifiers, EventContext, RepeatCount, EventHandler, ConditionalEventHandler, Event}; use anyhow::Result; use clap::Parser; @@ -125,16 +125,11 @@ fn start_interactive_mode(repl_params: Shared, metta: MettaShim) -> rl.set_helper(Some(helper)); //KEY BEHAVIOR: Enter and ctrl-M will add a newline when the cursor is in the middle of a line, while // ctrl-J will submit the line. - //TODO: Rustyline seems to have a bug where this is only true sometimes. Needs to be debugged. - // Ideally Rustyline could just subsume the whole "accept_in_the_middle" behavior with a design that - // allows the Validator to access the key event, so the Validator could make the decision without - // special logic inside rustyline. + //TODO: Document this behavior in the README.md, and possibly other key bindings also rl.bind_sequence(KeyEvent( KeyCode::Enter, Modifiers::NONE ), Cmd::AcceptOrInsertLine { accept_in_the_middle: false, }); - rl.bind_sequence(KeyEvent::ctrl('j'), Cmd::AcceptOrInsertLine { - accept_in_the_middle: true, - }); + rl.bind_sequence(KeyEvent::ctrl('j'), EventHandler::Conditional(Box::new(EnterKeyHandler::new(rl.helper().unwrap().force_submit.clone())))); rl.bind_sequence(KeyEvent::alt('n'), Cmd::HistorySearchForward); rl.bind_sequence(KeyEvent::alt('p'), Cmd::HistorySearchBackward); if let Some(history_path) = &repl_params.borrow().history_file { @@ -186,3 +181,30 @@ fn start_interactive_mode(repl_params: Shared, metta: MettaShim) -> Ok(()) } + +struct EnterKeyHandler { + force_submit: Arc> +} + +impl EnterKeyHandler { + fn new(force_submit: Arc>) -> Self { + Self { + force_submit + } + } +} + +impl ConditionalEventHandler for EnterKeyHandler { + fn handle( + &self, + _evt: &Event, + _n: RepeatCount, + _positive: bool, + _ctx: &EventContext<'_> + ) -> Option { + *self.force_submit.lock().unwrap() = true; + Some(Cmd::AcceptOrInsertLine { + accept_in_the_middle: true, + }) + } +} From 325b8c0aff189316fe4a6e1db751ee354858a078 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 14 Sep 2023 14:11:52 +0900 Subject: [PATCH 2/7] Separating init.metta from repl.metta configs, and migrating repl configs to `pragma!` --- lib/src/metta/runner/mod.rs | 21 +++++++----- lib/src/metta/runner/stdlib.rs | 24 ++++---------- lib/src/metta/runner/stdlib2.rs | 12 +++---- repl/src/config.default.metta | 16 --------- repl/src/config_params.rs | 59 +++++++++++++++++++++------------ repl/src/init.default.metta | 2 ++ repl/src/interactive_helper.rs | 26 +++++++++------ repl/src/main.rs | 11 +++--- repl/src/metta_shim.rs | 14 +++----- repl/src/repl.default.metta | 14 ++++++++ 10 files changed, 102 insertions(+), 97 deletions(-) delete mode 100644 repl/src/config.default.metta create mode 100644 repl/src/init.default.metta create mode 100644 repl/src/repl.default.metta diff --git a/lib/src/metta/runner/mod.rs b/lib/src/metta/runner/mod.rs index c418a2fb7..5d6f9654e 100644 --- a/lib/src/metta/runner/mod.rs +++ b/lib/src/metta/runner/mod.rs @@ -36,7 +36,7 @@ pub struct Metta(Rc); pub struct MettaContents { space: DynSpace, tokenizer: Shared, - settings: Shared>, + settings: Shared>, modules: Shared>, search_paths: Vec, } @@ -141,19 +141,22 @@ impl Metta { &self.0.modules } - pub(crate) fn settings(&self) -> &Shared> { + pub fn settings(&self) -> &Shared> { &self.0.settings } - #[cfg(test)] - fn set_setting(&self, key: String, value: String) { + pub fn set_setting(&self, key: String, value: Atom) { self.0.settings.borrow_mut().insert(key, value); } - fn get_setting(&self, key: &str) -> Option { + pub fn get_setting(&self, key: &str) -> Option { self.0.settings.borrow().get(key.into()).cloned() } + pub fn get_setting_string(&self, key: &str) -> Option { + self.0.settings.borrow().get(key.into()).map(|a| a.to_string()) + } + pub fn run(&self, parser: &mut SExprParser) -> Result>, String> { let mut state = self.start_run(); @@ -261,7 +264,7 @@ impl Metta { } fn type_check(&self, atom: Atom) -> Result { - let is_type_check_enabled = self.get_setting("type-check").map_or(false, |val| val == "auto"); + let is_type_check_enabled = self.get_setting_string("type-check").map_or(false, |val| val == "auto"); if is_type_check_enabled && !validate_atom(self.0.space.borrow().as_space(), &atom) { Err(Atom::expr([ERROR_SYMBOL, atom, BAD_TYPE_SYMBOL])) } else { @@ -337,7 +340,7 @@ mod tests { "; let metta = Metta::new(DynSpace::new(GroundingSpace::new()), Shared::new(Tokenizer::new())); - metta.set_setting("type-check".into(), "auto".into()); + metta.set_setting("type-check".into(), sym!("auto")); let result = metta.run(&mut SExprParser::new(program)); assert_eq!(result, Ok(vec![vec![expr!("Error" ("foo" "b") "BadType")]])); } @@ -351,7 +354,7 @@ mod tests { "; let metta = Metta::new(DynSpace::new(GroundingSpace::new()), Shared::new(Tokenizer::new())); - metta.set_setting("type-check".into(), "auto".into()); + metta.set_setting("type-check".into(), sym!("auto")); let result = metta.run(&mut SExprParser::new(program)); assert_eq!(result, Ok(vec![vec![expr!("Error" ("foo" "b") "BadType")]])); } @@ -406,7 +409,7 @@ mod tests { "; let metta = Metta::new(DynSpace::new(GroundingSpace::new()), Shared::new(Tokenizer::new())); - metta.set_setting("type-check".into(), "auto".into()); + metta.set_setting("type-check".into(), sym!("auto")); let result = metta.run(&mut SExprParser::new(program)); assert_eq!(result, Ok(vec![vec![expr!("Error" ("foo" "b") "BadType")]])); } diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 94ec8b32b..46744b516 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -10,6 +10,7 @@ use crate::common::shared::Shared; use crate::common::assert::vec_eq_no_order; use crate::common::ReplacingMapper; +use std::convert::TryFrom; use std::rc::Rc; use std::cell::RefCell; use std::fmt::Display; @@ -170,14 +171,6 @@ impl Display for BindOp { } } -// TODO: move it into hyperon::atom module? -fn atom_as_sym(atom: &Atom) -> Option<&SymbolAtom> { - match atom { - Atom::Symbol(sym) => Some(sym), - _ => None, - } -} - impl Grounded for BindOp { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED]) @@ -185,7 +178,7 @@ impl Grounded for BindOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("bind! expects two arguments: token and atom"); - let token = atom_as_sym(args.get(0).ok_or_else(arg_error)?).ok_or("bind! expects symbol atom as a token")?.name(); + let token = <&SymbolAtom>::try_from(args.get(0).ok_or_else(arg_error)?).map_err(|_| "bind! expects symbol atom as a token")?.name(); let atom = args.get(1).ok_or_else(arg_error)?.clone(); let token_regex = Regex::new(token).map_err(|err| format!("Could convert token {} into regex: {}", token, err))?; @@ -678,11 +671,11 @@ impl Grounded for SuperposeOp { #[derive(Clone, PartialEq, Debug)] pub struct PragmaOp { - settings: Shared>, + settings: Shared>, } impl PragmaOp { - pub fn new(settings: Shared>) -> Self { + pub fn new(settings: Shared>) -> Self { Self{ settings } } } @@ -700,12 +693,9 @@ impl Grounded for PragmaOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("pragma! expects key and value as arguments"); - let key = atom_as_sym(args.get(0).ok_or_else(arg_error)?).ok_or("pragma! expects symbol atom as a key")?.name(); - let value = atom_as_sym(args.get(1).ok_or_else(arg_error)?).ok_or("pragma! expects symbol atom as a value")?.name(); - - // TODO: add support for Grounded values when needed - self.settings.borrow_mut().insert(key.into(), value.into()); - + let key = <&SymbolAtom>::try_from(args.get(0).ok_or_else(arg_error)?).map_err(|_| "pragma! expects symbol atom as a key")?.name(); + let value = args.get(1).ok_or_else(arg_error)?; + self.settings.borrow_mut().insert(key.into(), value.clone()); Ok(vec![]) } diff --git a/lib/src/metta/runner/stdlib2.rs b/lib/src/metta/runner/stdlib2.rs index 67200a91a..7d8540a0c 100644 --- a/lib/src/metta/runner/stdlib2.rs +++ b/lib/src/metta/runner/stdlib2.rs @@ -293,11 +293,11 @@ impl Grounded for CollapseOp { #[derive(Clone, PartialEq, Debug)] pub struct PragmaOp { - settings: Shared>, + settings: Shared>, } impl PragmaOp { - pub fn new(settings: Shared>) -> Self { + pub fn new(settings: Shared>) -> Self { Self{ settings } } } @@ -317,12 +317,8 @@ impl Grounded for PragmaOp { let arg_error = || ExecError::from("pragma! expects key and value as arguments"); let key = TryInto::<&SymbolAtom>::try_into(args.get(0).ok_or_else(arg_error)?) .map_err(|_| "pragma! expects symbol atom as a key")?.name(); - let value = TryInto::<&SymbolAtom>::try_into(args.get(1).ok_or_else(arg_error)?) - .map_err(|_| "pragma! expects symbol atom as a value")?.name(); - - // TODO: add support for Grounded values when needed - self.settings.borrow_mut().insert(key.into(), value.into()); - + let value = args.get(1).ok_or_else(arg_error)?; + self.settings.borrow_mut().insert(key.into(), value.clone()); Ok(vec![]) } diff --git a/repl/src/config.default.metta b/repl/src/config.default.metta deleted file mode 100644 index 73247a498..000000000 --- a/repl/src/config.default.metta +++ /dev/null @@ -1,16 +0,0 @@ - -; TODO: Let the "includePaths" be modifiable, but I want better string manipulation atoms - -(= ReplDefaultPrompt "> ") -; (= ReplStyledPrompt "\x1b[1;32m> \x1b[0m") ; TODO: currently the MeTTa string parser doesn't resolve escape chars, although perhaps it should - -; TODO: somebody with better design sense should tweak these, and also provide dark-mode setings -; ANSI escape codes to configure the syntax highlighter -(= ReplBracketStyles ("94" "93" "95" "96")) -(= ReplCommentStyle "32") -(= ReplVariableStyle "33") -(= ReplSymbolStyle "34") -(= ReplStringStyle "31") -(= ReplErrorStyle "91") -(= ReplBracketMatchStyle "1;7") -; (= ReplBracketMatchEnabled True) ;TODO: enable this when I have a reliable value interchange path built. Another use for https://github.com/trueagi-io/hyperon-experimental/issues/351 diff --git a/repl/src/config_params.rs b/repl/src/config_params.rs index 3b3f29da3..148ad5121 100644 --- a/repl/src/config_params.rs +++ b/repl/src/config_params.rs @@ -3,18 +3,33 @@ use std::path::{Path, PathBuf}; use std::io::Write; use std::fs; -const DEFAULT_CONFIG_METTA: &[u8] = include_bytes!("config.default.metta"); +const DEFAULT_INIT_METTA: &[u8] = include_bytes!("init.default.metta"); +const DEFAULT_REPL_METTA: &[u8] = include_bytes!("repl.default.metta"); + +pub const CFG_DEFAULT_PROMPT: &str = "ReplDefaultPrompt"; +pub const CFG_STYLED_PROMPT: &str = "ReplStyledPrompt"; +pub const CFG_BRACKET_STYLES: &str = "ReplBracketStyles"; +pub const CFG_COMMENT_STYLE: &str = "ReplCommentStyle"; +pub const CFG_VARIABLE_STYLE: &str = "ReplVariableStyle"; +pub const CFG_SYMBOL_STYLE: &str = "ReplSymbolStyle"; +pub const CFG_STRING_STYLE: &str = "ReplStringStyle"; +pub const CFG_ERROR_STYLE: &str = "ReplErrorStyle"; +pub const CFG_BRACKET_MATCH_STYLE: &str = "ReplBracketMatchStyle"; +pub const CFG_BRACKET_MATCH_ENABLED: &str = "ReplBracketMatchEnabled"; #[derive(Default, Debug)] pub struct ReplParams { /// Path to the config dir for the whole repl, in an OS-specific location - config_dir: PathBuf, + pub config_dir: PathBuf, - /// Path to the config.metta file, used to configure the repl - config_metta_path: PathBuf, + /// A path to the init.metta file that's run to customize the MeTTa environment + pub init_metta_path: PathBuf, + + /// A path to the repl.metta file that's run to configure the repl environment + pub repl_config_metta_path: PathBuf, /// Path to the dir containing the script being run, or the cwd the repl was invoked from in interactive mode - metta_working_dir: PathBuf, + pub metta_working_dir: PathBuf, /// Other include paths, specified either through command-line args or config settings include_paths: Vec, @@ -44,15 +59,24 @@ impl ReplParams { let modules_dir = config_dir.join("modules"); std::fs::create_dir_all(&modules_dir).unwrap(); - //Create the default config.metta file, if none exists - let config_metta_path = config_dir.join("config.metta"); - if !config_metta_path.exists() { + //Create the default init.metta file and repl.meta file, if they don't already exist + let init_metta_path = config_dir.join("init.metta"); + if !init_metta_path.exists() { + let mut file = fs::OpenOptions::new() + .create(true) + .write(true) + .open(&init_metta_path) + .expect(&format!("Error creating default init file at {init_metta_path:?}")); + file.write_all(&DEFAULT_INIT_METTA).unwrap(); + } + let repl_config_metta_path = config_dir.join("repl.metta"); + if !repl_config_metta_path.exists() { let mut file = fs::OpenOptions::new() .create(true) .write(true) - .open(&config_metta_path) - .expect(&format!("Error creating default config file at {config_metta_path:?}")); - file.write_all(&DEFAULT_CONFIG_METTA).unwrap(); + .open(&repl_config_metta_path) + .expect(&format!("Error creating default repl config file at {repl_config_metta_path:?}")); + file.write_all(&DEFAULT_REPL_METTA).unwrap(); } //Push the "modules" dir, as the last place to search after the paths specified on the cmd line @@ -63,7 +87,8 @@ impl ReplParams { Self { config_dir: config_dir.into(), - config_metta_path, + init_metta_path, + repl_config_metta_path, metta_working_dir, include_paths, history_file: Some(config_dir.join("history.txt")), @@ -77,14 +102,4 @@ impl ReplParams { [self.metta_working_dir.clone()].into_iter().chain( self.include_paths.iter().cloned()) } - - /// A path to the config.metta file that's run to configure the repl environment - pub fn config_metta_path(&self) -> &PathBuf { - - //TODO: Temporary access to avoid warning. Delete soon - let _ = self.config_dir; - - &self.config_metta_path - } - } diff --git a/repl/src/init.default.metta b/repl/src/init.default.metta new file mode 100644 index 000000000..04e8c3752 --- /dev/null +++ b/repl/src/init.default.metta @@ -0,0 +1,2 @@ + +; TODO: Let the "importPaths" be modifiable, but I want better string manipulation atoms diff --git a/repl/src/interactive_helper.rs b/repl/src/interactive_helper.rs index 6bf581f03..7e588443d 100644 --- a/repl/src/interactive_helper.rs +++ b/repl/src/interactive_helper.rs @@ -12,6 +12,7 @@ use rustyline::{Completer, Helper, Hinter}; use hyperon::metta::text::{SExprParser, SyntaxNodeType}; +use crate::config_params::*; use crate::metta_shim::MettaShim; #[derive(Helper, Completer, Hinter)] @@ -37,7 +38,7 @@ struct StyleSettings { string_style: String, error_style: String, bracket_match_style: String, - // bracket_match_enabled: bool, //TODO + bracket_match_enabled: bool, } impl Highlighter for ReplHelper { @@ -119,9 +120,11 @@ impl Highlighter for ReplHelper { } //See if we need to render this node with the "bracket blink" - if let Some((_matching_char, blink_idx)) = &blink_char { - if node.src_range.contains(blink_idx) { - style_sequence.push(&self.style.bracket_match_style); + if self.style.bracket_match_enabled { + if let Some((_matching_char, blink_idx)) = &blink_char { + if node.src_range.contains(blink_idx) { + style_sequence.push(&self.style.bracket_match_style); + } } } @@ -233,13 +236,14 @@ impl ReplHelper { impl StyleSettings { pub fn new(metta_shim: &mut MettaShim) -> Self { Self { - bracket_styles: metta_shim.get_config_expr_vec("ReplBracketStyles").unwrap_or(vec!["94".to_string(), "93".to_string(), "95".to_string(), "96".to_string()]), - comment_style: metta_shim.get_config_string("ReplCommentStyle").unwrap_or("32".to_string()), - variable_style: metta_shim.get_config_string("ReplVariableStyle").unwrap_or("33".to_string()), - symbol_style: metta_shim.get_config_string("ReplSymbolStyle").unwrap_or("34".to_string()), - string_style: metta_shim.get_config_string("ReplStringStyle").unwrap_or("31".to_string()), - error_style: metta_shim.get_config_string("ReplErrorStyle").unwrap_or("91".to_string()), - bracket_match_style: metta_shim.get_config_string("ReplBracketMatchStyle").unwrap_or("1;7".to_string()), + bracket_styles: metta_shim.get_config_expr_vec(CFG_BRACKET_STYLES).unwrap_or(vec!["94".to_string(), "93".to_string(), "95".to_string(), "96".to_string()]), + comment_style: metta_shim.get_config_string(CFG_COMMENT_STYLE).unwrap_or("32".to_string()), + variable_style: metta_shim.get_config_string(CFG_VARIABLE_STYLE).unwrap_or("33".to_string()), + symbol_style: metta_shim.get_config_string(CFG_SYMBOL_STYLE).unwrap_or("34".to_string()), + string_style: metta_shim.get_config_string(CFG_STRING_STYLE).unwrap_or("31".to_string()), + error_style: metta_shim.get_config_string(CFG_ERROR_STYLE).unwrap_or("91".to_string()), + bracket_match_style: metta_shim.get_config_string(CFG_BRACKET_MATCH_STYLE).unwrap_or("1;7".to_string()), + bracket_match_enabled: metta_shim.get_config_atom(CFG_BRACKET_MATCH_ENABLED).map(|_bool_atom| true).unwrap_or(true), //TODO, make this work when we can bridge value atoms } } } diff --git a/repl/src/main.rs b/repl/src/main.rs index d8ceeb0ed..bcb0901cf 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -109,7 +109,10 @@ fn main() -> Result<()> { // To debug rustyline: // RUST_LOG=rustyline=debug cargo run --example example 2> debug.log -fn start_interactive_mode(repl_params: Shared, metta: MettaShim) -> rustyline::Result<()> { +fn start_interactive_mode(repl_params: Shared, mut metta: MettaShim) -> rustyline::Result<()> { + + //Run the repl init file + metta.load_metta_module(repl_params.borrow().repl_config_metta_path.clone()); //Init RustyLine let config = Config::builder() @@ -141,12 +144,12 @@ fn start_interactive_mode(repl_params: Shared, metta: MettaShim) -> //The Interpreter Loop loop { - //Set the prompt based on resolving a MeTTa variable + //Set the prompt based on the MeTTa pragma settings let prompt = { let helper = rl.helper_mut().unwrap(); let mut metta = helper.metta.borrow_mut(); - let prompt = metta.get_config_string("ReplDefaultPrompt").unwrap_or("> ".to_string()); - let styled_prompt = metta.get_config_string("ReplStyledPrompt").unwrap_or(format!("\x1b[1;32m{prompt}\x1b[0m")); + let prompt = metta.get_config_string(CFG_DEFAULT_PROMPT).unwrap_or_else(|| "> ".to_string()); + let styled_prompt = metta.get_config_string(CFG_STYLED_PROMPT).unwrap_or_else(|| format!("\x1b[1;32m{prompt}\x1b[0m")); helper.colored_prompt = styled_prompt; prompt }; diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 7efb6ef8b..6dda48152 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -3,10 +3,8 @@ use std::path::PathBuf; use hyperon::ExpressionAtom; use hyperon::Atom; -use hyperon::atom::VariableAtom; use hyperon::space::*; use hyperon::space::grounding::GroundingSpace; -use hyperon::metta::*; use hyperon::metta::runner::Metta; #[cfg(not(feature = "minimal"))] use hyperon::metta::runner::stdlib::register_rust_tokens; @@ -97,10 +95,9 @@ impl MettaShim { #[cfg(not(feature = "python"))] new_shim.metta.tokenizer().borrow_mut().register_token_with_regex_str("extend-py!", move |_| { Atom::gnd(py_mod_err::ImportPyErr) }); - //Run the config.metta file + //Run the init.metta file let repl_params = repl_params.borrow(); - let config_metta_path = repl_params.config_metta_path(); - new_shim.load_metta_module(config_metta_path.clone()); + new_shim.load_metta_module(repl_params.init_metta_path.clone()); new_shim } @@ -146,13 +143,10 @@ impl MettaShim { } pub fn get_config_atom(&mut self, config_name: &str) -> Option { + #[allow(unused_assignments)] let mut result = None; metta_shim_env!{{ - let val = VariableAtom::new("val"); - let bindings_set = self.metta.space().query(&Atom::expr(vec![EQUAL_SYMBOL, Atom::sym(config_name.to_string()), Atom::Variable(val.clone())])); - if let Some(bindings) = bindings_set.into_iter().next() { - result = bindings.resolve(&val); - } + result = self.metta.get_setting(config_name); }} result } diff --git a/repl/src/repl.default.metta b/repl/src/repl.default.metta new file mode 100644 index 000000000..7c8822423 --- /dev/null +++ b/repl/src/repl.default.metta @@ -0,0 +1,14 @@ + +!(pragma! ReplDefaultPrompt "> ") +; !(pragma! ReplStyledPrompt "\x1b[1;32m> \x1b[0m") ; TODO: currently the MeTTa string parser doesn't resolve escape chars, although perhaps it should + +; TODO: somebody with better design sense should tweak these, and also provide dark-mode setings +; ANSI escape codes to configure the syntax highlighter +!(pragma! ReplBracketStyles ("36" "35" "33" "32")) ; 36: cyan, 35: magenta, 33: yellow, 32: green +!(pragma! ReplCommentStyle "90") ; 90: light gray +!(pragma! ReplVariableStyle "33") ; 33: yellow +!(pragma! ReplSymbolStyle "0") ; 0: default text (as per terminal setting) +!(pragma! ReplStringStyle "31") ; 31: dark red +!(pragma! ReplErrorStyle "91") ; 91: bright red +!(pragma! ReplBracketMatchStyle "1;7") ; 1: bold, 7: reverse (swap background and foreground colors) +; !(pragma! ReplBracketMatchEnabled True) ;TODO: enable this when I have a reliable value interchange path built. Another use for https://github.com/trueagi-io/hyperon-experimental/issues/351 From 027fdc2e370d2aec72264ee3d1c368dc107b573f Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 14 Sep 2023 15:40:43 +0900 Subject: [PATCH 3/7] Changing history behavior to save duplicate lines Adding currently-inactive parameter to configure max length of history --- repl/src/config_params.rs | 1 + repl/src/main.rs | 3 +++ repl/src/metta_shim.rs | 3 +++ repl/src/repl.default.metta | 2 ++ 4 files changed, 9 insertions(+) diff --git a/repl/src/config_params.rs b/repl/src/config_params.rs index 148ad5121..dec81ed1c 100644 --- a/repl/src/config_params.rs +++ b/repl/src/config_params.rs @@ -16,6 +16,7 @@ pub const CFG_STRING_STYLE: &str = "ReplStringStyle"; pub const CFG_ERROR_STYLE: &str = "ReplErrorStyle"; pub const CFG_BRACKET_MATCH_STYLE: &str = "ReplBracketMatchStyle"; pub const CFG_BRACKET_MATCH_ENABLED: &str = "ReplBracketMatchEnabled"; +pub const CFG_HISTORY_MAX_LEN: &str = "ReplHistoryMaxLen"; #[derive(Default, Debug)] pub struct ReplParams { diff --git a/repl/src/main.rs b/repl/src/main.rs index bcb0901cf..003778a68 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -113,10 +113,13 @@ fn start_interactive_mode(repl_params: Shared, mut metta: MettaShim) //Run the repl init file metta.load_metta_module(repl_params.borrow().repl_config_metta_path.clone()); + let max_len = metta.get_config_int(CFG_HISTORY_MAX_LEN).unwrap_or_else(|| 500); //Init RustyLine let config = Config::builder() .history_ignore_space(true) + .max_history_size(max_len as usize).unwrap() + .history_ignore_dups(false).unwrap() .completion_type(CompletionType::List) .edit_mode(EditMode::Emacs) .build(); diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 6dda48152..1d9cc1a34 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -194,6 +194,9 @@ impl MettaShim { result } + pub fn get_config_int(&mut self, _config_name: &str) -> Option { + None //TODO. Make this work when I have reliable value atom bridging + } } #[cfg(not(feature = "python"))] diff --git a/repl/src/repl.default.metta b/repl/src/repl.default.metta index 7c8822423..a5f23640d 100644 --- a/repl/src/repl.default.metta +++ b/repl/src/repl.default.metta @@ -1,4 +1,6 @@ +; !(pragma! ReplHistoryMaxLen 500) ; TODO: enable this when I have value bridging implemented. + !(pragma! ReplDefaultPrompt "> ") ; !(pragma! ReplStyledPrompt "\x1b[1;32m> \x1b[0m") ; TODO: currently the MeTTa string parser doesn't resolve escape chars, although perhaps it should From 7ee990b2f6fa6589bf1f15a5aa2ebe540265f948 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 14 Sep 2023 22:32:45 +0900 Subject: [PATCH 4/7] Improving presentation of error message when a hyperonpy version mismatch is encountered --- repl/src/metta_shim.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 1d9cc1a34..b85ef9761 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -72,12 +72,17 @@ impl MettaShim { //Init HyperonPy if the repl includes Python support #[cfg(feature = "python")] - { + if let Err(err) = || -> Result<(), String> { //Confirm the hyperonpy version is compatible - py_mod_loading::confirm_hyperonpy_version(">=0.1.0, <0.2.0").unwrap(); + py_mod_loading::confirm_hyperonpy_version(">=0.1.0, <0.2.0")?; //Load the hyperonpy Python stdlib - py_mod_loading::load_python_module(&new_shim.metta, "hyperon.stdlib").unwrap(); + py_mod_loading::load_python_module(&new_shim.metta, "hyperon.stdlib")?; + + Ok(()) + }() { + eprintln!("Fatal Error: {err}"); + std::process::exit(-1); } //Load the Rust stdlib @@ -262,7 +267,10 @@ mod py_mod_loading { let req = VersionReq::parse(req_str).unwrap(); let version_string = get_hyperonpy_version()?; - let version = Version::parse(&version_string).map_err(|e| format!("Error parsing HyperonPy version: '{version_string}', {e}"))?; + //NOTE: Version parsing errors will be encountered by users building hyperonpy from source with an abnormal configuration + // Therefore references to the "hyperon source directory" are ok. Users who get hyperonpy from PyPi won't have hit issue + let version = Version::parse(&version_string) + .map_err(|_e| format!("Invalid HyperonPy version found: '{version_string}'.\nPlease update pip by running `python -m pip install -eU ./python[dev]` from your hyperon source directory."))?; if req.matches(&version) { Ok(()) } else { From 496db533f2f3fc16e65e0d124e7cdfcdf3c5edde Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 14 Sep 2023 22:37:43 +0900 Subject: [PATCH 5/7] Slight rewording to be more clear --- repl/src/metta_shim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index b85ef9761..681cefff2 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -268,9 +268,9 @@ mod py_mod_loading { let req = VersionReq::parse(req_str).unwrap(); let version_string = get_hyperonpy_version()?; //NOTE: Version parsing errors will be encountered by users building hyperonpy from source with an abnormal configuration - // Therefore references to the "hyperon source directory" are ok. Users who get hyperonpy from PyPi won't have hit issue + // Therefore references to the "hyperon source directory" are ok. Users who get hyperonpy from PyPi won't hit this issue let version = Version::parse(&version_string) - .map_err(|_e| format!("Invalid HyperonPy version found: '{version_string}'.\nPlease update pip by running `python -m pip install -eU ./python[dev]` from your hyperon source directory."))?; + .map_err(|_e| format!("Invalid HyperonPy version found: '{version_string}'.\nPlease update the package by running `python -m pip install -eU ./python[dev]` from your hyperon source directory."))?; if req.matches(&version) { Ok(()) } else { From 157ebea16832444ee76a92ac0df9163fb27916d6 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Mon, 18 Sep 2023 11:46:25 +0900 Subject: [PATCH 6/7] Changing HyperonPy version checking from semver to pep-440 version parsing logic --- repl/Cargo.toml | 4 ++-- repl/src/metta_shim.rs | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/repl/Cargo.toml b/repl/Cargo.toml index b787a6811..157439a6c 100644 --- a/repl/Cargo.toml +++ b/repl/Cargo.toml @@ -15,7 +15,7 @@ clap = { version = "4.4.0", features = ["derive"] } directories = "5.0.1" signal-hook = "0.3.17" pyo3 = { version = "0.19.2", features = ["auto-initialize"], optional = true } -semver = { version = "1.0.18", optional = true } +pep440_rs = { version = "0.3.11", optional = true } [[bin]] name = "metta" @@ -23,5 +23,5 @@ path = "src/main.rs" [features] # default = ["python", "minimal"] -python = ["pyo3", "semver"] +python = ["pyo3", "pep440_rs"] minimal = ["hyperon/minimal"] diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 681cefff2..37fef7214 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -239,8 +239,9 @@ mod py_mod_err { #[cfg(feature = "python")] mod py_mod_loading { use std::fmt::Display; + use std::str::FromStr; use std::path::PathBuf; - use semver::{Version, VersionReq}; + use pep440_rs::{parse_version_specifiers, Version}; use pyo3::prelude::*; use pyo3::types::{PyTuple, PyDict}; use hyperon::*; @@ -265,16 +266,16 @@ mod py_mod_loading { pub fn confirm_hyperonpy_version(req_str: &str) -> Result<(), String> { - let req = VersionReq::parse(req_str).unwrap(); + let req = parse_version_specifiers(req_str).unwrap(); let version_string = get_hyperonpy_version()?; //NOTE: Version parsing errors will be encountered by users building hyperonpy from source with an abnormal configuration // Therefore references to the "hyperon source directory" are ok. Users who get hyperonpy from PyPi won't hit this issue - let version = Version::parse(&version_string) - .map_err(|_e| format!("Invalid HyperonPy version found: '{version_string}'.\nPlease update the package by running `python -m pip install -eU ./python[dev]` from your hyperon source directory."))?; - if req.matches(&version) { + let version = Version::from_str(&version_string) + .map_err(|_e| format!("Invalid HyperonPy version found: '{version_string}'.\nPlease update the package by running `python -m pip install -e ./python[dev]` from your hyperon source directory."))?; + if req.iter().all(|specifier| specifier.contains(&version)) { Ok(()) } else { - Err(format!("MeTTa repl requires HyperonPy version matching '{req}'. Found version: '{version}'")) + Err(format!("MeTTa repl requires HyperonPy version matching '{req_str}'. Found version: '{version}'")) } } From 530639455469249635c186bda2888e78540eb9d8 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Tue, 19 Sep 2023 12:46:52 +0900 Subject: [PATCH 7/7] Changing repl config from pragma! state atoms registered with tokenizer --- lib/src/metta/runner/mod.rs | 20 ++++++++--------- repl/src/config_params.rs | 39 ++++++++++++++++++++++++---------- repl/src/interactive_helper.rs | 15 +++++++------ repl/src/main.rs | 7 ++++-- repl/src/metta_shim.rs | 15 ++++++++++--- repl/src/repl.default.metta | 22 +++++++++---------- 6 files changed, 74 insertions(+), 44 deletions(-) diff --git a/lib/src/metta/runner/mod.rs b/lib/src/metta/runner/mod.rs index 5d6f9654e..73f12db54 100644 --- a/lib/src/metta/runner/mod.rs +++ b/lib/src/metta/runner/mod.rs @@ -29,6 +29,15 @@ mod arithmetics; const EXEC_SYMBOL : Atom = sym!("!"); +pub fn atom_is_error(atom: &Atom) -> bool { + match atom { + Atom::Expression(expr) => { + expr.children().len() > 0 && expr.children()[0] == ERROR_SYMBOL + }, + _ => false, + } +} + #[derive(Clone, Debug, PartialEq)] pub struct Metta(Rc); @@ -185,16 +194,7 @@ impl Metta { match interpreter_state.into_result() { Err(msg) => return Err(msg), Ok(result) => { - fn is_error(atom: &Atom) -> bool { - match atom { - Atom::Expression(expr) => { - expr.children().len() > 0 && expr.children()[0] == ERROR_SYMBOL - }, - _ => false, - } - } - - let error = result.iter().any(|atom| is_error(atom)); + let error = result.iter().any(|atom| atom_is_error(atom)); state.results.push(result); if error { state.mode = MettaRunnerMode::TERMINATE; diff --git a/repl/src/config_params.rs b/repl/src/config_params.rs index dec81ed1c..ae4414607 100644 --- a/repl/src/config_params.rs +++ b/repl/src/config_params.rs @@ -6,17 +6,17 @@ use std::fs; const DEFAULT_INIT_METTA: &[u8] = include_bytes!("init.default.metta"); const DEFAULT_REPL_METTA: &[u8] = include_bytes!("repl.default.metta"); -pub const CFG_DEFAULT_PROMPT: &str = "ReplDefaultPrompt"; -pub const CFG_STYLED_PROMPT: &str = "ReplStyledPrompt"; -pub const CFG_BRACKET_STYLES: &str = "ReplBracketStyles"; -pub const CFG_COMMENT_STYLE: &str = "ReplCommentStyle"; -pub const CFG_VARIABLE_STYLE: &str = "ReplVariableStyle"; -pub const CFG_SYMBOL_STYLE: &str = "ReplSymbolStyle"; -pub const CFG_STRING_STYLE: &str = "ReplStringStyle"; -pub const CFG_ERROR_STYLE: &str = "ReplErrorStyle"; -pub const CFG_BRACKET_MATCH_STYLE: &str = "ReplBracketMatchStyle"; -pub const CFG_BRACKET_MATCH_ENABLED: &str = "ReplBracketMatchEnabled"; -pub const CFG_HISTORY_MAX_LEN: &str = "ReplHistoryMaxLen"; +pub const CFG_PROMPT: &str = "&ReplPrompt"; +pub const CFG_STYLED_PROMPT: &str = "&ReplStyledPrompt"; +pub const CFG_BRACKET_STYLES: &str = "&ReplBracketStyles"; +pub const CFG_COMMENT_STYLE: &str = "&ReplCommentStyle"; +pub const CFG_VARIABLE_STYLE: &str = "&ReplVariableStyle"; +pub const CFG_SYMBOL_STYLE: &str = "&ReplSymbolStyle"; +pub const CFG_STRING_STYLE: &str = "&ReplStringStyle"; +pub const CFG_ERROR_STYLE: &str = "&ReplErrorStyle"; +pub const CFG_BRACKET_MATCH_STYLE: &str = "&ReplBracketMatchStyle"; +pub const CFG_BRACKET_MATCH_ENABLED: &str = "&ReplBracketMatchEnabled"; +pub const CFG_HISTORY_MAX_LEN: &str = "&ReplHistoryMaxLen"; #[derive(Default, Debug)] pub struct ReplParams { @@ -104,3 +104,20 @@ impl ReplParams { self.include_paths.iter().cloned()) } } + +/// Returns the MeTTa code to init the Repl's MeTTa params and set them to default values +pub fn builtin_init_metta_code() -> String { + format!(r#" + ; !(bind! {CFG_HISTORY_MAX_LEN} (new-state 500)) ; TODO, enable this when value-bridging is implemented + !(bind! {CFG_PROMPT} (new-state "> ")) + ; !(bind! {CFG_STYLED_PROMPT} (new-state (concat "\x1b[1;32m" (get-state {CFG_PROMPT}) "\x1b[0m"))) ;TODO, two things before this works. Escape parsing in string literals, and string stdlib type with concat operation + !(bind! {CFG_BRACKET_STYLES} (new-state ("94" "93" "95" "96"))) + !(bind! {CFG_COMMENT_STYLE} (new-state "32")) + !(bind! {CFG_VARIABLE_STYLE} (new-state "33")) + !(bind! {CFG_SYMBOL_STYLE} (new-state "34")) + !(bind! {CFG_STRING_STYLE} (new-state "31")) + !(bind! {CFG_ERROR_STYLE} (new-state "91")) + !(bind! {CFG_BRACKET_MATCH_STYLE} (new-state "1;7")) + ; !(bind! {CFG_BRACKET_MATCH_ENABLED} (new-state True)) ; TODO, enable this when value-bridging is implemented + "#) +} diff --git a/repl/src/interactive_helper.rs b/repl/src/interactive_helper.rs index 7e588443d..af784ecff 100644 --- a/repl/src/interactive_helper.rs +++ b/repl/src/interactive_helper.rs @@ -234,15 +234,16 @@ impl ReplHelper { } impl StyleSettings { + const ERR_STR: &str = "Fatal Error: Invalid REPL config"; pub fn new(metta_shim: &mut MettaShim) -> Self { Self { - bracket_styles: metta_shim.get_config_expr_vec(CFG_BRACKET_STYLES).unwrap_or(vec!["94".to_string(), "93".to_string(), "95".to_string(), "96".to_string()]), - comment_style: metta_shim.get_config_string(CFG_COMMENT_STYLE).unwrap_or("32".to_string()), - variable_style: metta_shim.get_config_string(CFG_VARIABLE_STYLE).unwrap_or("33".to_string()), - symbol_style: metta_shim.get_config_string(CFG_SYMBOL_STYLE).unwrap_or("34".to_string()), - string_style: metta_shim.get_config_string(CFG_STRING_STYLE).unwrap_or("31".to_string()), - error_style: metta_shim.get_config_string(CFG_ERROR_STYLE).unwrap_or("91".to_string()), - bracket_match_style: metta_shim.get_config_string(CFG_BRACKET_MATCH_STYLE).unwrap_or("1;7".to_string()), + bracket_styles: metta_shim.get_config_expr_vec(CFG_BRACKET_STYLES).expect(Self::ERR_STR), + comment_style: metta_shim.get_config_string(CFG_COMMENT_STYLE).expect(Self::ERR_STR), + variable_style: metta_shim.get_config_string(CFG_VARIABLE_STYLE).expect(Self::ERR_STR), + symbol_style: metta_shim.get_config_string(CFG_SYMBOL_STYLE).expect(Self::ERR_STR), + string_style: metta_shim.get_config_string(CFG_STRING_STYLE).expect(Self::ERR_STR), + error_style: metta_shim.get_config_string(CFG_ERROR_STYLE).expect(Self::ERR_STR), + bracket_match_style: metta_shim.get_config_string(CFG_BRACKET_MATCH_STYLE).expect(Self::ERR_STR), bracket_match_enabled: metta_shim.get_config_atom(CFG_BRACKET_MATCH_ENABLED).map(|_bool_atom| true).unwrap_or(true), //TODO, make this work when we can bridge value atoms } } diff --git a/repl/src/main.rs b/repl/src/main.rs index 003778a68..1c52c1faa 100644 --- a/repl/src/main.rs +++ b/repl/src/main.rs @@ -111,6 +111,9 @@ fn main() -> Result<()> { // RUST_LOG=rustyline=debug cargo run --example example 2> debug.log fn start_interactive_mode(repl_params: Shared, mut metta: MettaShim) -> rustyline::Result<()> { + //Run the built-in repl-init code + metta.exec(&builtin_init_metta_code()); + //Run the repl init file metta.load_metta_module(repl_params.borrow().repl_config_metta_path.clone()); let max_len = metta.get_config_int(CFG_HISTORY_MAX_LEN).unwrap_or_else(|| 500); @@ -151,8 +154,8 @@ fn start_interactive_mode(repl_params: Shared, mut metta: MettaShim) let prompt = { let helper = rl.helper_mut().unwrap(); let mut metta = helper.metta.borrow_mut(); - let prompt = metta.get_config_string(CFG_DEFAULT_PROMPT).unwrap_or_else(|| "> ".to_string()); - let styled_prompt = metta.get_config_string(CFG_STYLED_PROMPT).unwrap_or_else(|| format!("\x1b[1;32m{prompt}\x1b[0m")); + let prompt = metta.get_config_string(CFG_PROMPT).expect("Fatal Error: Invalid REPL config"); + let styled_prompt = metta.get_config_string(CFG_STYLED_PROMPT).unwrap_or_else(|| format!("\x1b[1;32m{prompt}\x1b[0m")); //TODO, Let this default be set inside builtin_init_metta_code() when strings can be parsed with escape chars helper.colored_prompt = styled_prompt; prompt }; diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 37fef7214..d14c89c0f 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -5,7 +5,7 @@ use hyperon::ExpressionAtom; use hyperon::Atom; use hyperon::space::*; use hyperon::space::grounding::GroundingSpace; -use hyperon::metta::runner::Metta; +use hyperon::metta::runner::{Metta, atom_is_error}; #[cfg(not(feature = "minimal"))] use hyperon::metta::runner::stdlib::register_rust_tokens; #[cfg(feature = "minimal")] @@ -148,10 +148,15 @@ impl MettaShim { } pub fn get_config_atom(&mut self, config_name: &str) -> Option { + self.exec(&format!("!(get-state {config_name})")); + #[allow(unused_assignments)] let mut result = None; metta_shim_env!{{ - result = self.metta.get_setting(config_name); + result = self.result.get(0) + .and_then(|vec| vec.get(0)) + .and_then(|atom| (!atom_is_error(atom)).then_some(atom)) + .cloned() }} result } @@ -162,6 +167,7 @@ impl MettaShim { #[allow(unused_assignments)] let mut result = None; metta_shim_env!{{ + //TODO: We need to do atom type checking here result = Some(Self::strip_quotes(atom.to_string())); }} result @@ -192,7 +198,10 @@ impl MettaShim { if let Ok(expr) = ExpressionAtom::try_from(atom) { result = Some(expr.into_children() .into_iter() - .map(|atom| Self::strip_quotes(atom.to_string())) + .map(|atom| { + //TODO: We need to do atom type checking here + Self::strip_quotes(atom.to_string()) + }) .collect()) } }} diff --git a/repl/src/repl.default.metta b/repl/src/repl.default.metta index a5f23640d..7bf6499c8 100644 --- a/repl/src/repl.default.metta +++ b/repl/src/repl.default.metta @@ -1,16 +1,16 @@ -; !(pragma! ReplHistoryMaxLen 500) ; TODO: enable this when I have value bridging implemented. +; !(change-state! &ReplHistoryMaxLen 500) ; TODO: enable this when I have value bridging implemented. -!(pragma! ReplDefaultPrompt "> ") -; !(pragma! ReplStyledPrompt "\x1b[1;32m> \x1b[0m") ; TODO: currently the MeTTa string parser doesn't resolve escape chars, although perhaps it should +!(change-state! &ReplPrompt "> ") +; !(change-state! &ReplStyledPrompt "\x1b[1;32m> \x1b[0m") ; TODO: currently the MeTTa string parser doesn't resolve escape chars, although perhaps it should ; TODO: somebody with better design sense should tweak these, and also provide dark-mode setings ; ANSI escape codes to configure the syntax highlighter -!(pragma! ReplBracketStyles ("36" "35" "33" "32")) ; 36: cyan, 35: magenta, 33: yellow, 32: green -!(pragma! ReplCommentStyle "90") ; 90: light gray -!(pragma! ReplVariableStyle "33") ; 33: yellow -!(pragma! ReplSymbolStyle "0") ; 0: default text (as per terminal setting) -!(pragma! ReplStringStyle "31") ; 31: dark red -!(pragma! ReplErrorStyle "91") ; 91: bright red -!(pragma! ReplBracketMatchStyle "1;7") ; 1: bold, 7: reverse (swap background and foreground colors) -; !(pragma! ReplBracketMatchEnabled True) ;TODO: enable this when I have a reliable value interchange path built. Another use for https://github.com/trueagi-io/hyperon-experimental/issues/351 +!(change-state! &ReplBracketStyles ("36" "35" "33" "32")) ; 36: cyan, 35: magenta, 33: yellow, 32: green +!(change-state! &ReplCommentStyle "90") ; 90: light gray +!(change-state! &ReplVariableStyle "33") ; 33: yellow +!(change-state! &ReplSymbolStyle "0") ; 0: default text (as per terminal setting) +!(change-state! &ReplStringStyle "31") ; 31: dark red +!(change-state! &ReplErrorStyle "91") ; 91: bright red +!(change-state! &ReplBracketMatchStyle "1;7") ; 1: bold, 7: reverse (swap background and foreground colors) +; !(change-state! &ReplBracketMatchEnabled True) ;TODO: enable this when I have a reliable value interchange path built. Another use for https://github.com/trueagi-io/hyperon-experimental/issues/351