Skip to content

Commit

Permalink
random-float and random-int added
Browse files Browse the repository at this point in the history
  • Loading branch information
Innokenty committed Oct 31, 2024
1 parent 6547067 commit 897a89a
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 0 deletions.
14 changes: 14 additions & 0 deletions lib/src/metta/runner/stdlib.metta
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,20 @@
(@param "Index")))
(@return "Atom from an expression in the place defined by index. Error if index is out of bounds of an expression"))

(@doc random-int
(@desc "Returns random int number from range defined by two numbers (first and second argument)")
(@params (
(@param "Range start")
(@param "Range end")))
(@return "Random int number from defined range"))

(@doc random-float
(@desc "Returns random float number from range defined by two numbers (first and second argument)")
(@params (
(@param "Range start")
(@param "Range end")))
(@return "Random float number from defined range"))

(@doc println!
(@desc "Prints a line of text to the console")
(@params (
Expand Down
71 changes: 71 additions & 0 deletions lib/src/metta/runner/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use std::cell::RefCell;
use std::fmt::Display;
use std::collections::HashMap;
use regex::Regex;
use rand::Rng;

use super::arithmetics::*;
use super::string::*;
Expand Down Expand Up @@ -1253,6 +1254,64 @@ impl CustomExecute for SubtractionAtomOp {
}
}

#[derive(Clone, Debug)]
pub struct RandomIntOp {}

grounded_op!(RandomIntOp, "random-int");

impl Grounded for RandomIntOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
Some(self)
}
}

impl CustomExecute for RandomIntOp {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("random-int expects two arguments: number (start) and number (end)");
let start = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?;
let end = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?;
let range = Into::<i64>::into(start)..Into::<i64>::into(end);
if range.is_empty() {
return Err(ExecError::from("Wrong range"));
}
let mut rng = rand::thread_rng();
Ok(vec![Atom::value(rng.gen_range::<i64,_>(range))])
}
}

#[derive(Clone, Debug)]
pub struct RandomFloatOp {}

grounded_op!(RandomFloatOp, "random-float");

impl Grounded for RandomFloatOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
Some(self)
}
}

impl CustomExecute for RandomFloatOp {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("random-float expects two arguments: number (start) and number (end)");
let start = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?;
let end = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?;
let range = Into::<f64>::into(start)..Into::<f64>::into(end);
if range.is_empty() {
return Err(ExecError::from("Wrong range"));
}
let mut rng = rand::thread_rng();
Ok(vec![Atom::value(rng.gen_range::<f64,_>(range))])
}
}

/// The internal `non_minimal_only_stdlib` module contains code that is never used by the minimal stdlib
#[cfg(feature = "old_interpreter")]
mod non_minimal_only_stdlib {
Expand Down Expand Up @@ -1766,6 +1825,10 @@ mod non_minimal_only_stdlib {
tref.register_token(regex(r"cons-atom"), move |_| { cons_atom_op.clone() });
let index_atom_op = Atom::gnd(IndexAtomOp{});
tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() });
let random_int_op = Atom::gnd(RandomIntOp{});
tref.register_token(regex(r"random-int"), move |_| { random_int_op.clone() });
let random_float_op = Atom::gnd(RandomFloatOp{});
tref.register_token(regex(r"random-float"), move |_| { random_float_op.clone() });
let println_op = Atom::gnd(PrintlnOp{});
tref.register_token(regex(r"println!"), move |_| { println_op.clone() });
let format_args_op = Atom::gnd(FormatArgsOp{});
Expand Down Expand Up @@ -2061,6 +2124,14 @@ mod tests {
assert_eq!(res, Err(ExecError::from("Index is out of bounds")));
}

#[test]
fn random_op() {
let res = RandomIntOp{}.execute(&mut vec![expr!({Number::Integer(2)}), expr!({Number::Integer(-2)})]);
assert_eq!(res, Err(ExecError::from("Wrong range")));
let res = RandomFloatOp{}.execute(&mut vec![expr!({Number::Integer(0)}), expr!({Number::Integer(0)})]);
assert_eq!(res, Err(ExecError::from("Wrong range")));
}

#[test]
fn bind_new_space_op() {
let tokenizer = Shared::new(Tokenizer::new());
Expand Down
14 changes: 14 additions & 0 deletions lib/src/metta/runner/stdlib_minimal.metta
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@
(@param "Index")))
(@return "Atom from an expression in the place defined by index. Error if index is out of bounds"))

(@doc random-int
(@desc "Returns random int number from range defined by two numbers (first and second argument)")
(@params (
(@param "Range start")
(@param "Range end")))
(@return "Random int number from defined range"))

(@doc random-float
(@desc "Returns random float number from range defined by two numbers (first and second argument)")
(@params (
(@param "Range start")
(@param "Range end")))
(@return "Random float number from defined range"))

(@doc collapse-bind
(@desc "Evaluates minimal MeTTa operation (first argument) and returns an expression which contains all alternative evaluations in a form (Atom Bindings). Bindings are represented in a form of a grounded atom.")
(@params (
Expand Down
10 changes: 10 additions & 0 deletions lib/src/metta/runner/stdlib_minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared<Tokenizer
tref.register_token(regex(r"match"), move |_| { match_op.clone() });
let index_atom_op = Atom::gnd(stdlib::IndexAtomOp{});
tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() });
let random_int_op = Atom::gnd(stdlib::RandomIntOp{});
tref.register_token(regex(r"random-int"), move |_| { random_int_op.clone() });
let random_float_op = Atom::gnd(stdlib::RandomFloatOp{});
tref.register_token(regex(r"random-float"), move |_| { random_float_op.clone() });
let mod_space_op = Atom::gnd(stdlib::ModSpaceOp::new(metta.clone()));
tref.register_token(regex(r"mod-space!"), move |_| { mod_space_op.clone() });
let print_mods_op = Atom::gnd(stdlib::PrintModsOp::new(metta.clone()));
Expand Down Expand Up @@ -623,6 +627,12 @@ mod tests {
assert_eq!(run_program(&format!("!(index-atom (A B C D E) 5)")), Ok(vec![vec![expr!("Error" ({ stdlib::IndexAtomOp{} } ("A" "B" "C" "D" "E") {Number::Integer(5)}) "Index is out of bounds")]]));
}

#[test]
fn metta_random() {
assert_eq!(run_program(&format!("!(random-int 0 0)")), Ok(vec![vec![expr!("Error" ({ stdlib::RandomIntOp{} } {Number::Integer(0)} {Number::Integer(0)}) "Wrong range")]]));
assert_eq!(run_program(&format!("!(random-float 0 -5)")), Ok(vec![vec![expr!("Error" ({ stdlib::RandomFloatOp{} } {Number::Integer(0)} {Number::Integer(-5)}) "Wrong range")]]));
}

#[test]
fn metta_switch() {
let result = run_program("!(eval (switch (A $b) ( (($a B) ($b $a)) ((B C) (C B)) )))");
Expand Down

0 comments on commit 897a89a

Please sign in to comment.