Merge pull request #145 from chriseth/xor_and_match

Xor and match
This commit is contained in:
chriseth
2023-04-06 13:39:12 +02:00
committed by GitHub
12 changed files with 251 additions and 104 deletions

View File

@@ -47,6 +47,19 @@ impl Display for Expression {
Expression::UnaryOperation(op, exp) => write!(f, "{op}{exp}"),
Expression::FunctionCall(fun, args) => write!(f, "{fun}({})", format_expressions(args)),
Expression::LocalVariableReference(index) => write!(f, "${index}"),
Expression::MatchExpression(scrutinee, arms) => write!(
f,
"match {scrutinee} {{ {} }}",
arms.iter()
.map(|(n, e)| format!(
"{} => {e},",
n.as_ref()
.map(|n| n.to_string())
.unwrap_or_else(|| "_".to_string())
))
.collect::<Vec<_>>()
.join(" ")
),
}
}
}

View File

@@ -159,6 +159,10 @@ pub enum Expression {
UnaryOperation(UnaryOperator, Box<Expression>),
/// Call to a non-macro function (like a constant polynomial)
FunctionCall(String, Vec<Expression>),
MatchExpression(
Box<Expression>,
Vec<(Option<AbstractNumberType>, Expression)>,
),
}
#[derive(Debug, PartialEq, Eq, Default, Clone)]

View File

@@ -508,6 +508,12 @@ impl PILContext {
ast::Expression::FunctionCall(name, arguments) => {
Expression::FunctionCall(self.namespaced(name), self.process_expressions(arguments))
}
ast::Expression::MatchExpression(scrutinee, arms) => Expression::MatchExpression(
Box::new(self.process_expression(scrutinee)),
arms.iter()
.map(|(n, e)| (n.clone(), self.process_expression(e)))
.collect(),
),
ast::Expression::FreeInput(_) => panic!(),
}
}
@@ -576,6 +582,7 @@ impl PILContext {
ast::Expression::UnaryOperation(op, value) => self.evaluate_unary_operation(op, value),
ast::Expression::FunctionCall(_, _) => None,
ast::Expression::FreeInput(_) => panic!(),
ast::Expression::MatchExpression(_, _) => None,
}
}
@@ -600,6 +607,7 @@ impl PILContext {
}
BinaryOperator::Mod => left % right,
BinaryOperator::BinaryAnd => left & right,
BinaryOperator::BinaryXor => left ^ right,
BinaryOperator::BinaryOr => left | right,
BinaryOperator::ShiftLeft => left << abstract_to_degree(&right),
BinaryOperator::ShiftRight => left >> abstract_to_degree(&right),

View File

