added some string methods and assume1, which takes ([]/[t]) or ([]/[t] string) and returns t. If the first argument passed is [] instead of [t], panics with the custom error message, if one was provided (string argument).

This commit is contained in:
Dummi26 2023-03-22 19:20:02 +01:00
parent 2a00d01812
commit 744f268bfd
3 changed files with 228 additions and 1 deletions

View File

@ -6,3 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
regex = "1.7.2"

View File

@ -100,7 +100,7 @@ A match arm consists of two consecutive statements: The condition statement foll
If the condition statement matches, the action statement will be executed. The matched value from the condition statement can be found in the variable we originally matched on, so it can be used in the action statement. If the condition statement matches, the action statement will be executed. The matched value from the condition statement can be found in the variable we originally matched on, so it can be used in the action statement.
**These are the rules for matching:** **These are the rules for matching:**
- The condition statement *does not match* if it returns **false** or **[]** - The condition statement *does not match* if it returns **false**, **[]** or an error.
- If the condition statement returns a length 1 tuple [v], it will match and the matched value will be v. The condition statement can't return any tuple except [] and [v] to avoid confusion. To return a tuple, wrap it in a length-1 tuple: [[val_1, val_2, val_3]]. - If the condition statement returns a length 1 tuple [v], it will match and the matched value will be v. The condition statement can't return any tuple except [] and [v] to avoid confusion. To return a tuple, wrap it in a length-1 tuple: [[val_1, val_2, val_3]].
- Otherwise, the condition statement will match and the matched value will be whatever it returned. - Otherwise, the condition statement will match and the matched value will be whatever it returned.

View File

