diff --git a/src/compiling/bytecode.rs b/src/compiling/bytecode.rs index 9514df1d..93a6bffb 100644 --- a/src/compiling/bytecode.rs +++ b/src/compiling/bytecode.rs @@ -1094,6 +1094,19 @@ impl<'a> FuncBuilder<'a> { ) } + pub fn has_op( + &mut self, + left: UnoptRegister, + right: UnoptRegister, + dest: UnoptRegister, + span: CodeSpan, + ) { + self.push_opcode_spanned( + ProtoOpcode::Raw(UnoptOpcode::Has { left, right, dest }), + span, + ) + } + pub fn as_op( &mut self, left: UnoptRegister, diff --git a/src/compiling/compiler.rs b/src/compiling/compiler.rs index ce5baf82..64191ddd 100644 --- a/src/compiling/compiler.rs +++ b/src/compiling/compiler.rs @@ -1246,6 +1246,9 @@ impl<'a> Compiler<'a> { BinOp::In => { bin_op!(left in_op right) }, + BinOp::Has => { + bin_op!(left has_op right) + } BinOp::As => { bin_op!(left as_op right) }, diff --git a/src/lexing/tokens.rs b/src/lexing/tokens.rs index 304325c1..8edab23b 100644 --- a/src/lexing/tokens.rs +++ b/src/lexing/tokens.rs @@ -23,6 +23,7 @@ lexer! { While: text("while"), For: text("for"), In: text("in"), + Has: text("has"), Try: text("try"), Catch: text("catch"), Throw: text("throw"), @@ -166,6 +167,7 @@ impl Token { Self::While => "while", Self::For => "for", Self::In => "in", + Self::Has => "has", Self::Try => "try", Self::Catch => "catch", Self::Return => "return", diff --git a/src/parsing/mod.rs b/src/parsing/mod.rs index 0b45d8b7..5e63ecd3 100644 --- a/src/parsing/mod.rs +++ b/src/parsing/mod.rs @@ -270,6 +270,7 @@ mod tests { match BinOp::And { BinOp::Range => (), BinOp::In => (), + BinOp::Has => (), BinOp::BinOr => (), BinOp::Or => (), BinOp::BinAnd => (), @@ -312,6 +313,12 @@ mod tests { Ex::Op(Ex::Float(1.2).into(), BinOp::In, Ex::Float(2.2).into()) ); + let t = parse("6.9 has 4.20")?; + expr_eq!( + t, + Ex::Op(Ex::Float(6.9).into(), BinOp::Has, Ex::Float(4.2).into()) + ); + let t = parse("0b01 | 0b10")?; expr_eq!( t, @@ -463,7 +470,20 @@ mod tests { let t = parse(r#"[10,]"#)?; expr_eq!(t, Ex::Array(vec![Ex::Int(10).into(),])); - let t = parse(r#"[10, a, true, "aa", 1.2, @a, [1, 2], a in b, !false]"#)?; + let t = parse(r#" + [ + 10, + a, + true, + "aa", + 1.2, + @a, + [1, 2], + a in b, + !false, + {} has "hi", + ] + "#)?; expr_eq!( t, Ex::Array(vec![ @@ -481,6 +501,11 @@ mod tests { ) .into(), Ex::Unary(UnaryOp::ExclMark, Ex::Bool(false).into()).into(), + Ex::Op( + Ex::Dict(vec![]).into(), + BinOp::Has, + string!("hi").into(), + ).into(), ]) ); diff --git a/src/parsing/utils.rs b/src/parsing/utils.rs index 66d80582..51fc3dd2 100644 --- a/src/parsing/utils.rs +++ b/src/parsing/utils.rs @@ -170,7 +170,7 @@ operators! { // Right => [Assign]; // Right => [PlusEq, MinusEq, MultEq, DivEq, PowEq, ModEq, BinAndEq, BinOrEq, BinNotEq, ShiftLeftEq, ShiftRightEq]; Left => [Range]; - Left => [In]; + Left => [In, Has]; // Left => [Is]; Left => [BinOr, Or]; Left => [BinAnd, And]; diff --git a/src/vm/interpreter.rs b/src/vm/interpreter.rs index 6755e12e..200a54d8 100644 --- a/src/vm/interpreter.rs +++ b/src/vm/interpreter.rs @@ -740,6 +740,9 @@ impl<'a> Vm<'a> { Opcode::In { left, right, dest } => { self.bin_op(vo::in_op, func, ip, left, right, dest, BinOp::In)? }, + Opcode::Has { left, right, dest } => { + self.bin_op(vo::has_op, func, ip, left, right, dest, BinOp::Has)? + } Opcode::As { left, right, dest } => { let span = self.get_span(func, ip); diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index 3b364bb8..920000c4 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -263,6 +263,8 @@ opcodes! { Range { => left, => right, => dest }, #[delve(display = |a: &R, b: &R, x: &R| format!("R{a} in R{b} -> R{x}"))] In { => left, => right, => dest }, + #[delve(display = |a: &R, b: &R, x: &R| format!("R{a} has R{b} -> R{x}"))] + Has { => left, => right, => dest }, #[delve(display = |a: &R, b: &R, x: &R| format!("R{a} as R{b} -> R{x}"))] As { => left, => right, => dest }, // #[delve(display = |a: &R, b: &R, x: &R| format!("R{a} is R{b} -> R{x}"))] diff --git a/src/vm/value_ops.rs b/src/vm/value_ops.rs index 78060a50..459ec0d2 100644 --- a/src/vm/value_ops.rs +++ b/src/vm/value_ops.rs @@ -161,6 +161,34 @@ pub fn in_op( }) } +pub fn has_op( + a: &StoredValue, + b: &StoredValue, + span: CodeSpan, + vm: &Vm, + code: BytecodeKey, +) -> RuntimeResult { + Ok(match (&a.value, &b.value) { + (Value::Array(a), b) => { + todo!("I want to give an useful error instead of just spamming random text") // it's true + }, + + (Value::Dict(d), Value::String(s)) => { + Value::Bool(d.contains_key(&vm.intern(&s.iter().collect::()))) + }, + + _ => { + return Err(RuntimeError::InvalidOperands { + op: BinOp::Has, + a: (a.value.get_type(), a.area.clone()), + b: (b.value.get_type(), b.area.clone()), + area: vm.make_area(span, code), + call_stack: vm.get_call_stack(), + }) + }, + }) +} + pub fn add( a: &StoredValue, b: &StoredValue,