diff --git a/src/vm/builtins/builtin_utils.rs b/src/vm/builtins/builtin_utils.rs index 4bee57b9..c4aebaef 100644 --- a/src/vm/builtins/builtin_utils.rs +++ b/src/vm/builtins/builtin_utils.rs @@ -78,6 +78,9 @@ macro_rules! impl_type { let mut __arg_idx = 0usize; + impl_type!(@macro dict [$] $vm); + impl_type!(@macro array [$] $vm); + $( $( @@ -395,6 +398,51 @@ macro_rules! impl_type { (@extra Key ($name:ident, $vm:ident, $args:ident, $arg_index:ident) ) => { let $name = $args[$arg_index]; }; + + (@macro dict [$dollar:tt] $vm:ident) => { + macro_rules! dict { + { $dollar($key:ident: $value:expr $dollar(,)?)* } => { + use ahash::AHashMap; + use lasso::Spur; + use crate::vm::interpreter::{ValueKey, Visibility}; + let mut dict: AHashMap = AHashMap::new(); + $dollar( + dict.insert( + $vm.intern(stringify!($key)), + ( + $vm.memory.insert(StoredValue { + value: $value, + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: crate::sources::CodeSpan { start: 0, end: 0 } + } + }), + Visibility::Public, + ) + ); + )* + Value::Dict(dict) + } + } + }; + (@macro array [$dollar:tt] $vm:ident) => { + macro_rules! array { + [ $dollar($value:expr $dollar(,)?)* ] => { + let mut array: Vec<$crate::vm::interpreter::ValueKey> = Vec::new(); + $dollar( + array.push( + $vm.memory.insert(StoredValue { + value: $value, + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: crate::sources::CodeSpan { start: 0, end: 0 } + } + }), + ); + )* + } + } + }; } pub use impl_type; diff --git a/src/vm/builtins/core/array.rs b/src/vm/builtins/core/array.rs index 120cac4e..e7c9bab6 100644 --- a/src/vm/builtins/core/array.rs +++ b/src/vm/builtins/core/array.rs @@ -100,5 +100,49 @@ impl_type! { slf.get_mut_ref(vm).insert(0, cloned); Value::Empty } + + fn pick(Array(array) as self, quantity: Empty | Int if (>0) = {()}, Bool(duplicates) as allow_duplicates = {false}) { + use rand::prelude::*; + + let mut rng = rand::thread_rng(); + + match quantity { + QuantityValue::Empty(_) => { + array.choose(&mut rng).map_or(Value::Maybe(None), |v| { + let value_key = vm.deep_clone_key_insert(*v); + vm.memory[value_key].value.clone() + }) + }, + QuantityValue::Int(q) => { + if array.is_empty() { return Ok(Value::Array(vec![])) } + + if duplicates { + let mut output = vec![]; + for i in 0..q.0 { + output.push(vm.deep_clone_key_insert(*array.choose(&mut rng).unwrap())) + } + Value::Array(output) + } else { + let mut cloned_array: Vec<_> = array.iter().map(|v| vm.deep_clone_key_insert(*v)).collect(); + let mut output = vec![]; + for i in 0..q.0.min(cloned_array.len() as i64) { + output.push(cloned_array.remove(rng.gen_range(0..cloned_array.len()))) + } + Value::Array(output) + } + }, + } + } + + fn shuffle(Array(array) as self) { + use rand::prelude::*; + + let mut rng = rand::thread_rng(); + + let mut cloned_array = array.iter().map(|v| vm.deep_clone_key_insert(*v)).collect::>(); + cloned_array.shuffle(&mut rng); + + Value::Array(cloned_array) + } } } diff --git a/src/vm/builtins/core/builtins.rs b/src/vm/builtins/core/builtins.rs index f5b14c2a..4e85aa9d 100644 --- a/src/vm/builtins/core/builtins.rs +++ b/src/vm/builtins/core/builtins.rs @@ -1,4 +1,5 @@ use std::hash::Hasher; +use std::time::SystemTime; use crate::gd::gd_object::{GdObject, Trigger}; use crate::gd::ids::Id; @@ -91,5 +92,35 @@ impl_type! { vm.hash_value(value, &mut hasher); Value::Int(unsafe { std::mem::transmute::(hasher.finish()) }) } + + fn time(Builtins as self) -> Int { + match std::time::SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(time) => Value::Float(time.as_secs_f64()), + Err(e) => { + // return Err(Runti) // not sure if there needs to be added a new error for this, idk + Value::Float(0.0) + } + } + } + + fn random(Builtins as self, input: Array | Range | Empty = {()}) -> Float { + use rand::prelude::*; + + let mut rng = rand::thread_rng(); + + match input { + InputValue::Array(array) => + array.choose(&mut rng).map_or(Value::Maybe(None), |v| { + let value_key = vm.deep_clone_key_insert(*v); + vm.memory[value_key].value.clone() + }), + InputValue::Range(RangeDeref(start, end, step)) => + Value::Int((start..end).step_by(step).choose(&mut rng).unwrap_or(0)), + InputValue::Empty(_) => + Value::Float(rng.gen::()), // 0.0..1.0 + _ => + unreachable!(), + } + } } } diff --git a/src/vm/builtins/core/dict.rs b/src/vm/builtins/core/dict.rs index c3862351..3be1cdb4 100644 --- a/src/vm/builtins/core/dict.rs +++ b/src/vm/builtins/core/dict.rs @@ -1,6 +1,6 @@ +use crate::sources::CodeArea; use crate::vm::builtins::builtin_utils::impl_type; -use crate::vm::interpreter::Visibility; -use crate::vm::value::Value; +use crate::vm::value::{Value, StoredValue}; impl_type! { impl Dict { @@ -18,5 +18,52 @@ impl_type! { // // Value::Dict(dict.clone()) // } + + // what about visibility? + fn size(Dict(dict) as self) -> Int { + Value::Int(dict.len() as i64) + } + + // TODO: visibility + fn keys(Dict(dict) as self) -> Array { + Value::Array(dict.keys().map(|v| + vm.memory.insert(StoredValue { + value: Value::String(vm.resolve(v).chars().collect()), + area: CodeArea { // TODO: hire a spwn dev to fix this, since I am not a spwn dev. + src: crate::sources::SpwnSource::Core(Default::default()), + span: crate::sources::CodeSpan { start: 0, end: 0 } + }, + }) + ).collect()) + } + fn values(Dict(dict) as self) -> Array { + Value::Array(dict.values().map(|v| { + vm.deep_clone_key_insert(v.0) + }).collect()) + } + fn items(Dict(dict) as self) -> Array { + let mut items = vec![]; + + for (key, value) in &dict { + items.push(StoredValue { + value: Value::Array(vec![ + vm.memory.insert(StoredValue { + value: Value::String(vm.resolve(key).chars().collect()), + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: crate::sources::CodeSpan { start: 0, end: 0 } + }, + }), + vm.deep_clone_key_insert(value.0), + ]), + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: crate::sources::CodeSpan { start: 0, end: 0 } + }, + }); + } + + Value::Array(items.iter().map(|v| vm.memory.insert(v.clone())).collect()) + } } } diff --git a/src/vm/builtins/core/float.rs b/src/vm/builtins/core/float.rs index dc730a6c..6370b211 100644 --- a/src/vm/builtins/core/float.rs +++ b/src/vm/builtins/core/float.rs @@ -25,11 +25,14 @@ impl_type! { fn floor(Float(n) as self) -> Float { Value::Float(n.floor()) } + fn trunc(Float(n) as self) -> Float { + Value::Float(n.trunc()) + } fn sqrt(Float(n) as self) -> Float { Value::Float(n.sqrt()) } - fn log(Float(n) as self, Float(base) as base = {2.7182818284590452353602874713527}) -> Float { + fn log(Float(n) as self, Float(base) as base = {2.71828182845904523536028747135266250}) -> Float { Value::Float(n.log(base)) } diff --git a/src/vm/builtins/core/int.rs b/src/vm/builtins/core/int.rs index a10faf70..2fb68b4b 100644 --- a/src/vm/builtins/core/int.rs +++ b/src/vm/builtins/core/int.rs @@ -18,7 +18,7 @@ impl_type! { fn sqrt(Int(n) as self) -> Float { Value::Float((n as f64).sqrt()) } - fn log(Int(n) as self, Float(base) as base = {2.7182818284590452353602874713527}) -> Float { + fn log(Int(n) as self, Float(base) as base = {2.71828182845904523536028747135266250}) -> Float { Value::Float((n as f64).log(base)) } fn clamp(Int(n) as self, Int(min) as min, Int(max) as max) -> Float { @@ -27,5 +27,16 @@ impl_type! { fn wrap(Int(n) as self, Int(min) as min, Int(max) as max) -> Float { Value::Int(((n - min) % (max - min)) + min) } + fn ordinal(Int(n) as self) -> String { + let n = n.abs(); + let last_digit = n % 10; + let is_ten = n / 10 % 10 == 1; + Value::String((n.to_string() + match (last_digit, is_ten) { + (1, false) => "st", + (2, false) => "nd", + (3, false) => "rd", + (_, _) => "th", + }).chars().collect()) + } } } diff --git a/src/vm/builtins/core/mod.rs b/src/vm/builtins/core/mod.rs index 86b80c99..63851b50 100644 --- a/src/vm/builtins/core/mod.rs +++ b/src/vm/builtins/core/mod.rs @@ -6,3 +6,4 @@ pub mod error; pub mod float; pub mod int; pub mod string; +pub mod path; diff --git a/src/vm/builtins/core/path.rs b/src/vm/builtins/core/path.rs new file mode 100644 index 00000000..8e2db1e4 --- /dev/null +++ b/src/vm/builtins/core/path.rs @@ -0,0 +1,171 @@ +use std::env; +use std::fs; +use std::path::Component; +use std::path::PathBuf; + +use crate::sources::CodeArea; +use crate::vm::builtins::builtin_utils::impl_type; +use crate::vm::value::{Value, StoredValue}; + +impl_type! { + impl Path { + Constants: + + Functions(vm, call_area): + + fn new(path: String) -> Path { + Value::Path(PathBuf::from(path.0.iter().collect::())) + } + + fn cwd() -> Path { + Value::Path(env::current_dir().unwrap_or(PathBuf::from("./"))) + } + + // MODIFY + // returned + fn join(Path(path) as self, sub_path: String | Path) -> Path { + let sub_path = match sub_path { + SubPathValue::String(string) => PathBuf::from(string.iter().collect::()), + SubPathValue::Path(path) => path.to_path_buf(), + }; + Value::Path(path.join(sub_path)) + } + fn parent(Path(path) as self) -> Path { + let mut path = path.clone(); + path.pop(); + Value::Path(path) + } + // in-place + fn push(slf: &Path, sub_path: String | Path) { + let path = match sub_path { + SubPathValue::String(string) => PathBuf::from(string.iter().collect::()), + SubPathValue::Path(path) => path.to_path_buf(), + }; + slf.get_mut_ref(vm).push(path); + Value::Empty + } + fn pop(slf: &Path) { + slf.get_mut_ref(vm).pop(); + Value::Empty + } + + fn is_absolute(Path(path) as self) -> Bool { + Value::Bool(path.is_absolute()) + } + fn is_relative(Path(path) as self) -> Bool { + Value::Bool(path.is_relative()) + } + + fn split(Path(path) as self) -> Array { + Value::Array( + path + .components() + .enumerate() + .filter_map(|(i, component)| + match component { + Component::RootDir => if i == 0 { Some("/") } else { None }, + Component::CurDir => Some("./"), + Component::ParentDir => Some(".."), + Component::Normal(string) => Some(string.to_str().unwrap()), + Component::Prefix(prefix) => Some(prefix.as_os_str().to_str().unwrap()), + } + ) + .map(|string| string.chars().collect::>()) + .map(|string| StoredValue { + value: Value::String(string), + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: Default::default(), + }, + }) + .map(|stored_value| vm.memory.insert(stored_value)) + .collect() + ) + } + + // FS DATA METHODS + fn exists(Path(path) as self) -> Bool { + Value::Bool(path.exists()) + } + fn kind(Path(path) as self) -> String { + match path.metadata() { + Ok(metadata) => { + Value::String(match metadata.file_type() { + meta if meta.is_file() => "file", + meta if meta.is_dir() => "dir", + _ => "unknown", + }.chars().collect()) + }, + Err(err) => { + todo!() + }, + } + } + fn metadata(Path(path) as self) -> Dict { + match path.metadata() { + Ok(metadata) => { + dict!{ + length: Value::Int(metadata.len() as i64), + kind: Value::String(format!("{:?}", metadata.file_type()).chars().collect::>()), + } + }, + Err(err) => { + todo!() + // Value::Empty + }, + } + } + + // FILE METHODS + fn write(Path(path) as self, content: String) { + fs::write(path, content.0.iter().collect::()).unwrap(); + Value::Empty + } + fn read(Path(path) as self) -> String { + Value::String(fs::read(path).unwrap().iter().map(|byte| *byte as char).collect()) + } + fn remove(Path(path) as self) { + fs::remove_file(path).unwrap(); + Value::Empty + } + + // FOLDER METHODS + fn read_dir(Path(path) as self) -> Array { + match path.read_dir() { + Ok(dirs) => { + Value::Array( + dirs + .into_iter() + .filter_map(|f| f.ok().map(|f| vm.memory.insert(StoredValue { + value: Value::Path(f.path()), + area: CodeArea { + src: crate::sources::SpwnSource::Core(Default::default()), + span: Default::default(), + } + }))) + .collect::>() + ) + }, + Err(err) => { + todo!() + }, + } + } + fn create_dir(Path(path) as self, all: Bool = {false}) { + let error = if *all { + fs::create_dir_all(path) + } else { + fs::create_dir(path) + }; + Value::Empty + } + fn remove_dir(Path(path) as self, all: Bool = {false}) { + let error = if *all { + fs::remove_dir_all(path) + } else { + fs::remove_dir(path) + }; + Value::Empty + } + } +} \ No newline at end of file diff --git a/src/vm/builtins/core/string.rs b/src/vm/builtins/core/string.rs index dfbcec00..36b45f18 100644 --- a/src/vm/builtins/core/string.rs +++ b/src/vm/builtins/core/string.rs @@ -9,21 +9,13 @@ impl_type! { Functions(vm, call_area): - // todo: better time complexiyt lol - /// Returns `true` if the string contains the given substring (`O(nm)` time complexity) + /// Returns `true` if the string contains the given substring fn contains(String(s) as self, String(substr) as substr) { - for i in 0..s.len() { - let mut j = i; - let mut k = 0; - while substr[k] == s[j] { - j += 1; - k += 1; - if k == substr.len() { - return Ok(Value::Bool(true)); - } - } - } - Value::Bool(false) + Value::Bool( + s.iter().collect::().contains( + &substr.iter().collect::() + ) + ) } /// Returns `true` if the string ends with the given suffix @@ -36,24 +28,18 @@ impl_type! { Value::Bool(s.starts_with(&prefix)) } - // todo: better time complexiyt lol - /// Returns the index of the first occurrence of the given substring (`O(nm)` time complexity) + /// Returns the index of the first occurrence of the given substring fn index(String(s) as self, String(substr) as substr) { - for i in 0..s.len() { - let mut j = i; - let mut k = 0; - while substr[k] == s[j] { - j += 1; - k += 1; - if k == substr.len() { - return Ok(Value::Maybe(Some(vm.memory.insert(StoredValue { - value: Value::Int(i as i64), - area: call_area - })))); - } - } - } - Value::Maybe(None) + s.iter().collect::().find( // todo: this returns the byte address, not the character index + &substr.iter().collect::() + ) + .map_or( + Value::Maybe(None), + |i| Value::Maybe(Some(vm.memory.insert(StoredValue { + value: Value::Int(i as i64), + area: call_area + }))) + ) } /// Returns `true` if the string is numeric diff --git a/src/vm/interpreter.rs b/src/vm/interpreter.rs index 6755e12e..1f343a67 100644 --- a/src/vm/interpreter.rs +++ b/src/vm/interpreter.rs @@ -2118,6 +2118,7 @@ impl<'a> Vm<'a> { } }, Value::Chroma { r, g, b, a } => (r, g, b, a).hash(state), + Value::Path(path) => path.hash(state), } } diff --git a/src/vm/value.rs b/src/vm/value.rs index 326b0e6c..a9ffd756 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::rc::Rc; use std::str::FromStr; +use std::path::PathBuf; use ahash::AHashMap; use delve::{FieldNames, ModifyField, VariantNames}; @@ -371,6 +372,8 @@ value! { r: u8, g: u8, b: u8, a: u8, }, + Path(PathBuf), + => Instance { typ: CustomTypeKey, items: AHashMap, @@ -558,6 +561,7 @@ impl Value { ), Value::Iterator(_) => "".into(), Value::ObjectKey(k) => format!("$.obj_props.{}", >::into(*k)), + Value::Path(path) => path.to_str().unwrap_or("").to_string(), Value::Error(_) => todo!(), } }