Skip to content

Commit

Permalink
Evaluate bitwise binary epxressions
Browse files Browse the repository at this point in the history
  • Loading branch information
dannymcgee committed May 16, 2024
1 parent 5b606eb commit 449071e
Showing 1 changed file with 138 additions and 31 deletions.
169 changes: 138 additions & 31 deletions packages/server/src/pre/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value> {
// 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)
}
}
}
}

Expand Down Expand Up @@ -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::<usize>() {
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::<usize>() {
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::<isize>() {
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::<isize>() {
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::<usize>() {
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::<usize>() {
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::<f32>() {
Ok(value) => Ok(Value::Float(*value)),
Expand Down Expand Up @@ -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::*;

Expand Down

0 comments on commit 449071e

Please sign in to comment.