@@ -46,7 +46,7 @@ impl ASMPILConverter {
fn convert(&mut self, input: ASMFile) -> PILFile {
self.set_degree(1024);
let mut statements = input.0.iter().peekable();
let mut statements = input.0.into_iter().peekable();
if let Some(ASMStatement::Degree(_, degree)) = statements.peek() {
self.set_degree(crate::number::abstract_to_degree(degree));
@@ -72,27 +72,25 @@ impl ASMPILConverter {
panic!("The degree statement is only supported at the start of the asm source");
}
ASMStatement::RegisterDeclaration(start, name, flags) => {
self.handle_register_declaration(flags, name, start);
self.handle_register_declaration(flags, &name, start);
}
ASMStatement::InstructionDeclaration(start, name, params, body) => {
self.handle_instruction_def(start, body, name, params);
}
ASMStatement::InlinePil(_start, statements) => self.pil.extend(statements.clone()),
ASMStatement::Assignment(start, write_regs, assign_reg, value) => {
match value.as_ref() {
Expression::FunctionCall(function_name, args) => {
self.handle_functional_instruction(
write_regs,
assign_reg,
function_name,
args,
);
}
_ => {
self.handle_assignment(*start, write_regs, assign_reg, value.as_ref());
}
ASMStatement::Assignment(start, write_regs, assign_reg, value) => match *value {
Expression::FunctionCall(function_name, args) => {
self.handle_functional_instruction(
write_regs,
assign_reg,
function_name,
args,
);
}
}
_ => {
self.handle_assignment(start, write_regs, assign_reg, *value);
}
},
ASMStatement::Instruction(_start, instr_name, args) => {
self.handle_instruction(instr_name, args)
}
@@ -104,7 +102,7 @@ impl ASMPILConverter {
}
let assignment_registers = self.assignment_registers().cloned().collect::<Vec<_>>();
for reg in assignment_registers {
self.create_constraints_for_assignment_reg(&reg);
self.create_constraints_for_assignment_reg(reg);
}
self.pil.extend(
@@ -143,9 +141,9 @@ impl ASMPILConverter {
fn handle_register_declaration(
&mut self,
flags: &Option<RegisterFlag>,
flags: Option<RegisterFlag>,
name: &str,
start: &usize,
start: usize,
) {
let mut conditioned_updates = vec![];
let mut default_update = None;
@@ -158,7 +156,7 @@ impl ASMPILConverter {
// This might be superfluous but makes it easier to determine that the PC needs to
// be zero in the first row.
self.pil.push(Statement::PolynomialIdentity(
*start,
start,
build_mul(direct_reference("first_step"), direct_reference(name)),
));
// The value here is actually irrelevant, it is only important
@@ -173,7 +171,7 @@ impl ASMPILConverter {
// This might be superfluous but makes it easier to determine that the register needs to
// be zero in the first row.
self.pil.push(Statement::PolynomialIdentity(
*start,
start,
build_mul(direct_reference("first_step"), direct_reference(name)),
));
conditioned_updates = vec![
@@ -185,7 +183,7 @@ impl ASMPILConverter {
// TODO do this at the same place where we set up the read flags.
for reg in assignment_regs {
let write_flag = format!("reg_write_{reg}_{name}");
self.create_witness_fixed_pair(*start, &write_flag);
self.create_witness_fixed_pair(start, &write_flag);
conditioned_updates
.push((direct_reference(&write_flag), direct_reference(&reg)));
}
@@ -197,30 +195,30 @@ impl ASMPILConverter {
Register {
conditioned_updates,
default_update,
is_assignment: *flags == Some(RegisterFlag::IsAssignment),
is_assignment: flags == Some(RegisterFlag::IsAssignment),
},
);
self.pil.push(witness_column(*start, name, None));
self.pil.push(witness_column(start, name, None));
}
fn handle_instruction_def(
&mut self,
start: &usize,
body: &Vec<InstructionBodyElement>,
name: &str,
params: &Vec<InstructionParam>,
start: usize,
body: Vec<InstructionBodyElement>,
name: String,
params: Vec<InstructionParam>,
) {
let instruction_flag = format!("instr_{name}");
self.create_witness_fixed_pair(*start, &instruction_flag);
self.create_witness_fixed_pair(start, &instruction_flag);
// it's part of the lookup!
//self.pil.push(constrain_zero_one(&col_name));
let mut substitutions = HashMap::new();
for p in params {
for p in &params {
if p.assignment_reg.0.is_none() && p.assignment_reg.1.is_none() {
// literal argument
let param_col_name = format!("instr_{name}_param_{}", p.name);
self.create_witness_fixed_pair(*start, &param_col_name);
self.create_witness_fixed_pair(start, &param_col_name);
substitutions.insert(p.name.clone(), param_col_name);
}
}
@@ -247,40 +245,36 @@ impl ASMPILConverter {
assert!(left.selector.is_none(), "LHS selector not supported, could and-combine with instruction flag later.");
let left = SelectedExpressions {
selector: Some(direct_reference(&instruction_flag)),
expressions: substitute_vec(&left.expressions, &substitutions),
expressions: substitute_vec(left.expressions, &substitutions),
};
let right = substitute_selected_exprs(right, &substitutions);
self.pil.push(match op {
PlookupOperator::In => Statement::PlookupIdentity(*start, left, right),
PlookupOperator::Is => Statement::PermutationIdentity(*start, left, right),
PlookupOperator::In => Statement::PlookupIdentity(start, left, right),
PlookupOperator::Is => Statement::PermutationIdentity(start, left, right),
})
}
}
}
let instr = Instruction {
params: params.clone(),
};
self.instructions.insert(name.to_string(), instr);
let instr = Instruction { params };
self.instructions.insert(name, instr);
}
fn handle_assignment(
&mut self,
_start: usize,
write_regs: &Vec<String>,
assign_reg: &Option<String>,
value: &Expression,
write_regs: Vec<String>,
assign_reg: Option<String>,
value: Expression,
) {
assert!(write_regs.len() <= 1);
assert!(
assign_reg.is_some(),
"Implicit assign register not yet supported."
);
let assign_reg = assign_reg.clone().unwrap();
let assign_reg = assign_reg.unwrap();
let value = self.process_assignment_value(value);
self.code_lines.push(CodeLine {
write_regs: [(assign_reg.clone(), write_regs.clone())]
.into_iter()
.collect(),
write_regs: [(assign_reg.clone(), write_regs)].into_iter().collect(),
value: [(assign_reg, value)].into(),
..Default::default()
})
@@ -288,27 +282,27 @@ impl ASMPILConverter {
fn handle_functional_instruction(
&mut self,
write_regs: &Vec<String>,
assign_reg: &Option<String>,
instr_name: &str,
args: &Vec<Expression>,
write_regs: Vec<String>,
assign_reg: Option<String>,
instr_name: String,
args: Vec<Expression>,
) {
assert!(write_regs.len() == 1);
assert!(assign_reg.is_some());
let instr = &self.instructions[instr_name];
let instr = &self.instructions[&instr_name];
assert_eq!(instr.params.len(), args.len() + 1);
let last_param = instr.params.last().unwrap();
assert!(last_param.param_type.is_none());
assert!(last_param.assignment_reg.0.is_none());
assert!(last_param.assignment_reg.1 == Some(assign_reg.clone()));
assert!(last_param.assignment_reg.1 == Some(assign_reg));
let mut args = args.clone();
args.push(direct_reference(write_regs.first().unwrap()));
self.handle_instruction(instr_name, &args);
let mut args = args;
args.push(direct_reference(write_regs.first().unwrap().clone()));
self.handle_instruction(instr_name, args);
}
fn handle_instruction(&mut self, instr_name: &str, args: &Vec<Expression>) {
let instr = &self.instructions[instr_name];
fn handle_instruction(&mut self, instr_name: String, args: Vec<Expression>) {
let instr = &self.instructions[&instr_name];
assert_eq!(instr.params.len(), args.len());
let mut value = BTreeMap::new();
let mut instruction_literal_args = vec![];
@@ -357,7 +351,7 @@ impl ASMPILConverter {
fn process_assignment_value(
&self,
value: &Expression,
value: Expression,
) -> Vec<(AbstractNumberType, AffineExpressionComponent)> {
match value {
Expression::Constant(_) => panic!(),
@@ -370,30 +364,28 @@ impl ASMPILConverter {
// TODO check it actually is a register
vec![(
1.into(),
AffineExpressionComponent::Register(reference.name.clone()),
AffineExpressionComponent::Register(reference.name),
)]
}
Expression::Number(value) => vec![(value.clone(), AffineExpressionComponent::Constant)],
Expression::Number(value) => vec![(value, AffineExpressionComponent::Constant)],
Expression::String(_) => panic!(),
Expression::Tuple(_) => panic!(),
Expression::MatchExpression(_, _) => panic!(),
Expression::FreeInput(expr) => {
vec![(
1.into(),
AffineExpressionComponent::FreeInput(*expr.clone()),
)]
vec![(1.into(), AffineExpressionComponent::FreeInput(*expr))]
}
Expression::BinaryOperation(left, op, right) => match op {
BinaryOperator::Add => self.add_assignment_value(
self.process_assignment_value(left),
self.process_assignment_value(right),
self.process_assignment_value(*left),
self.process_assignment_value(*right),
),
BinaryOperator::Sub => self.add_assignment_value(
self.process_assignment_value(left),
self.negate_assignment_value(self.process_assignment_value(right)),
self.process_assignment_value(*left),
self.negate_assignment_value(self.process_assignment_value(*right)),
),
BinaryOperator::Mul => {
let left = self.process_assignment_value(left);
let right = self.process_assignment_value(right);
let left = self.process_assignment_value(*left);
let right = self.process_assignment_value(*right);
if let [(f, AffineExpressionComponent::Constant)] = &left[..] {
// TODO overflow?
right
@@ -409,17 +401,18 @@ impl ASMPILConverter {
panic!("Multiplication by non-constant.");
}
}
BinaryOperator::Div => panic!(),
BinaryOperator::Mod => panic!(),
BinaryOperator::Pow => panic!(),
BinaryOperator::BinaryAnd => panic!(),
BinaryOperator::BinaryOr => panic!(),
BinaryOperator::ShiftLeft => panic!(),
BinaryOperator::ShiftRight => panic!(),
BinaryOperator::Div
| BinaryOperator::Mod
| BinaryOperator::Pow
| BinaryOperator::BinaryAnd
| BinaryOperator::BinaryXor
| BinaryOperator::BinaryOr
| BinaryOperator::ShiftLeft
| BinaryOperator::ShiftRight => panic!(),
},
Expression::UnaryOperation(op, expr) => {
assert!(*op == UnaryOperator::Minus);
self.negate_assignment_value(self.process_assignment_value(expr))
assert!(op == UnaryOperator::Minus);
self.negate_assignment_value(self.process_assignment_value(*expr))
}
}
}
@@ -441,7 +434,7 @@ impl ASMPILConverter {
expr.into_iter().map(|(v, c)| (-v, c)).collect()
}
fn create_constraints_for_assignment_reg(&mut self, register: &str) {
fn create_constraints_for_assignment_reg(&mut self, register: String) {
let assign_const = format!("{register}_const");
self.create_witness_fixed_pair(0, &assign_const);
let read_free = format!("{register}_read_free");
@@ -453,11 +446,14 @@ impl ASMPILConverter {
.map(|name| {
let read_coefficient = format!("read_{register}_{name}");
self.create_witness_fixed_pair(0, &read_coefficient);
build_mul(direct_reference(&read_coefficient), direct_reference(name))
build_mul(
direct_reference(read_coefficient),
direct_reference(name.clone()),
)
})
.chain([
direct_reference(&assign_const),
build_mul(direct_reference(&read_free), direct_reference(&free_value)),
direct_reference(assign_const),
build_mul(direct_reference(read_free), direct_reference(free_value)),
])
.reduce(build_add);
self.pil.push(Statement::PolynomialIdentity(
@@ -571,7 +567,7 @@ impl ASMPILConverter {
let free_value = format!("{reg}_free_value");
witness_column(
0,
&free_value,
free_value,
Some(FunctionDefinition::Query(
vec!["i".to_string()],
Expression::Tuple(free_value_queries[reg].clone()),
@@ -690,21 +686,25 @@ enum AffineExpressionComponent {
FreeInput(Expression),
}
fn witness_column(start: usize, name: &str, def: Option<FunctionDefinition>) -> Statement {
fn witness_column<S: Into<String>>(
start: usize,
name: S,
def: Option<FunctionDefinition>,
) -> Statement {
Statement::PolynomialCommitDeclaration(
start,
vec![PolynomialName {
name: name.to_string(),
name: name.into(),
array_size: None,
}],
def,
)
}
fn direct_reference(name: &str) -> Expression {
fn direct_reference<S: Into<String>>(name: S) -> Expression {
Expression::PolynomialReference(PolynomialReference {
namespace: None,
name: name.to_owned(),
name: name.into(),
index: None,
next: false,
})
@@ -764,7 +764,7 @@ fn extract_update(expr: Expression) -> (Option<String>, Expression) {
}
}
fn substitute(input: &Expression, substitution: &HashMap<String, String>) -> Expression {
fn substitute(input: Expression, substitution: &HashMap<String, String>) -> Expression {
match input {
// TODO namespace
Expression::PolynomialReference(r) => {
@@ -774,38 +774,55 @@ fn substitute(input: &Expression, substitution: &HashMap<String, String>) -> Exp
})
}
Expression::BinaryOperation(left, op, right) => build_binary_expr(
substitute(left, substitution),
*op,
substitute(right, substitution),
substitute(*left, substitution),
op,
substitute(*right, substitution),
),
Expression::UnaryOperation(op, exp) => build_unary_expr(*op, substitute(exp, substitution)),
Expression::UnaryOperation(op, exp) => build_unary_expr(op, substitute(*exp, substitution)),
Expression::FunctionCall(name, args) => Expression::FunctionCall(
name.clone(),
args.iter().map(|e| substitute(e, substitution)).collect(),
name,
args.into_iter()
.map(|e| substitute(e, substitution))
.collect(),
),
Expression::Tuple(items) => Expression::Tuple(
items
.into_iter()
.map(|e| substitute(e, substitution))
.collect(),
),
Expression::Tuple(items) => {
Expression::Tuple(items.iter().map(|e| substitute(e, substitution)).collect())
}
Expression::Constant(_)
| Expression::PublicReference(_)
| Expression::Number(_)
| Expression::String(_)
| Expression::FreeInput(_) => input.clone(),
Expression::MatchExpression(scrutinee, arms) => Expression::MatchExpression(
Box::new(substitute(*scrutinee, substitution)),
arms.into_iter()
.map(|(n, e)| (n, substitute(e, substitution)))
.collect(),
),
}
}
fn substitute_selected_exprs(
input: &SelectedExpressions,
input: SelectedExpressions,
substitution: &HashMap<String, String>,
) -> SelectedExpressions {
SelectedExpressions {
selector: input.selector.as_ref().map(|s| substitute(s, substitution)),
expressions: substitute_vec(&input.expressions, substitution),
selector: input.selector.map(|s| substitute(s, substitution)),
expressions: substitute_vec(input.expressions, substitution),
}
}
fn substitute_vec(input: &[Expression], substitution: &HashMap<String, String>) -> Vec<Expression> {
input.iter().map(|e| substitute(e, substitution)).collect()
fn substitute_vec(
input: Vec<Expression>,
substitution: &HashMap<String, String>,
) -> Vec<Expression> {
input
.into_iter()
.map(|e| substitute(e, substitution))
.collect()
}
fn substitute_string(input: &str, substitution: &HashMap<String, String>) -> String {

View File

@@ -91,6 +91,13 @@ impl<'a> Evaluator<'a> {
let values = &self.other_constants[name.as_str()];
values[abstract_to_degree(&arg_values[0]) as usize % values.len()].clone()
}
Expression::MatchExpression(scrutinee, arms) => {
let v = self.evaluate(scrutinee);
arms.iter()
.find(|(n, _)| n.is_none() || n.as_ref() == Some(&v))
.map(|(_, e)| self.evaluate(e))
.expect("No arm matched the value {v}")
}
}
}
@@ -116,6 +123,7 @@ impl<'a> Evaluator<'a> {
BinaryOperator::Pow => left.pow(abstract_to_degree(&right) as u32),
BinaryOperator::Mod => left % right,
BinaryOperator::BinaryAnd => left & right,
BinaryOperator::BinaryXor => left ^ right,
BinaryOperator::BinaryOr => left | right,
BinaryOperator::ShiftLeft => left << abstract_to_degree(&right),
BinaryOperator::ShiftRight => left >> abstract_to_degree(&right),
@@ -177,6 +185,43 @@ mod test {
);
}
#[test]
pub fn test_xor() {
let src = r#"
constant %N = 8;
namespace F(%N);
pol constant X(i) { i ^ (i + 17) | 3 };
"#;
let analyzed = analyze_string(src);
let (constants, degree) = generate(&analyzed);
assert_eq!(degree, 8);
assert_eq!(
constants,
vec![("F.X", convert((0..8).map(|i| i ^ (i + 17) | 3).collect()))]
);
}
#[test]
pub fn test_match() {
let src = r#"
constant %N = 8;
namespace F(%N);
pol constant X(i) { match i {
0 => 7,
3 => 9,
5 => 2,
_ => 4,
} + 1 };
"#;
let analyzed = analyze_string(src);
let (constants, degree) = generate(&analyzed);
assert_eq!(degree, 8);
assert_eq!(
constants,
vec![("F.X", convert(vec![8, 5, 5, 10, 5, 3, 5, 5]))]
);
}
#[test]
pub fn test_macro() {
let src = r#"

View File

@@ -270,6 +270,7 @@ impl<'a> Exporter<'a> {
BinaryOperator::Mod
| BinaryOperator::BinaryAnd
| BinaryOperator::BinaryOr
| BinaryOperator::BinaryXor
| BinaryOperator::ShiftLeft
| BinaryOperator::ShiftRight => {
panic!("Operator {op:?} not supported on polynomials.")
@@ -305,6 +306,9 @@ impl<'a> Exporter<'a> {
}
Expression::String(_) => panic!("Strings not allowed here."),
Expression::Tuple(_) => panic!("Tuples not allowed here"),
Expression::MatchExpression(_, _) => {
panic!("No match expressions allowed here.")
}
}
}

View File

@@ -48,6 +48,10 @@ pub enum Expression {
UnaryOperation(UnaryOperator, Box<Expression>),
FunctionCall(String, Vec<Expression>),
FreeInput(Box<Expression>),
MatchExpression(
Box<Expression>,
Vec<(Option<AbstractNumberType>, Expression)>,
),
}
#[derive(Debug, PartialEq, Eq, Default, Clone)]
@@ -79,6 +83,7 @@ pub enum BinaryOperator {
Mod,
Pow,
BinaryAnd,
BinaryXor,
BinaryOr,
ShiftLeft,
ShiftRight,

View File

@@ -153,6 +153,19 @@ impl Display for Expression {
Expression::UnaryOperation(op, exp) => write!(f, "{op}{exp}"),
Expression::FunctionCall(fun, args) => write!(f, "{fun}({})", format_expressions(args)),
Expression::FreeInput(input) => write!(f, "${{ {input} }}"),
Expression::MatchExpression(scrutinee, arms) => write!(
f,
"match {scrutinee} {{ {} }}",
arms.iter()
.map(|(n, e)| format!(
"{} => {e},",
n.as_ref()
.map(|n| n.to_string())
.unwrap_or_else(|| "_".to_string())
))
.collect::<Vec<_>>()
.join(" ")
),
}
}
}
@@ -203,6 +216,7 @@ impl Display for BinaryOperator {
BinaryOperator::Mod => "%",
BinaryOperator::Pow => "**",
BinaryOperator::BinaryAnd => "&",
BinaryOperator::BinaryXor => "^",
BinaryOperator::BinaryOr => "|",
BinaryOperator::ShiftLeft => "<<",
BinaryOperator::ShiftRight => ">>",

View File

@@ -241,18 +241,27 @@ Expression: Expression = {
}
BoxedExpression: Box<Expression> = {
BinaryOr
BinaryOr,
}
BinaryOr: Box<Expression> = {
BinaryOr BinaryOrOp BinaryAnd => Box::new(Expression::BinaryOperation(<>)),
BinaryAnd,
BinaryXor,
}
BinaryOrOp: BinaryOperator = {
"|" => BinaryOperator::BinaryOr,
}
BinaryXor: Box<Expression> = {
BinaryXor BinaryXorOp BinaryAnd => Box::new(Expression::BinaryOperation(<>)),
BinaryAnd,
}
BinaryXorOp: BinaryOperator = {
"^" => BinaryOperator::BinaryXor,
}
BinaryAnd: Box<Expression> = {
BinaryAnd BinaryAndOp BitShift => Box::new(Expression::BinaryOperation(<>)),
BitShift,
@@ -319,6 +328,7 @@ Term: Box<Expression> = {
PublicReference => Box::new(Expression::PublicReference(<>)),
Number => Box::new(Expression::Number(<>)),
StringLiteral => Box::new(Expression::String(<>)),
MatchExpression,
"(" <head:Expression> "," <tail:ExpressionList> ")" => { let mut list = vec![head]; list.extend(tail); Box::new(Expression::Tuple(list)) },
"(" <BoxedExpression> ")",
"${" <BoxedExpression> "}" => Box::new(Expression::FreeInput(<>))
@@ -339,6 +349,15 @@ PublicReference: String = {
":" <Identifier>
}
MatchExpression: Box<Expression> = {
"match" <BoxedExpression> "{" <(<MatchArm> ",")*> "}" => Box::new(Expression::MatchExpression(<>))
}
MatchArm: (Option<AbstractNumberType>, Expression) = {
<n:Number> "=>" <e:Expression> => (Some(n), e),
<n:"_"> "=>" <e:Expression> => (None, e),
}
// ---------------------------- Terminals -----------------------------

View File

@@ -47,6 +47,9 @@ impl<SV: SymbolicVariables> ExpressionEvaluator<SV> {
Expression::FunctionCall(_, _) => {
Err("Function calls not implemented.".to_string().into())
}
Expression::MatchExpression(_, _) => {
Err("Match expressions not implemented.".to_string().into())
}
}
}
@@ -112,6 +115,7 @@ impl<SV: SymbolicVariables> ExpressionEvaluator<SV> {
}
BinaryOperator::Mod
| BinaryOperator::BinaryAnd
| BinaryOperator::BinaryXor
| BinaryOperator::BinaryOr
| BinaryOperator::ShiftLeft
| BinaryOperator::ShiftRight => {
@@ -121,6 +125,7 @@ impl<SV: SymbolicVariables> ExpressionEvaluator<SV> {
let result = match op {
BinaryOperator::Mod => left % right,
BinaryOperator::BinaryAnd => left & right,
BinaryOperator::BinaryXor => left ^ right,
BinaryOperator::BinaryOr => left | right,
BinaryOperator::ShiftLeft => left << abstract_to_degree(&right),
BinaryOperator::ShiftRight => left >> abstract_to_degree(&right),

View File

@@ -141,6 +141,14 @@ impl<'a> ReferenceExtractor<'a> {
Expression::BinaryOperation(l, _, r) => &self.in_expression(l) | &self.in_expression(r),
Expression::UnaryOperation(_, e) => self.in_expression(e),
Expression::FunctionCall(_, args) => self.in_expressions(args),
Expression::MatchExpression(scrutinee, arms) => {
&self.in_expression(scrutinee)
| &arms
.iter()
.map(|(_, e)| self.in_expression(e))
.reduce(|a, b| &a | &b)
.unwrap_or_default()
}
Expression::LocalVariableReference(_)
| Expression::PublicReference(_)
| Expression::Number(_)

View File

@@ -1,3 +1,5 @@
use std::iter::once;
use crate::analyzer::{Expression, PolynomialReference};
use super::FixedData;
@@ -62,6 +64,9 @@ pub fn expr_any(expr: &Expression, f: &mut impl FnMut(&Expression) -> bool) -> b
Expression::BinaryOperation(l, _, r) => expr_any(l, f) || expr_any(r, f),
Expression::UnaryOperation(_, e) => expr_any(e, f),
Expression::FunctionCall(_, args) => args.iter().any(|e| expr_any(e, f)),
Expression::MatchExpression(scrutinee, arms) => once(scrutinee.as_ref())
.chain(arms.iter().map(|(_n, e)| e))
.any(|e| expr_any(e, f)),
Expression::Constant(_)
| Expression::PolynomialReference(_)
| Expression::LocalVariableReference(_)