mirror of
https://github.com/Dummi26/mers.git
synced 2025-12-14 11:16:17 +01:00
.
This commit is contained in:
@@ -13,6 +13,7 @@ mod with_list;
|
||||
mod with_math;
|
||||
mod with_multithreading;
|
||||
mod with_stdio;
|
||||
mod with_string;
|
||||
|
||||
/// Usage: create an empty Config using Config::new(), use the methods to customize it, then get the Infos using Config::infos()
|
||||
/// bundle_* for bundles (combines multiple groups or even bundles)
|
||||
@@ -36,11 +37,13 @@ impl Config {
|
||||
/// `bundle_base()`
|
||||
/// `with_stdio()`
|
||||
/// `with_list()`
|
||||
/// `with_string()`
|
||||
/// `with_command_running()`
|
||||
/// `with_multithreading()`
|
||||
pub fn bundle_std(self) -> Self {
|
||||
self.with_multithreading()
|
||||
.with_command_running()
|
||||
.with_string()
|
||||
.with_list()
|
||||
.with_stdio()
|
||||
.bundle_base()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, Type},
|
||||
program::run::{CheckInfo, Info},
|
||||
data::{self, Data, MersType, Type},
|
||||
program::run::{CheckError, CheckInfo, Info},
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
@@ -11,9 +11,105 @@ impl Config {
|
||||
/// `deref: fn` clones the value from a reference
|
||||
/// `eq: fn` returns true if all the values are equal, otherwise false.
|
||||
/// `loop: fn` runs a function until it returns (T) instead of (), then returns T.
|
||||
/// `try: fn` runs the first valid function with the argument. usage: (arg, (f1, f2, f3)).try
|
||||
/// NOTE: try's return type may miss some types that can actually happen when using it on tuples, so... don't do ((a, b), (f1, any -> ())).try unless f1 also returns ()
|
||||
/// `len: fn` gets the length of strings or tuples
|
||||
/// `panic: fn` exits the program with the given exit code
|
||||
pub fn with_base(self) -> Self {
|
||||
self.add_var(
|
||||
self.add_var("try".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
let mut out = Type::empty();
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
if t.0.len() != 2 {
|
||||
return Err(CheckError(format!("cannot use try with tuple argument where len != 2 (got len {})", t.0.len())));
|
||||
}
|
||||
let arg_type = &t.0[0];
|
||||
let functions = &t.0[1];
|
||||
for arg_type in arg_type.types.iter() {
|
||||
let arg_type = Type::newm(vec![arg_type.clone()]);
|
||||
// possibilities for the tuple (f1, f2, f3, ..., fn)
|
||||
for ft in functions.types.iter() {
|
||||
let mut tuple_fallible = true;
|
||||
let mut tuple_possible = false;
|
||||
if let Some(ft) = ft.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
// f1, f2, f3, ..., fn
|
||||
let mut func_errors = vec![];
|
||||
for ft in ft.0.iter() {
|
||||
let mut func_fallible = false;
|
||||
// possibilities for f_
|
||||
for ft in ft.types.iter() {
|
||||
if let Some(ft) = ft.as_any().downcast_ref::<data::function::FunctionT>() {
|
||||
func_errors.push(match ft.0(&arg_type) {
|
||||
Err(e) => {
|
||||
func_fallible = true;
|
||||
Some(e)
|
||||
}
|
||||
Ok(o) => {
|
||||
tuple_possible = true;
|
||||
for t in o.types {
|
||||
out.add(t);
|
||||
}
|
||||
None
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Err(CheckError(format!("try: arguments f1-fn must be functions")));
|
||||
}
|
||||
}
|
||||
// found a function that won't fail for this arg_type!
|
||||
if !func_fallible {
|
||||
tuple_fallible = false;
|
||||
}
|
||||
}
|
||||
if tuple_fallible || !tuple_possible {
|
||||
return Err(CheckError(format!("try: if the argument is {arg_type}, there is no infallible function. add a fallback function to handle this case! Errors for all functions: {}", func_errors.iter().enumerate().map(|(i, v)| match v {
|
||||
Some(e) => format!("\n{i}: {}", e.0),
|
||||
None => "\n({i}: no error)".to_owned(),
|
||||
}).collect::<String>())));
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("try: argument must be (arg, (f1, f2, f3, ..., fn))")));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("cannot use try with non-tuple argument")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
let tuple = a.get();
|
||||
let tuple = tuple.as_any().downcast_ref::<data::tuple::Tuple>().expect("try: not a tuple");
|
||||
let arg = &tuple.0[0];
|
||||
let funcs = tuple.0[1].get();
|
||||
let funcs = funcs.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
|
||||
for func in funcs.0.iter() {
|
||||
let func = func.get();
|
||||
let func = func.as_any().downcast_ref::<data::function::Function>().unwrap();
|
||||
if func.check(&arg.get().as_type()).is_ok() {
|
||||
return func.run(arg.clone());
|
||||
}
|
||||
}
|
||||
unreachable!("try: no function found")
|
||||
})
|
||||
}))
|
||||
.add_var("panic".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| if a.is_included_in(&data::int::IntT) {
|
||||
Ok(Type::empty())
|
||||
} else {
|
||||
Err(CheckError(format!("cannot call exit with non-int argument")))
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
std::process::exit(a.get().as_any().downcast_ref::<data::int::Int>().map(|i| i.0 as _).unwrap_or(1));
|
||||
})
|
||||
}))
|
||||
.add_var(
|
||||
"len".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
|
||||
@@ -6,7 +6,10 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersData, MersType, Type},
|
||||
program::{self, run::CheckInfo},
|
||||
program::{
|
||||
self,
|
||||
run::{CheckError, CheckInfo},
|
||||
},
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
@@ -16,13 +19,27 @@ impl Config {
|
||||
/// `run_command: fn` runs a command with arguments.
|
||||
/// Args: (cmd, args) where cmd is a string and args is an Iterable over strings
|
||||
/// `RunCommandError` holds the error if the command can't be executed
|
||||
/// returns (int/(), string, string) on success (status code, stdout, stderr)
|
||||
pub fn with_command_running(self) -> Self {
|
||||
self.add_var(
|
||||
"run_command".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
|
||||
out: Arc::new(|a, i| todo!()),
|
||||
out: Arc::new(|a, _i| {
|
||||
if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().is_some_and(|t| t.0.len() == 2 && t.0[0].is_included_in(&data::string::StringT) && t.0[1].iterable().is_some_and(|t| t.is_included_in(&data::string::StringT)))) {
|
||||
Ok(Type::newm(vec![
|
||||
Arc::new(data::tuple::TupleT(vec![
|
||||
Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::tuple::TupleT(vec![]))]),
|
||||
Type::new(data::string::StringT),
|
||||
Type::new(data::string::StringT),
|
||||
])),
|
||||
Arc::new(RunCommandErrorT)
|
||||
]))
|
||||
} else {
|
||||
return Err(CheckError(format!("run_command called with invalid arguments (must be (String, Iter<String>))")));
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(cmd) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
if let (Some(cmd), Some(args)) = (cmd.get(0), cmd.get(1)) {
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Config {
|
||||
/// `iter: fn` executes a function once for each element of the iterable
|
||||
/// `map: fn` maps each value in the iterable to a new one by applying a transformation function
|
||||
/// `filter: fn` filters the iterable by removing all elements where the filter function doesn't return true
|
||||
/// `filter_map: fn` combines filter and map
|
||||
/// `filter_map: fn` combines filter and map. requires that the function returns ()/(t).
|
||||
/// `enumerate: fn` transforms an iterator over T into one over (Int, T), where Int is the index of the element
|
||||
pub fn with_iters(self) -> Self {
|
||||
self.add_var(
|
||||
@@ -49,7 +49,8 @@ impl Config {
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!(
|
||||
"for_each called on tuple not containing iterable and function"
|
||||
"for_each called on tuple not containing iterable and function: {v} is {}",
|
||||
if v.iterable().is_some() { "iterable" } else { "not iterable" },
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,10 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersData, MersType, Type},
|
||||
program::{self, run::CheckInfo},
|
||||
program::{
|
||||
self,
|
||||
run::{CheckError, CheckInfo},
|
||||
},
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
@@ -14,9 +17,118 @@ impl Config {
|
||||
/// Adds a simple list type
|
||||
/// `List` can store a variable number of items
|
||||
/// `as_list: fn` turns a tuple into a list
|
||||
/// `push: fn` adds an element to a list
|
||||
/// `pop: fn` removes the last element from a list. returns (element) or ().
|
||||
/// TODO!
|
||||
/// `get_mut: fn` like get, but returns a reference to the object
|
||||
pub fn with_list(self) -> Self {
|
||||
// TODO: Type with generics
|
||||
self.add_type("List".to_string(), Type::new(ListT(Type::empty_tuple())))
|
||||
.add_var(
|
||||
"pop".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
if let Some(a) = a.dereference() {
|
||||
let mut out = Type::empty();
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<ListT>() {
|
||||
out.add(Arc::new(t.0.clone()));
|
||||
} else {
|
||||
return Err(CheckError(format!(
|
||||
"pop: found a reference to {t}, which is not a list"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
} else {
|
||||
return Err(CheckError(format!("pop: not a reference: {a}")));
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
match a
|
||||
.get()
|
||||
.as_any()
|
||||
.downcast_ref::<data::reference::Reference>()
|
||||
.unwrap()
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_mut()
|
||||
.mut_any()
|
||||
.downcast_mut::<List>()
|
||||
.unwrap()
|
||||
.0
|
||||
.pop()
|
||||
{
|
||||
Some(data) => Data::one_tuple(data),
|
||||
None => Data::empty_tuple(),
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"push".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
if t.0.len() != 2 {
|
||||
return Err(CheckError(format!(
|
||||
"push: tuple must have length 2"
|
||||
)));
|
||||
}
|
||||
let a = &t.0[0];
|
||||
let new = &t.0[1];
|
||||
if let Some(a) = a.dereference() {
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<ListT>() {
|
||||
if !new.is_included_in(&t.0) {
|
||||
return Err(CheckError(format!(
|
||||
"push: found a reference to {t}, which is a list which can't contain elements of type {new}"
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!(
|
||||
"push: found a reference to {t}, which is not a list"
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!(
|
||||
"push: first element in tuple not a reference: {a}"
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("push: not a tuple: {t}")));
|
||||
}
|
||||
}
|
||||
Ok(Type::empty_tuple())
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
let tuple = a.get();
|
||||
let tuple = tuple.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
|
||||
tuple.0[0]
|
||||
.get()
|
||||
.as_any()
|
||||
.downcast_ref::<data::reference::Reference>()
|
||||
.unwrap()
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_mut()
|
||||
.mut_any()
|
||||
.downcast_mut::<List>()
|
||||
.unwrap()
|
||||
.0
|
||||
.push(tuple.0[1].clone());
|
||||
Data::empty_tuple()
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"as_list".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, Type},
|
||||
data::{self, Data, MersType, Type},
|
||||
program::{
|
||||
self,
|
||||
run::{CheckError, CheckInfo},
|
||||
@@ -12,13 +12,135 @@ use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// `sum: fn` returns the sum of all the numbers in the tuple
|
||||
/// `diff: fn` returns b - a
|
||||
/// `product: fn` returns the product of all the numbers in the tuple
|
||||
/// `signum: fn` returns 1 for positive numbers, -1 for negative ones and 0 for 0 (always returns an Int, even when input is Float)
|
||||
/// `parse_int: fn` parses a string to an int, returns () on failure
|
||||
/// `parse_float: fn` parses a string to an int, returns () on failure
|
||||
/// TODO!
|
||||
/// `as_float: fn` turns integers into floats. returns floats without changing them.
|
||||
/// `round: fn` rounds the float and returns an int
|
||||
/// `ceil: fn` rounds the float [?] and returns an int
|
||||
/// `floor: fn` rounds the float [?] and returns an int
|
||||
/// `div: fn` returns a / b. Performs integer division if a and b are both integers.
|
||||
/// `modulo: fn` returns a % b
|
||||
pub fn with_math(self) -> Self {
|
||||
self.add_var(
|
||||
self.add_var("parse_float".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
if a.is_included_in(&Type::new(data::string::StringT)) {
|
||||
Ok(Type::newm(vec![
|
||||
Arc::new(data::float::FloatT),
|
||||
Arc::new(data::tuple::TupleT(vec![])),
|
||||
]))
|
||||
} else {
|
||||
Err(CheckError(format!("parse_float called on non-string type")))
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() {
|
||||
Data::new(data::float::Float(n))
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
}
|
||||
})
|
||||
})).add_var("parse_int".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
if a.is_included_in(&Type::new(data::string::StringT)) {
|
||||
Ok(Type::newm(vec![
|
||||
Arc::new(data::int::IntT),
|
||||
Arc::new(data::tuple::TupleT(vec![])),
|
||||
]))
|
||||
} else {
|
||||
Err(CheckError(format!("parse_float called on non-string type")))
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() {
|
||||
Data::new(data::int::Int(n))
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
}
|
||||
})
|
||||
})).add_var("signum".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
if a.is_included_in(&Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::float::FloatT)])) {
|
||||
Ok(Type::new(data::int::IntT))
|
||||
} else {
|
||||
Err(CheckError(format!("signum called on non-number type")))
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
Data::new(data::int::Int(if let Some(n) = a.get().as_any().downcast_ref::<data::int::Int>() {
|
||||
n.0.signum()
|
||||
} else
|
||||
if let Some(n) = a.get().as_any().downcast_ref::<data::float::Float>() {
|
||||
if n.0 > 0.0 {
|
||||
1
|
||||
} else if n.0 < 0.0 {
|
||||
-1
|
||||
} else { 0
|
||||
}
|
||||
} else { unreachable!("called signum on non-number type")}))
|
||||
})
|
||||
}))
|
||||
.add_var("diff".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
let mut float = false;
|
||||
for t in &a.types {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
if t.0.len() != 2 {
|
||||
return Err(CheckError(format!("Called diff with a tuple where len != 2")));
|
||||
}
|
||||
for (t, side) in [(&t.0[0], "left"), (&t.0[1], "right")] {
|
||||
for t in t.types.iter() {
|
||||
if t.as_any().is::<data::float::FloatT>() {
|
||||
float = true;
|
||||
} else if !t.as_any().is::<data::int::IntT>() {
|
||||
return Err(CheckError(format!("Called diff, but the {side} side of the tuple had type {t}, which isn't Int/Float.")));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("Called diff on a non-tuple")));
|
||||
}
|
||||
}
|
||||
Ok(if a.types.is_empty() {
|
||||
Type::empty()
|
||||
} else if float {
|
||||
Type::new(data::float::FloatT)
|
||||
} else {
|
||||
Type::new(data::int::IntT)
|
||||
})
|
||||
}),
|
||||
run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||
let left = t.0[0].get();
|
||||
let right = t.0[1].get();
|
||||
let (left, right) = (left.as_any(), right.as_any());
|
||||
match (left.downcast_ref::<data::int::Int>(), left.downcast_ref::<data::float::Float>(),
|
||||
right.downcast_ref::<data::int::Int>(), right.downcast_ref::<data::float::Float>()
|
||||
) {
|
||||
(Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => Data::new(data::int::Int(r - l)),
|
||||
(Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => Data::new(data::float::Float(r - *l as f64)),
|
||||
(None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => Data::new(data::float::Float(*r as f64 - l)),
|
||||
(None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => Data::new(data::float::Float(r - l)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else { unreachable!() }),
|
||||
}))
|
||||
.add_var(
|
||||
"sum".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, i| {
|
||||
out: Arc::new(|a, _i| {
|
||||
let mut ints = false;
|
||||
let mut floats = false;
|
||||
for a in &a.types {
|
||||
@@ -74,5 +196,66 @@ impl Config {
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.add_var(
|
||||
"product".to_string(),
|
||||
Data::new(data::function::Function {
|
||||
info: Arc::new(program::run::Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
let mut ints = false;
|
||||
let mut floats = false;
|
||||
for a in &a.types {
|
||||
if let Some(i) = a.iterable() {
|
||||
if i.types
|
||||
.iter()
|
||||
.all(|t| t.as_any().downcast_ref::<data::int::IntT>().is_some())
|
||||
{
|
||||
ints = true;
|
||||
} else if i.types.iter().all(|t| {
|
||||
t.as_any().downcast_ref::<data::int::IntT>().is_some()
|
||||
|| t.as_any().downcast_ref::<data::float::FloatT>().is_some()
|
||||
}) {
|
||||
floats = true;
|
||||
} else {
|
||||
return Err(CheckError(format!("cannot get product of iterator over type {i} because it contains types that aren't int/float")))
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!(
|
||||
"cannot get product of non-iterable type {a}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(match (ints, floats) {
|
||||
(_, true) => Type::new(data::float::FloatT),
|
||||
(true, false) => Type::new(data::int::IntT),
|
||||
(false, false) => Type::empty(),
|
||||
})
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
if let Some(i) = a.get().iterable() {
|
||||
let mut prodi = 1;
|
||||
let mut prodf = 1.0;
|
||||
let mut usef = false;
|
||||
for val in i {
|
||||
if let Some(i) = val.get().as_any().downcast_ref::<data::int::Int>() {
|
||||
prodi *= i.0;
|
||||
} else if let Some(i) =
|
||||
val.get().as_any().downcast_ref::<data::float::Float>()
|
||||
{
|
||||
prodf *= i.0;
|
||||
usef = true;
|
||||
}
|
||||
}
|
||||
if usef {
|
||||
Data::new(data::float::Float(prodi as f64 + prodf))
|
||||
} else {
|
||||
Data::new(data::int::Int(prodi))
|
||||
}
|
||||
} else {
|
||||
unreachable!("product called on non-tuple")
|
||||
}
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::{
|
||||
use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// `get: fn` is used to retrieve elements from collections
|
||||
/// `thread: fn` turns `(func, arg)` into a `Thread`, which will run the function with the argument.
|
||||
/// `thread_get_result: fn` returns `()` while the thread is running and `(result)` otherwise.
|
||||
pub fn with_multithreading(self) -> Self {
|
||||
|
||||
132
mers_lib/src/program/configs/with_string.rs
Normal file
132
mers_lib/src/program/configs/with_string.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{
|
||||
data::{self, Data, MersType, Type},
|
||||
program::run::{CheckError, CheckInfo, Info},
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
|
||||
impl Config {
|
||||
/// `substring: fn` extracts part of a string. usage: (str, start).substring or (str, start, end).substring. start and end may be negative, in which case they become str.len - n: (str, 0, -1) shortens the string by 1.
|
||||
/// `index_of: fn` finds the index of a pattern in a string
|
||||
/// `index_of_rev: fn` finds the last index of a pattern in a string
|
||||
/// `to_string: fn` turns any argument into a (more or less useful) string representation
|
||||
/// `concat: fn` concatenates all arguments given to it. arg must be an enumerable
|
||||
pub fn with_string(self) -> Self {
|
||||
self.add_var("concat".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| if a.iterable().is_some() {
|
||||
Ok(Type::new(data::string::StringT))
|
||||
} else {
|
||||
Err(CheckError(format!("concat called on non-iterable type {a}")))
|
||||
}),
|
||||
run: Arc::new(|a, _i| Data::new(data::string::String(a.get().iterable().unwrap().map(|v| v.get().to_string()).collect()))),
|
||||
})).add_var("to_string".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|_a, _i| Ok(Type::new(data::string::StringT))),
|
||||
run: Arc::new(|a, _i| Data::new(data::string::String(a.get().to_string()))),
|
||||
})).add_var("index_of".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| if a.is_included_in(&data::tuple::TupleT(vec![Type::new(data::string::StringT), Type::new(data::string::StringT)])) {
|
||||
Ok(Type::newm(vec![
|
||||
Arc::new(data::tuple::TupleT(vec![])),
|
||||
Arc::new(data::int::IntT),
|
||||
]))
|
||||
} else {
|
||||
Err(CheckError(format!("wrong args for index_of: must be (string, string)")))
|
||||
}),
|
||||
run: Arc::new(|a, _i| index_of(a, false)),
|
||||
})).add_var("index_of_rev".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| if a.is_included_in(&data::tuple::TupleT(vec![Type::new(data::string::StringT), Type::new(data::string::StringT)])) {
|
||||
Ok(Type::newm(vec![
|
||||
Arc::new(data::tuple::TupleT(vec![])),
|
||||
Arc::new(data::int::IntT),
|
||||
]))
|
||||
} else {
|
||||
Err(CheckError(format!("wrong args for index_of: must be (string, string)")))
|
||||
}),
|
||||
run: Arc::new(|a, _i| index_of(a, true)),
|
||||
})).add_var("substring".to_string(), Data::new(data::function::Function {
|
||||
info: Arc::new(Info::neverused()),
|
||||
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
|
||||
out: Arc::new(|a, _i| {
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
if t.0.len() != 2 && t.0.len() != 3 {
|
||||
return Err(CheckError(format!("cannot call substring with tuple argument of len != 3")));
|
||||
}
|
||||
if !t.0[0].is_included_in(&data::string::StringT) {
|
||||
return Err(CheckError(format!("cannot call substring with tuple argument that isn't (*string*, int, int)")));
|
||||
}
|
||||
if !t.0[1].is_included_in(&data::int::IntT) {
|
||||
return Err(CheckError(format!("cannot call substring with tuple argument that isn't (string, *int*, int)")));
|
||||
}
|
||||
if t.0.len() > 2 && !t.0[2].is_included_in(&data::int::IntT) {
|
||||
return Err(CheckError(format!("cannot call substring with tuple argument that isn't (string, int, *int*)")));
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("cannot call substring with non-tuple argument.")));
|
||||
}
|
||||
}
|
||||
Ok(if a.types.is_empty() {
|
||||
Type::empty()
|
||||
} else {
|
||||
Type::new(data::string::StringT)
|
||||
})
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
let tuple = a.get();
|
||||
let tuple = tuple.as_any().downcast_ref::<data::tuple::Tuple>().expect("called substring with non-tuple arg");
|
||||
let (s, start, end) = (&tuple.0[0], &tuple.0[1], tuple.0.get(2));
|
||||
let s = s.get();
|
||||
let s = &s.as_any().downcast_ref::<data::string::String>().unwrap().0;
|
||||
let start = start.get();
|
||||
let start = start.as_any().downcast_ref::<data::int::Int>().unwrap().0;
|
||||
let start = if start < 0 { s.len().saturating_sub(start.abs() as usize) } else { start as usize };
|
||||
let end = end
|
||||
.map(|end| end.get())
|
||||
.map(|end| end.as_any().downcast_ref::<data::int::Int>().unwrap().0)
|
||||
.map(|i| if i < 0 { s.len().saturating_sub(i.abs() as usize) } else { i as usize })
|
||||
.unwrap_or(usize::MAX);
|
||||
let end = end.min(s.len());
|
||||
if end < start {
|
||||
return Data::new(data::string::String(String::new()));
|
||||
}
|
||||
Data::new(data::string::String(s[start..end].to_owned()))
|
||||
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of(a: Data, rev: bool) -> Data {
|
||||
let a = a.get();
|
||||
let a = a
|
||||
.as_any()
|
||||
.downcast_ref::<data::tuple::Tuple>()
|
||||
.expect("index_of called on non-tuple");
|
||||
let src = a.0[0].get();
|
||||
let src = &src
|
||||
.as_any()
|
||||
.downcast_ref::<data::string::String>()
|
||||
.unwrap()
|
||||
.0;
|
||||
let pat = a.0[1].get();
|
||||
let pat = &pat
|
||||
.as_any()
|
||||
.downcast_ref::<data::string::String>()
|
||||
.unwrap()
|
||||
.0;
|
||||
let i = if rev { src.rfind(pat) } else { src.find(pat) };
|
||||
if let Some(i) = i {
|
||||
Data::new(data::int::Int(i as _))
|
||||
} else {
|
||||
Data::empty_tuple()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
data::{self, Type},
|
||||
data::{self, Data, MersType, Type},
|
||||
parsing::SourcePos,
|
||||
};
|
||||
|
||||
@@ -26,13 +26,24 @@ impl MersStatement for AssignTo {
|
||||
}
|
||||
let source = self.source.check(info, None)?;
|
||||
let target = self.target.check(info, Some(&source))?;
|
||||
Ok(source)
|
||||
if !self.is_init {
|
||||
if let Some(t) = target.dereference() {
|
||||
if !source.is_included_in(&t) {
|
||||
return Err(CheckError(format!(
|
||||
"can't assign {source} to {target} because it isn't included in {t}!"
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
return Err(CheckError(format!("can't assign to non-reference!")));
|
||||
}
|
||||
}
|
||||
Ok(Type::empty_tuple())
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> crate::data::Data {
|
||||
let source = self.source.run(info);
|
||||
let target = self.target.run(info);
|
||||
data::defs::assign(&source, &target);
|
||||
target
|
||||
Data::empty_tuple()
|
||||
}
|
||||
fn has_scope(&self) -> bool {
|
||||
false
|
||||
|
||||
@@ -32,13 +32,14 @@ impl MersStatement for Variable {
|
||||
.expect("variable's is_init was true, but check_custom's assign was None? How?")
|
||||
.clone();
|
||||
}
|
||||
Ok(if self.is_ref {
|
||||
let val = if self.is_ref {
|
||||
Type::new(data::reference::ReferenceT(
|
||||
info.scopes[self.var.0].vars[self.var.1].clone(),
|
||||
))
|
||||
} else {
|
||||
info.scopes[self.var.0].vars[self.var.1].clone()
|
||||
})
|
||||
};
|
||||
Ok(val)
|
||||
}
|
||||
fn run_custom(&self, info: &mut super::Info) -> Data {
|
||||
if self.is_init {
|
||||
|
||||
Reference in New Issue
Block a user