diff --git a/compiler/src/builtins.rs b/compiler/src/builtins.rs index 6ed49eb0..5a56b248 100644 --- a/compiler/src/builtins.rs +++ b/compiler/src/builtins.rs @@ -243,7 +243,7 @@ impl Value { } _ => (), }, - Value::Macro(m) => match member.as_ref().as_str() { + Value::Macro(Macro::FuncLike(m)) => match member.as_ref().as_str() { "args" => { let mut args = vec![]; for MacroArgDef { @@ -297,7 +297,7 @@ impl Value { Value::Builtins => match Builtin::from_str(member.as_str()) { Err(_) => None, Ok(builtin) => Some(store_const_value( - Value::BuiltinFunction(builtin), + Value::Macro(Macro::BuiltinLike(builtin)), globals, context.start_group, info.position, @@ -508,6 +508,32 @@ macro_rules! builtins { Self::new() } } + + + pub fn get_builtin_arg_patterns(b: &Builtin) -> Option> { + match b { + $( + Builtin::$variant => { + $( if stringify!($argdesc) == "any" { return None } )? + $( if stringify!($argdesc) == "none" { return Some(vec![]) } )? + + $( + return Some(vec![$( + loop { + $( break pattern_from_value_variant(stringify!($arg_type)); )? + #[allow(unreachable_code)] + break Pattern::Any + }, + )+]); + )? + + #[allow(unreachable_code)] + None + } + )* + } + } + #[inline] pub fn built_in_function( func: Builtin, @@ -1946,6 +1972,38 @@ $.assert(name_age == { } } + [Call] #[safe = true, desc = "Calls a macro with the arguments provided in an array", example = " +m = (x, y) => x + y +$.assert($.call(m, [1, 2]) == 3) + "] + fn call((m): Macro, (args): Array) { + + if let Macro::FuncLike(m) = m { + let args = args.iter() + .map(|s| parser::ast::Argument::from(*s, globals.stored_values.map.get(*s).unwrap().def_area.pos) ) + .collect::>(); + + + let full_context = unsafe { FullContext::from_ptr(full_context) }; + let parent = full_context.inner().return_value2; + execute_macro( + (m, args.clone()), + full_context, + globals, + parent, + info.clone(), + )?; + globals.stored_values[full_context.inner().return_value].clone() + } else { + return Err(RuntimeError::BuiltinError { + builtin, + message: format!("Can only dynamically call func-like macros (so far)"), + info, + }) + } + + } + [Regex] #[safe = true, desc = "Performs a regex operation on a string", example = ""] fn regex(#["`mode` can be either \"match\", \"replace\", \"find_all\" or \"find_groups\""](regex): Str, (s): Str, (mode): Str, (replace)) { use fancy_regex::Regex; @@ -2329,6 +2387,22 @@ $.assert(name_age == { (*globals.stored_values.map.get_mut(arguments[0]).unwrap()).def_area = info.position; Value::Null } + [RightShiftOp] #[safe = true, desc = "Default implementation of the `>>` operator", example = "$._right_shift_(10, 2)"] + fn _right_shift_((a): Number, (b): Number) { + Value::Number(((a as i128) >> (b as i128)) as f64) + } + [LeftShiftOp] #[safe = true, desc = "Default implementation of the `<<` operator", example = "$._left_shift_(3, 1)"] + fn _left_shift_((a): Number, (b): Number) { + Value::Number(((a as i128) << (b as i128)) as f64) + } + [BitNotOp] #[safe = true, desc = "Default implementation of the `~n` operator", example = "$._bit_not_(4)"] + fn _bit_not_((a): Number) { + Value::Number((!(a as i128)) as f64) + } + [BitXorOp] #[safe = true, desc = "Default implementation of the `~?` operator", example = "$._bit_xor_(4, 9)"] + fn _bit_xor_((a): Number, (b): Number) { + Value::Number(((a as i128) ^ (b as i128)) as f64) + } [SwapOp] #[safe = true, desc = "Default implementation of the `<=>` operator", example = "let a = 10\nlet b = 5\n$._swap_(a, b)\n$.assert(a == 5)\n$.assert(b == 10)"] fn _swap_(mut (a), mut (b)) { @@ -2563,36 +2637,56 @@ $.assert(name_age == { Value::Null } + [LeftShiftAssignOp] #[safe = true, desc = "Default implementation of the `<<=` operator", example = "let val = 8\n$._left_shift_assign_(val, 5)\n$.assert(val == 256)"] + fn _left_shift_assign_(mut (a): Number, (b): Number) {a = ((a as i128) << (b as i128)) as f64; Value::Null} + + [RightShiftAssignOp] #[safe = true, desc = "Default implementation of the `>>=` operator", example = "let val = 32\n$._right_shift_assign_(val, 2)\n$.assert(val == 8)"] + fn _right_shift_assign_(mut (a): Number, (b): Number) {a = ((a as i128) >> (b as i128)) as f64; Value::Null} + [EitherOp] #[safe = true, desc = "Default implementation of the `|` operator", example = "$._either_(@number, @counter)"] fn _either_((a), (b)) { - Value::Pattern(Pattern::Either( - if let Value::Pattern(p) = convert_type(&a, type_id!(pattern), &info, globals, context)? { - Box::new(p) - } else { - unreachable!() - }, - if let Value::Pattern(p) = convert_type(&b, type_id!(pattern), &info, globals, context)? { - Box::new(p) - } else { - unreachable!() + match (a.clone(), b.clone()) { + (Value::Number(a), Value::Number(b)) => { + Value::Number(((a as i128) | (b as i128)) as f64) }, - )) + _ => { + Value::Pattern(Pattern::Either( + if let Value::Pattern(p) = convert_type(&a, type_id!(pattern), &info, globals, context)? { + Box::new(p) + } else { + unreachable!() + }, + if let Value::Pattern(p) = convert_type(&b, type_id!(pattern), &info, globals, context)? { + Box::new(p) + } else { + unreachable!() + }, + )) + } + } } [BothOp] #[safe = true, desc = "Default implementation of the `&` operator", example = "$._both_(@number, @counter)"] fn _both_((a), (b)) { - Value::Pattern(Pattern::Both( - if let Value::Pattern(p) = convert_type(&a, type_id!(pattern), &info, globals, context)? { - Box::new(p) - } else { - unreachable!() - }, - if let Value::Pattern(p) = convert_type(&b, type_id!(pattern), &info, globals, context)? { - Box::new(p) - } else { - unreachable!() + match (a.clone(), b.clone()) { + (Value::Number(a), Value::Number(b)) => { + Value::Number(((a as i128) & (b as i128)) as f64) }, - )) + _ => { + Value::Pattern(Pattern::Both( + if let Value::Pattern(p) = convert_type(&a, type_id!(pattern), &info, globals, context)? { + Box::new(p) + } else { + unreachable!() + }, + if let Value::Pattern(p) = convert_type(&b, type_id!(pattern), &info, globals, context)? { + Box::new(p) + } else { + unreachable!() + }, + )) + } + } } [DisplayOp] #[safe = true, desc = "returns the default value display string for the given value", example = "$._display_(counter()) // \"@counter::{ item: ?i, bits: 16 }\""] fn _display_((a)) { diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index d127f311..ea186802 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -302,7 +302,7 @@ pub fn compile_scope( Value::Builtins => { for name in BUILTIN_LIST.iter() { let p = store_const_value( - Value::BuiltinFunction(*name), + Value::Macro(Macro::BuiltinLike(*name)), globals, fn_context, info.position, diff --git a/compiler/src/compiler_types.rs b/compiler/src/compiler_types.rs index 9305b6ce..49d4fb0f 100644 --- a/compiler/src/compiler_types.rs +++ b/compiler/src/compiler_types.rs @@ -63,7 +63,7 @@ pub fn handle_operator( globals, info.clone(), ) { - if let Value::Macro(m) = globals.stored_values[val].clone() { + if let Value::Macro(Macro::FuncLike(m)) = globals.stored_values[val].clone() { if m.args.is_empty() { return Err(RuntimeError::CustomError(create_error( info.clone(), @@ -91,7 +91,7 @@ pub fn handle_operator( execute_macro( ( - *m, + m, //copies argument so the original value can't be mutated //prevents side effects and shit vec![ast::Argument::from( @@ -142,7 +142,7 @@ pub fn handle_unary_operator( globals, info.clone(), ) { - if let Value::Macro(m) = globals.stored_values[val].clone() { + if let Value::Macro(Macro::FuncLike(m)) = globals.stored_values[val].clone() { if m.args.is_empty() { return Err(RuntimeError::CustomError(create_error( info.clone(), @@ -152,7 +152,7 @@ pub fn handle_unary_operator( ))); } - execute_macro((*m, Vec::new()), full_context, globals, value, info.clone())?; + execute_macro((m, Vec::new()), full_context, globals, value, info.clone())?; } else { built_in_function(macro_name, vec![value], info.clone(), globals, full_context)?; } @@ -227,6 +227,11 @@ impl From for Builtin { Divide => DivideOp, IntDivide => IntdivideOp, Is => IsOp, + RightShift => RightShiftOp, + LeftShift => LeftShiftOp, + BitXor => BitXorOp, + LeftShiftAssign => LeftShiftAssignOp, + RightShiftAssign => RightShiftAssignOp } } } @@ -311,7 +316,7 @@ impl EvalExpression for ast::Expression { } pub fn execute_macro( - (m, args): (Macro, Vec), + (m, args): (MacroFuncData, Vec), contexts: &mut FullContext, globals: &mut Globals, parent: StoredValue, diff --git a/compiler/src/value.rs b/compiler/src/value.rs index 74f4d9da..445b9790 100644 --- a/compiler/src/value.rs +++ b/compiler/src/value.rs @@ -33,12 +33,12 @@ pub enum Value { Bool(bool), TriggerFunc(TriggerFunction), Dict(AHashMap, StoredValue>), - Macro(Box), + Macro(Macro), Str(String), Array(Vec), Obj(Vec<(u16, ObjParam)>, ast::ObjectMode), Builtins, - BuiltinFunction(Builtin), + // BuiltinFunction(Builtin), TypeIndicator(TypeId), Range(i32, i32, usize), //start, end, step Pattern(Pattern), @@ -50,7 +50,7 @@ pub type Slice = (Option, Option, Option); const MAX_DICT_EL_DISPLAY: usize = 10; #[derive(Clone, Debug, PartialEq)] -pub struct Macro { +pub struct MacroFuncData { pub args: Vec, pub def_variables: AHashMap, StoredValue>, pub def_file: LocalIntern, @@ -60,22 +60,37 @@ pub struct Macro { pub ret_pattern: Option, } +#[derive(Clone, Debug, PartialEq)] +pub enum Macro { + FuncLike(MacroFuncData), + BuiltinLike(Builtin) +} + #[allow(clippy::derive_hash_xor_eq)] impl Hash for Macro { fn hash(&self, state: &mut H) { - //self.args.hash(state); - for i in &self.def_variables { - i.hash(state); + + match self { + Macro::FuncLike(m) => { + for i in &m.def_variables { + i.hash(state); + } + m.def_file.hash(state); + //body.hash(state); + //tag.hash(state); + m.arg_pos.hash(state); + m.ret_pattern.hash(state); + + /* + i omitted the stuff that has ast inside cuz it + was too deep of a rabbit hoke to derive Hash for + */ + + }, + Macro::BuiltinLike(b) => { + b.hash(state) + }, } - self.def_file.hash(state); - //self.body.hash(state); - //self.tag.hash(state); - self.arg_pos.hash(state); - self.ret_pattern.hash(state); - /* - i omitted the stuff that has ast inside cuz it - was too deep of a rabbit hoke to derive Hash for - */ } } @@ -131,6 +146,30 @@ pub enum Pattern { } } +pub fn pattern_from_value_variant(s: &str) -> Pattern { + match s { + "Group" => Pattern::Type(type_id!(group)), + "Color" => Pattern::Type(type_id!(color)), + "Block" => Pattern::Type(type_id!(block)), + "Item" => Pattern::Type(type_id!(item)), + "Number" => Pattern::Type(type_id!(number)), + "Bool" => Pattern::Type(type_id!(bool)), + "TriggerFunc" => Pattern::Type(type_id!(trigger_function)), + "Dict" => Pattern::Type(type_id!(dictionary)), + "Macro" => Pattern::Type(type_id!(macro)), + "Str" => Pattern::Type(type_id!(string)), + "Array" => Pattern::Type(type_id!(array)), + "Obj" => Pattern::Type(type_id!(object)), + "Builtins" => Pattern::Type(type_id!(spwn)), + "TypeIndicator" => Pattern::Type(type_id!(type_indicator)), + "Range" => Pattern::Type(type_id!(range)), + "Pattern" => Pattern::Type(type_id!(pattern)), + "Null" => Pattern::Type(type_id!(NULL)), + _ => unreachable!() + } +} + + impl Pattern { pub fn in_pat( &self, @@ -455,7 +494,7 @@ impl Value { ast::ObjectMode::Trigger => type_id!(trigger), }, Value::Builtins => type_id!(spwn), - Value::BuiltinFunction(_) => type_id!(builtin), + // Value::BuiltinFunction(_) => type_id!(builtin), Value::TypeIndicator(_) => type_id!(type_indicator), Value::Null => type_id!(NULL), Value::Range(_, _, _) => type_id!(range), @@ -514,7 +553,7 @@ impl Value { m.hash(state); } Value::Builtins => "spwn".hash(state), - Value::BuiltinFunction(v) => v.hash(state), + // Value::BuiltinFunction(v) => v.hash(state), Value::TypeIndicator(v) => v.hash(state), Value::Range(s, e, st) => { s.hash(state); @@ -790,46 +829,82 @@ impl Value { } Pattern::Macro { args, ret } => { if let Value::Macro(m) = self { - if m.args.len() != args.len() { - (*full_context.inner()).return_value = store_const_value( - Value::Bool(false), - globals, - full_context.inner().start_group, - info.position, - ); - } else { - let mut is_matching = true; - for (i, m_arg) in m.args.iter().enumerate() { - if let Some(pat_stored) = m_arg.pattern { - match &convert_type(&globals.stored_values[pat_stored].clone(), type_id!(pattern), info, globals, full_context.inner())? { - Value::Pattern(p) => { - let matches = p.in_pat(&args[i], globals)?; - if !matches { - is_matching = false; - break; + + match m { + Macro::FuncLike(m) => { + if m.args.len() != args.len() { + (*full_context.inner()).return_value = store_const_value( + Value::Bool(false), + globals, + full_context.inner().start_group, + info.position, + ); + } else { + let mut is_matching = true; + for (i, m_arg) in m.args.iter().enumerate() { + if let Some(pat_stored) = m_arg.pattern { + match &convert_type(&globals.stored_values[pat_stored].clone(), type_id!(pattern), info, globals, full_context.inner())? { + Value::Pattern(p) => { + let matches = p.in_pat(&args[i], globals)?; + if !matches { + is_matching = false; + break; + } + }, + _ => unreachable!() } - }, - _ => unreachable!() + } } + if is_matching { + if let Some(ret_stored) = m.ret_pattern { + match &convert_type(&globals.stored_values[ret_stored].clone(), type_id!(pattern), info, globals, full_context.inner())? { + Value::Pattern(p) => { + is_matching = p.in_pat(&ret, globals)?; + + }, + _ => unreachable!() + } + } + } + (*full_context.inner()).return_value = store_const_value( + Value::Bool(is_matching), + globals, + full_context.inner().start_group, + info.position, + ); } } - if is_matching { - if let Some(ret_stored) = m.ret_pattern { - match &convert_type(&globals.stored_values[ret_stored].clone(), type_id!(pattern), info, globals, full_context.inner())? { - Value::Pattern(p) => { - is_matching = p.in_pat(&ret, globals)?; - - }, - _ => unreachable!() - } + Macro::BuiltinLike(b) => { + match get_builtin_arg_patterns(b) { + Some(v) => { + (*full_context.inner()).return_value = store_const_value( + Value::Bool( + { + let mut matches = true; + for (a, b) in v.iter().zip(args) { + if !a.in_pat(&b, globals)? { + matches = false; + break; + } + } + matches + } + ), + globals, + full_context.inner().start_group, + info.position, + ); + }, + None => { + (*full_context.inner()).return_value = store_const_value( + Value::Bool(false), + globals, + full_context.inner().start_group, + info.position, + ); + }, } } - (*full_context.inner()).return_value = store_const_value( - Value::Bool(is_matching), - globals, - full_context.inner().start_group, - info.position, - ); } } else { (*full_context.inner()).return_value = store_const_value( @@ -980,40 +1055,45 @@ impl Value { out } Value::Macro(m) => { - globals.push_new_preserved(); - for arg in &m.args { - if let Some(v) = &arg.pattern { - globals.push_preserved_val(*v); - } - if let Some(v) = &arg.default { - globals.push_preserved_val(*v); - } + match m { + Macro::FuncLike(m) => { + globals.push_new_preserved(); + for arg in &m.args { + if let Some(v) = &arg.pattern { + globals.push_preserved_val(*v); + } + if let Some(v) = &arg.default { + globals.push_preserved_val(*v); + } - } + } - let mut out = String::from("("); - if !m.args.is_empty() { - for arg in m.args.iter() { - out += &arg.name; - if let Some(val) = arg.pattern { - out += &format!( - ": {}", - display_inner(&globals.stored_values[val].clone(), globals)? - ) - }; - if let Some(val) = arg.default { - out += &format!( - " = {}", - display_inner(&globals.stored_values[val].clone(), globals)? - ) - }; - out += ", "; + let mut out = String::from("("); + if !m.args.is_empty() { + for arg in m.args.iter() { + out += &arg.name; + if let Some(val) = arg.pattern { + out += &format!( + ": {}", + display_inner(&globals.stored_values[val].clone(), globals)? + ) + }; + if let Some(val) = arg.default { + out += &format!( + " = {}", + display_inner(&globals.stored_values[val].clone(), globals)? + ) + }; + out += ", "; + } + out.pop(); + out.pop(); + } + globals.pop_preserved(); + out + ") { /* ... */ }" } - out.pop(); - out.pop(); + Macro::BuiltinLike(b) => format!("$.{}", String::from(*b)), } - globals.pop_preserved(); - out + ") { /* ... */ }" } Value::Str(s) => format!("'{}'", s), Value::Array(a) => { @@ -1046,7 +1126,6 @@ impl Value { out } Value::Builtins => "$".to_string(), - Value::BuiltinFunction(n) => format!("$.{}", String::from(*n)), Value::Null => "null".to_string(), Value::TypeIndicator(id) => format!( "@{}", @@ -1595,7 +1674,7 @@ pub fn macro_to_value( }; full_context.inner().return_value = store_const_value( - Value::Macro(Box::new(Macro { + Value::Macro(Macro::FuncLike(MacroFuncData { args, body: m.body.statements.clone(), def_variables: full_context @@ -3119,7 +3198,7 @@ impl VariableFuncs for ast::Variable { Value::TypeIndicator(t) => match globals.implementations.get(t) { Some(imp) => match imp.get(a) { Some((val, _)) => { - if let Value::Macro(m) = &globals.stored_values[*val] { + if let Value::Macro(Macro::FuncLike(m)) = &globals.stored_values[*val] { if !m.args.is_empty() && m.args[0].name == globals.SELF_MEMBER_NAME { @@ -3613,16 +3692,38 @@ impl VariableFuncs for ast::Variable { let val_ptr = full_context.inner().return_value; match globals.stored_values[val_ptr].clone() { - Value::Macro(m) => { + Value::Macro(Macro::FuncLike(m)) => { let parent = full_context.inner().return_value2; execute_macro( - (*m, args.clone()), + (m, args.clone()), full_context, globals, parent, info.clone(), )?; } + Value::Macro(Macro::BuiltinLike(name)) => { + let evaled_args = all_combinations( + args.iter().map(|x| x.value.clone()).collect(), + full_context, + globals, + info.clone(), + constant, + )?; + + globals.push_new_preserved(); + for (arg_values, _) in &evaled_args { + for val in arg_values { + globals.push_preserved_val(*val) + } + } + + for (args, context) in evaled_args { + built_in_function(name, args, info.clone(), globals, context)?; + } + + globals.pop_preserved(); + } Value::TypeIndicator(_) => { if args.len() != 1 { @@ -3659,28 +3760,6 @@ impl VariableFuncs for ast::Variable { } } - Value::BuiltinFunction(name) => { - let evaled_args = all_combinations( - args.iter().map(|x| x.value.clone()).collect(), - full_context, - globals, - info.clone(), - constant, - )?; - - globals.push_new_preserved(); - for (arg_values, _) in &evaled_args { - for val in arg_values { - globals.push_preserved_val(*val) - } - } - - for (args, context) in evaled_args { - built_in_function(name, args, info.clone(), globals, context)?; - } - - globals.pop_preserved(); - } _a => { return Err(RuntimeError::TypeError { expected: "macro, built-in function or type indicator" @@ -3725,6 +3804,7 @@ impl VariableFuncs for ast::Variable { UnaryOperator::MoreOrEqPattern => Builtin::MoreOrEqPatternOp, UnaryOperator::LessOrEqPattern => Builtin::LessOrEqPatternOp, UnaryOperator::InPattern => Builtin::InPatternOp, + UnaryOperator::BitNot => Builtin::BitNotOp, }, full_context, globals, @@ -3752,7 +3832,7 @@ impl VariableFuncs for ast::Variable { // } if !self.tag.tags.is_empty() { for c in contexts.iter() { - if let Value::Macro(m) = &mut globals.stored_values[c.inner().return_value] { + if let Value::Macro(Macro::FuncLike(m)) = &mut globals.stored_values[c.inner().return_value] { m.tag.tags.extend(self.tag.tags.clone()) } } @@ -4113,4 +4193,4 @@ pub fn display_val( a => display_val(a, full_context, globals, info)?, }, ) -} +} \ No newline at end of file diff --git a/compiler/src/value_storage.rs b/compiler/src/value_storage.rs index d5fbb6d9..36d3a373 100644 --- a/compiler/src/value_storage.rs +++ b/compiler/src/value_storage.rs @@ -70,7 +70,7 @@ impl ValStorage { self.mark(*e) } } - Value::Macro(m) => { + Value::Macro(Macro::FuncLike(m)) => { for MacroArgDef { default, pattern, .. } in m.args @@ -249,7 +249,7 @@ pub fn clone_and_get_value( ); } - Value::Macro(m) => { + Value::Macro(Macro::FuncLike(m)) => { for arg in &mut m.args { if let Some(def_val) = &mut arg.default { (*def_val) = clone_value_preserve_area(*def_val, globals, fn_context, constant); diff --git a/docgen/src/documentation.rs b/docgen/src/documentation.rs index c69c362d..8ad748c4 100644 --- a/docgen/src/documentation.rs +++ b/docgen/src/documentation.rs @@ -272,7 +272,7 @@ fn document_dict( ]; for (name, x) in dict { - if let Value::Macro(m) = &globals.stored_values[*x] { + if let Value::Macro(Macro::FuncLike(m)) = &globals.stored_values[*x] { if m.tag.get("constructor").is_some() { categories[0].1.push((*name, *x)); } else if name.starts_with('_') && name.ends_with('_') { @@ -349,7 +349,7 @@ fn document_dict( } fn document_macro( - mac: &Macro, + mac: &MacroFuncData, globals: &mut Globals, full_context: &mut FullContext, type_links: &AHashMap, @@ -576,7 +576,7 @@ fn document_val( let (new_doc, sidebar) = &match &val { Value::Dict(d) => document_dict(d, globals, full_context, type_links, path, tn)?, - Value::Macro(m) => ( + Value::Macro(Macro::FuncLike(m)) => ( document_macro(m, globals, full_context, type_links)?, String::new(), ), diff --git a/libraries/std/array.spwn b/libraries/std/array.spwn index e90842da..c8cf1e8d 100644 --- a/libraries/std/array.spwn +++ b/libraries/std/array.spwn @@ -274,12 +274,24 @@ $.assert(arr.index_all(2) == [2,4]) return $.remove_index(self, index) }, - remove: #[desc("Returns array without provided with all elements that match value removed"), example(u" - let arr = [1, 2, 3, 4, 5] - $.assert(arr.remove(3) == [1, 2, 4, 5]) - ")] + remove: #[desc("Returns the array with the elements at the provided index or indices removed"), example(u' + let arr = ["a", "b", "c"] + $.assert(arr.remove(1) == ["a", "c"]) + + let arr = ["a", "b", "c", "d", "e"] + $.assert(arr.remove([1, 4]) == ["a", "c", "d"]) + ')] + (self, index: @number | [@number]) -> @array { + matches = (i => i == index) if index is @number else (i => i in index) + return self.filter((_, i) => !matches(i)) + }, + + erase: #[desc("Returns the array with the elements that match the value provided removed"), example(u' + let arr = ["a", "b", "c"] + $.assert(arr.erase("c") == ["a", "b"]) + ')] (self, value) -> @array { - return self.map(x => x if x != value else @NULL::{}).filter(el => el != @NULL::{}) + return self.filter(el => el != value) }, map: #[desc("Calls a defined callback function on each element of an array, and returns an array that contains the results, or modifies in place if specified."), example(u" @@ -312,7 +324,7 @@ $.assert(arr.index_all(2) == [2,4]) $.assert(arr2.filter(@bool) == [true, false]) ")] - (self, cb: (_ -> @bool) | @pattern | @type_indicator) -> @array | @NULL { + (self, cb: @macro) -> @array | @NULL { let output = []; for index in 0..self.length { value = self[index] @@ -339,7 +351,7 @@ $.assert(arr.index_all(2) == [2,4]) product = arr2.reduce((acum, el) => acum * el, 1) $.assert(product == 150) ")] - (self, cb: ((_, _) -> _) | @builtin, default = 0) -> _ { + (self, cb: ((_, _) -> _), default = 0) -> _ { let acum = default; for iter in self { acum = cb(acum, iter); @@ -357,7 +369,7 @@ $.assert(arr.index_all(2) == [2,4]) result = arr2.l_fold($._divided_by_) $.assert(result == 0.4) ")] - (self, cb: ((_, _) -> _) | @builtin) -> _ { + (self, cb: ((_, _) -> _)) -> _ { let acum = self[0]; for iter in 1..self.length { acum = cb(acum, self[iter]); @@ -375,7 +387,7 @@ $.assert(arr.index_all(2) == [2,4]) result = arr2.r_fold($._divided_by_) $.assert(result == 10) ")] - (self, cb: ((_, _) -> _) | @builtin) -> _ { + (self, cb: ((_, _) -> _)) -> _ { let acum = self[-1]; for iter in (self.length - 1)..0 { acum = cb(self[iter], acum); diff --git a/libraries/std/lib.spwn b/libraries/std/lib.spwn index 6b7359de..ebaecf57 100644 --- a/libraries/std/lib.spwn +++ b/libraries/std/lib.spwn @@ -8,6 +8,7 @@ import "color.spwn" import "item.spwn" import "block.spwn" import "array.spwn" +import "macro.spwn" import "object.spwn" import "dictionary.spwn" import "string.spwn" diff --git a/libraries/std/macro.spwn b/libraries/std/macro.spwn new file mode 100644 index 00000000..5df4bbf2 --- /dev/null +++ b/libraries/std/macro.spwn @@ -0,0 +1,12 @@ +#[no_std, cache_output] + +impl @macro { + call: (self, args: @array) { + return $.call(self, args) + }, + partial_call: (self, args: @array) { + pass_args = args[:self.args.length] + [null] * (self.args.length - args.length) + return $.call(self, pass_args) + }, +} + diff --git a/parser/src/ast.rs b/parser/src/ast.rs index fe18fe45..336175bd 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -188,6 +188,13 @@ pub enum Operator { Exponate, Modulate, Swap, + + RightShift, + LeftShift, + BitXor, + + LeftShiftAssign, + RightShiftAssign } #[derive(Clone, PartialEq, Debug)] @@ -204,6 +211,7 @@ pub enum UnaryOperator { MoreOrEqPattern, LessOrEqPattern, InPattern, + BitNot, } #[derive(Clone, PartialEq, Debug)] @@ -516,4 +524,4 @@ pub struct Id { pub number: u16, pub unspecified: bool, pub class_name: IdClass, -} +} \ No newline at end of file diff --git a/parser/src/fmt.rs b/parser/src/fmt.rs index 44c1dd35..2b0ba388 100644 --- a/parser/src/fmt.rs +++ b/parser/src/fmt.rs @@ -475,6 +475,11 @@ impl SpwnFmt for Operator { Operator::Modulate => "%=", Operator::Swap => "<=>", Operator::Is => "is", + Operator::RightShift => ">>", + Operator::LeftShift => "<<", + Operator::BitXor => "~?", + Operator::LeftShiftAssign => "<<=", + Operator::RightShiftAssign => ">>=" } .to_string() } @@ -494,6 +499,7 @@ impl SpwnFmt for UnaryOperator { UnaryOperator::MoreOrEqPattern => ">=", UnaryOperator::LessOrEqPattern => "<=", UnaryOperator::InPattern => "in", + UnaryOperator::BitNot => "~", } .to_string() } diff --git a/parser/src/parser.rs b/parser/src/parser.rs index e0aa4dc3..b9f9c30e 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -304,6 +304,24 @@ pub enum Token { #[token("sync")] Sync, + #[token(">>")] + RightShift, + + #[token("<<")] + LeftShift, + + #[token("~")] + BitNot, + + #[token("~?")] + BitXor, + + #[token(">>=")] + RightShiftAssign, + + #[token("<<=")] + LeftShiftAssign, + //STATEMENT SEPARATOR #[regex(r"[\n\r;]+")] StatementSeparator, @@ -311,6 +329,8 @@ pub enum Token { #[error] #[regex(r"[ \t\f]+|/\*[^*]*\*(([^/\*][^\*]*)?\*)*/|//[^\n]*", logos::skip)] Error, + + } impl Token { @@ -320,7 +340,8 @@ impl Token { Or | And | Equal | NotEqual | MoreOrEqual | LessOrEqual | MoreThan | LessThan | Star | Modulo | Power | Plus | Minus | Slash | Exclamation | Assign | Add | Subtract | Multiply | Divide | IntDividedBy | IntDivide | As | In | Either - | Ampersand | DoubleStar | Exponate | Modulate | Increment | Decrement | Swap | Is => { + | Ampersand | DoubleStar | Exponate | Modulate | Increment | Decrement | Swap | Is + | RightShift | LeftShift | BitNot | BitXor | LeftShiftAssign | RightShiftAssign => { "operator" } Symbol => "identifier", @@ -344,7 +365,6 @@ impl Token { } } } - pub struct ParseNotes { pub tag: ast::Attribute, pub file: SpwnSource, @@ -916,7 +936,6 @@ pub fn parse_statement( /* You might be asking yourself here, "why are we parsing it as a variable and not a type?" - Well the answer to that is simply that the developer thought that some people might not like the typing system and would want to use a variable instead. @@ -1043,7 +1062,7 @@ macro_rules! op_precedence { op_precedence! { // make sure the highest precedence is at the top 12, Left => As, 11, Left => Both, - 10, Left => Either, + 10, Left => Either LeftShift RightShift BitXor, 9, Right => Power, 8, Left => Modulo Star Slash IntDividedBy, 7, Left => Plus Minus, @@ -1053,7 +1072,7 @@ op_precedence! { // make sure the highest precedence is at the top 3, Left => Is NotEqual In Equal, 2, Left => And, 1, Left => Or, - 0, Right => Assign Add Subtract Multiply Divide IntDivide Exponate Modulate Swap, + 0, Right => Assign Add Subtract Multiply Divide IntDivide Exponate Modulate Swap LeftShiftAssign RightShiftAssign, } fn fix_precedence(mut expr: ast::Expression) -> ast::Expression { @@ -1449,6 +1468,11 @@ fn parse_operator(token: &Token) -> Option { Token::In => Some(ast::Operator::In), Token::As => Some(ast::Operator::As), Token::Is => Some(ast::Operator::Is), + Token::RightShift => Some(ast::Operator::RightShift), + Token::LeftShift => Some(ast::Operator::LeftShift), + Token::BitXor => Some(ast::Operator::BitXor), + Token::LeftShiftAssign => Some(ast::Operator::LeftShiftAssign), + Token::RightShiftAssign => Some(ast::Operator::RightShiftAssign), _ => None, } } @@ -2352,6 +2376,12 @@ fn parse_variable( let operator = match first_token { // does it start with an op? (e.g -3, let i) + + Some(Token::BitNot) => { + first_token = tokens.next(false); + Some(ast::UnaryOperator::BitNot) + } + Some(Token::Minus) => { first_token = tokens.next(false); Some(ast::UnaryOperator::Minus) @@ -3048,4 +3078,4 @@ fn parse_variable( }*/ Ok(val) -} +} \ No newline at end of file diff --git a/test/test.spwn b/test/test.spwn index cd06ed53..3bfebac9 100644 --- a/test/test.spwn +++ b/test/test.spwn @@ -1,13 +1,6 @@ -#[no_level, no_std] +#[no_level] -let lol = { - a: 3, - bebe: "shit", -} +$.print([0, 1, 2].reduce($._plus_)) -$.print(lol is { - a: @number, - bebe: @string, -})