Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repl config improvements & cntl-J force-submit #433

Merged
merged 10 commits into from
Sep 19, 2023
Merged
41 changes: 22 additions & 19 deletions lib/src/metta/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,23 @@ 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<MettaContents>);

#[derive(Debug, PartialEq)]
pub struct MettaContents {
space: DynSpace,
tokenizer: Shared<Tokenizer>,
settings: Shared<HashMap<String, String>>,
settings: Shared<HashMap<String, Atom>>,
modules: Shared<HashMap<PathBuf, DynSpace>>,
search_paths: Vec<PathBuf>,
}
Expand Down Expand Up @@ -141,19 +150,22 @@ impl Metta {
&self.0.modules
}

pub(crate) fn settings(&self) -> &Shared<HashMap<String, String>> {
pub fn settings(&self) -> &Shared<HashMap<String, Atom>> {
&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<String> {
pub fn get_setting(&self, key: &str) -> Option<Atom> {
self.0.settings.borrow().get(key.into()).cloned()
}

pub fn get_setting_string(&self, key: &str) -> Option<String> {
self.0.settings.borrow().get(key.into()).map(|a| a.to_string())
}

pub fn run(&self, parser: &mut SExprParser) -> Result<Vec<Vec<Atom>>, String> {
let mut state = self.start_run();

Expand Down Expand Up @@ -182,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;
Expand Down Expand Up @@ -261,7 +264,7 @@ impl Metta {
}

fn type_check(&self, atom: Atom) -> Result<Atom, Atom> {
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 {
Expand Down Expand Up @@ -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")]]));
}
Expand All @@ -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")]]));
}
Expand Down Expand Up @@ -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")]]));
}
Expand Down
24 changes: 7 additions & 17 deletions lib/src/metta/runner/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,22 +171,14 @@ 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])
}

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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))?;
Expand Down Expand Up @@ -678,11 +671,11 @@ impl Grounded for SuperposeOp {

#[derive(Clone, PartialEq, Debug)]
pub struct PragmaOp {
settings: Shared<HashMap<String, String>>,
settings: Shared<HashMap<String, Atom>>,
}

impl PragmaOp {
pub fn new(settings: Shared<HashMap<String, String>>) -> Self {
pub fn new(settings: Shared<HashMap<String, Atom>>) -> Self {
Self{ settings }
}
}
Expand All @@ -700,12 +693,9 @@ impl Grounded for PragmaOp {

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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![])
}

Expand Down
12 changes: 4 additions & 8 deletions lib/src/metta/runner/stdlib2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,11 @@ impl Grounded for CollapseOp {

#[derive(Clone, PartialEq, Debug)]
pub struct PragmaOp {
settings: Shared<HashMap<String, String>>,
settings: Shared<HashMap<String, Atom>>,
}

impl PragmaOp {
pub fn new(settings: Shared<HashMap<String, String>>) -> Self {
pub fn new(settings: Shared<HashMap<String, Atom>>) -> Self {
Self{ settings }
}
}
Expand All @@ -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![])
}

Expand Down
4 changes: 2 additions & 2 deletions repl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ 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"
path = "src/main.rs"

[features]
# default = ["python", "minimal"]
python = ["pyo3", "semver"]
python = ["pyo3", "pep440_rs"]
minimal = ["hyperon/minimal"]
16 changes: 0 additions & 16 deletions repl/src/config.default.metta

This file was deleted.

75 changes: 54 additions & 21 deletions repl/src/config_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,34 @@ 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_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 {
/// 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<PathBuf>,
Expand Down Expand Up @@ -44,15 +60,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
Expand All @@ -63,7 +88,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")),
Expand All @@ -77,14 +103,21 @@ 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
}

/// 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
"#)
}
2 changes: 2 additions & 0 deletions repl/src/init.default.metta
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

; TODO: Let the "importPaths" be modifiable, but I want better string manipulation atoms
Loading