diff --git a/packages/server/src/pre/interpreter.rs b/packages/server/src/pre/interpreter.rs index 702ab931..9d7f52fd 100644 --- a/packages/server/src/pre/interpreter.rs +++ b/packages/server/src/pre/interpreter.rs @@ -464,17 +464,67 @@ impl Interpreter { fn eval_binary_bitwise_expr( &self, - _lhs: Value, - _op: BinBitwise, - _rhs: Value, + lhs: Value, + op: BinBitwise, + rhs: Value, op_token: &Token, ) -> gramatika::Result { - // TODO - Err(SpannedError { - message: "Bitwise operations not currently supported by the pre-parser".into(), - source: self.source.clone(), - span: Some(op_token.span()), - }) + use BinBitwise::*; + use Value::*; + + match (lhs, op, rhs) { + (Bool(_), _, _) | (_, _, Bool(_)) => Err(SpannedError { + message: format!("Cannot use operator {op_token} to combine booleans"), + source: self.source.clone(), + span: Some(op_token.span()), + }), + // Happy Path + // | + (Uint(lhs), Or, Uint(rhs)) => Ok(Uint(lhs | rhs)), + (Int(lhs), Or, Int(rhs)) => Ok(Int(lhs | rhs)), + // ^ + (Uint(lhs), Xor, Uint(rhs)) => Ok(Uint(lhs ^ rhs)), + (Int(lhs), Xor, Int(rhs)) => Ok(Int(lhs ^ rhs)), + // & + (Uint(lhs), And, Uint(rhs)) => Ok(Uint(lhs & rhs)), + (Int(lhs), And, Int(rhs)) => Ok(Int(lhs & rhs)), + // << + (Uint(lhs), LShift, Uint(rhs)) => Ok(Uint(lhs << rhs)), + (Int(lhs), LShift, Int(rhs)) => Ok(Int(lhs << rhs)), + // >> + (Uint(lhs), RShift, Uint(rhs)) => Ok(Uint(lhs >> rhs)), + (Int(lhs), RShift, Int(rhs)) => Ok(Int(lhs >> rhs)), + // Coercion + (Float(lhs), op, rhs) if lhs.fract().abs() == 0.0 => { + let lhs = Int(lhs as isize); + self.eval_binary_bitwise_expr(lhs, op, rhs, op_token) + } + (lhs, op, Float(rhs)) if rhs.fract().abs() == 0.0 => { + let rhs = Int(rhs as isize); + self.eval_binary_bitwise_expr(lhs, op, rhs, op_token) + } + (Float(_), _, _) | (_, _, Float(_)) => Err(SpannedError { + message: "Cannot perform bitwise operations on floats with fractional parts".into(), + source: self.source.clone(), + span: Some(op_token.span()), + }), + (Int(lhs), op, Uint(rhs)) if lhs >= 0 => { + let lhs = Uint(lhs as usize); + self.eval_binary_bitwise_expr(lhs, op, Uint(rhs), op_token) + } + (Int(lhs), op, Uint(rhs)) => { + let rhs = Int(rhs as isize); + self.eval_binary_bitwise_expr(Int(lhs), op, rhs, op_token) + } + (Uint(lhs), op, Int(rhs)) if rhs >= 0 => { + let rhs = Uint(rhs as usize); + self.eval_binary_bitwise_expr(Uint(lhs), op, rhs, op_token) + } + (Uint(lhs), op, Int(rhs)) => { + let lhs = Int(lhs as isize); + self.eval_binary_bitwise_expr(lhs, op, Int(rhs), op_token) + } + } } } @@ -539,33 +589,72 @@ impl Visitor for Interpreter { let lexeme = token.lexeme(); let result = match token.kind() { IntLiteral if lexeme.ends_with('u') => { - match &lexeme[..lexeme.len() - 1].parse::() { - Ok(value) => Ok(Value::Uint(*value)), - Err(err) => Err(SpannedError { - message: format!("{err}"), - source: self.source.clone(), - span: Some(token.span()), - }), + let lex = lexeme.substr(..lexeme.len() - 1); + + if let Some(s) = lex.strip_prefix("0x") { + match usize::from_str_radix(s, 16) { + Ok(value) => Ok(Value::Uint(value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } + } else { + match lex.parse::() { + Ok(value) => Ok(Value::Uint(value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } } } IntLiteral if lexeme.ends_with('i') => { - match &lexeme[..lexeme.len() - 1].parse::() { - Ok(value) => Ok(Value::Int(*value)), - Err(err) => Err(SpannedError { - message: format!("{err}"), - source: self.source.clone(), - span: Some(token.span()), - }), + let lex = lexeme.substr(..lexeme.len() - 1); + + if let Some(s) = lex.strip_prefix("0x") { + match isize::from_str_radix(s, 16) { + Ok(value) => Ok(Value::Int(value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } + } else { + match &lexeme[..lexeme.len() - 1].parse::() { + Ok(value) => Ok(Value::Int(*value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } + } + } + IntLiteral => { + if let Some(s) = lexeme.strip_prefix("0x") { + match usize::from_str_radix(s, 16) { + Ok(value) => Ok(Value::Uint(value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } + } else { + match lexeme.parse::() { + Ok(value) => Ok(Value::Uint(value)), + Err(err) => Err(SpannedError { + message: format!("{err}"), + source: self.source.clone(), + span: Some(token.span()), + }), + } } } - IntLiteral => match lexeme.parse::() { - Ok(value) => Ok(Value::Uint(value)), - Err(err) => Err(SpannedError { - message: format!("{err}"), - source: self.source.clone(), - span: Some(token.span()), - }), - }, FloatLiteral if lexeme.ends_with('f') || lexeme.ends_with('h') => { match &lexeme[..lexeme.len() - 1].parse::() { Ok(value) => Ok(Value::Float(*value)), @@ -884,6 +973,24 @@ mod tests { snapshot!("{error}"); } + #[snapshot_test] + fn eval_binary_bitwise_expressions() { + let defs = defs! { + "FLAGS": "0x11", + "FLAG_A": "0x01", + "FLAG_B": "0x10", + }; + + assert_matches!( + eval!("(FLAGS & FLAG_A) && (FLAGS & FLAG_B)", defs.clone()), + Ok(Value::Bool(true)), + ); + assert_matches!( + eval!("(FLAG_A | FLAG_B) == FLAGS", defs), + Ok(Value::Bool(true)), + ); + } + mod eval_binary_expr { use super::*;