@ -1,3 +1,4 @@
use core::panicking::unreachable_display;
use std::{ use std::{
path::PathBuf, path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -12,6 +13,8 @@ use super::{
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BuiltinFunction { pub enum BuiltinFunction {
// core
Assume1, // assume []/[t] is [t], return t. Optionally provide a reason as to why (2nd arg)
// print // print
Print, Print,
Println, Println,
@ -60,11 +63,19 @@ pub enum BuiltinFunction {
Remove, Remove,
Get, Get,
Len, Len,
// String
Contains,
StartsWith,
EndsWith,
Trim,
Substring,
Regex,
} }
impl BuiltinFunction { impl BuiltinFunction {
pub fn get(s: &str) -> Option<Self> { pub fn get(s: &str) -> Option<Self> {
Some(match s { Some(match s {
"assume1" => Self::Assume1,
"print" => Self::Print, "print" => Self::Print,
"println" => Self::Println, "println" => Self::Println,
"debug" => Self::Debug, "debug" => Self::Debug,
@ -105,11 +116,50 @@ impl BuiltinFunction {
"remove" => Self::Remove, "remove" => Self::Remove,
"get" => Self::Get, "get" => Self::Get,
"len" => Self::Len, "len" => Self::Len,
"contains" => Self::Contains,
"starts_with" => Self::StartsWith,
"ends_with" => Self::EndsWith,
"trim" => Self::Trim,
"substring" => Self::Substring,
"regex" => Self::Regex,
_ => return None, _ => return None,
}) })
} }
pub fn can_take(&self, input: &Vec<VType>) -> bool { pub fn can_take(&self, input: &Vec<VType>) -> bool {
match self { match self {
Self::Assume1 => {
if input.len() >= 1 {
let len0 = false;
let len1 = false;
for t in input[0].types.iter() {
match t {
VSingleType::Tuple(v) => match v.len() {
0 => len0 = true,
1 => len1 = true,
_ => return false,
},
_ => return false,
}
}
if !len0 {
eprintln!("Warn: calling assume1 on a value of type {}, which will always be a length-1 tuple.", input[0]);
}
if !len1 {
eprintln!("Warn: calling assume1 on a value of type {}, which will never be a length-1 tuple!", input[0]);
}
if input.len() >= 2 {
if input.len() == 2 {
input[1].fits_in(&VSingleType::String.to()).is_empty()
} else {
false
}
} else {
true
}
} else {
false
}
}
Self::Print | Self::Println => { Self::Print | Self::Println => {
if input.len() == 1 { if input.len() == 1 {
input[0].fits_in(&VSingleType::String.to()).is_empty() input[0].fits_in(&VSingleType::String.to()).is_empty()
@ -269,6 +319,20 @@ impl BuiltinFunction {
/// for invalid inputs, may panic /// for invalid inputs, may panic
pub fn returns(&self, input: Vec<VType>) -> VType { pub fn returns(&self, input: Vec<VType>) -> VType {
match self { match self {
Self::Assume1 => {
let mut out = VType { types: vec![] };
for t in &input[0].types {
match t {
VSingleType::Tuple(v) => {
if !v.is_empty() {
out = out | &v[0];
}
}
_ => unreachable!(),
}
}
out
}
// [] // []
Self::Print | Self::Println | Self::Debug | Self::Sleep => VType { Self::Print | Self::Println | Self::Debug | Self::Sleep => VType {
types: vec![VSingleType::Tuple(vec![])], types: vec![VSingleType::Tuple(vec![])],
@ -421,10 +485,47 @@ impl BuiltinFunction {
Self::Eq | Self::Lt | Self::Gt | Self::Ltoe | Self::Gtoe => VSingleType::Bool.to(), Self::Eq | Self::Lt | Self::Gt | Self::Ltoe | Self::Gtoe => VSingleType::Bool.to(),
Self::Push | Self::Insert => VSingleType::Tuple(vec![]).into(), Self::Push | Self::Insert => VSingleType::Tuple(vec![]).into(),
Self::Len => VSingleType::Int.into(), Self::Len => VSingleType::Int.into(),
Self::Contains | Self::StartsWith | Self::EndsWith => VSingleType::Bool.into(),
Self::Trim => VSingleType::String.into(),
Self::Substring => VSingleType::String.into(),
Self::Regex => VType {
types: vec![
VSingleType::Tuple(vec![
// does match
VSingleType::Tuple(vec![VSingleType::List(VSingleType::String.to()).to()])
.to(),
// no error
]),
VSingleType::Tuple(vec![
// does not match
VSingleType::Tuple(vec![]).to(),
// error
VSingleType::String.to(),
]),
],
},
} }
} }
pub fn run(&self, args: &Vec<RStatement>, vars: &Vec<Arc<Mutex<VData>>>) -> VData { pub fn run(&self, args: &Vec<RStatement>, vars: &Vec<Arc<Mutex<VData>>>) -> VData {
match self { match self {
Self::Assume1 => {
if let VDataEnum::Tuple(v) = args[0].run(vars).data {
v[0]
} else {
panic!(
"ASSUMPTION FAILED: assume1 :: {}",
if args.len() > 1 {
if let VDataEnum::String(v) = args[1].run(vars).data {
v
} else {
String::new()
}
} else {
String::new()
}
);
}
}
BuiltinFunction::Print => { BuiltinFunction::Print => {
if let VDataEnum::String(arg) = args[0].run(vars).data { if let VDataEnum::String(arg) = args[0].run(vars).data {
print!("{}", arg); print!("{}", arg);
@ -1103,6 +1204,131 @@ impl BuiltinFunction {
unreachable!("len: not 1 arg") unreachable!("len: not 1 arg")
} }
} }
Self::Contains => {
if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars).data {
if let VDataEnum::String(a2) = args[1].run(vars).data {
VDataEnum::Bool(a1.contains(a2.as_str())).to()
} else {
unreachable!()
}
} else {
unreachable!()
}
} else {
unreachable!()
}
}
Self::StartsWith => {
if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars).data {
if let VDataEnum::String(a2) = args[1].run(vars).data {
VDataEnum::Bool(a1.starts_with(a2.as_str())).to()
} else {
unreachable!()
}
} else {
unreachable!()
}
} else {
unreachable!()
}
}
Self::EndsWith => {
if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars).data {
if let VDataEnum::String(a2) = args[1].run(vars).data {
VDataEnum::Bool(a1.ends_with(a2.as_str())).to()
} else {
unreachable!()
}
} else {
unreachable!()
}
} else {
unreachable!()
}
}
Self::Trim => {
if args.len() == 1 {
if let VDataEnum::String(a) = args[0].run(vars).data {
VDataEnum::String(a.trim().to_string()).to()
} else {
unreachable!()
}
} else {
unreachable!()
}
}
Self::Substring => {
if args.len() >= 2 {
if let VDataEnum::String(a) = args[0].run(vars).data {
if args.len() > 3 {
unreachable!()
}
let left = if let VDataEnum::Int(left) = args[1].run(vars).data {
left
} else {
unreachable!()
};
let len = if args.len() == 3 {
if let VDataEnum::Int(len) = args[2].run(vars).data {
Some(len)
} else {
unreachable!()
}
} else {
None
};
let left = if left >= 0 { left as usize } else { 0 };
if let Some(len) = len {
let len = if len >= 0 {
len as usize
} else {
todo!("negative len - shorthand for backwards? not sure yet...")
};
VDataEnum::String(a.chars().skip(left).take(len).collect()).to()
} else {
VDataEnum::String(a.chars().skip(left).collect()).to()
}
} else {
unreachable!()
}
} else {
unreachable!()
}
}
Self::Regex => {
if args.len() == 2 {
if let (VDataEnum::String(a), VDataEnum::String(regex)) =
(args[0].run(vars).data, args[1].run(vars).data)
{
match regex::Regex::new(regex.as_str()) {
Ok(regex) => {
VDataEnum::Tuple(vec![VDataEnum::Tuple(vec![VDataEnum::List(
VSingleType::String.to(),
regex
.find_iter(a.as_str())
.map(|v| VDataEnum::String(v.as_str().to_string()).to())
.collect(),
)
.to()])
.to()])
.to()
}
Err(e) => VDataEnum::Tuple(vec![
VDataEnum::Tuple(vec![]).to(), // no results
VDataEnum::String(e.to_string()).to(),
])
.to(),
}
} else {
unreachable!()
}
} else {
unreachable!()
}
}
} }
} }
} }