This commit is contained in:
Mark
2023-10-19 18:46:15 +02:00
parent 2d79e75ba2
commit b39a768099
22 changed files with 822 additions and 94 deletions

View File

@@ -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()

View File

@@ -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()),

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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")
}
}),
}),
)
}
}

View File

@@ -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 {

View 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()
}
}

View File

@@ -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

View File

@@ -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 {