Skip to content

Commit

Permalink
Merge pull request #433 from luketpeterson/repl
Browse files Browse the repository at this point in the history
Repl config improvements & cntl-J force-submit
  • Loading branch information
vsbogd authored Sep 19, 2023
2 parents fcba3fb + 5cbffea commit b3db49a
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 133 deletions.
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

0 comments on commit b3db49a

Please sign in to comment.