Merge pull request #647 from powdr-labs/do_not_parse_constant_refs_differently

Do not treat constants specially during parsing.
This commit is contained in:
chriseth
2023-10-10 16:18:27 +00:00
committed by GitHub
14 changed files with 137 additions and 121 deletions

View File

@@ -575,7 +575,6 @@ impl<T: FieldElement> ASMPILConverter<T> {
value: Expression<T>,
) -> Vec<(T, AffineExpressionComponent<T>)> {
match value {
Expression::Constant(_) => panic!(),
Expression::PublicReference(_) => panic!(),
Expression::FunctionCall(_) => panic!(),
Expression::Reference(reference) => {

View File

@@ -11,28 +11,25 @@ use super::*;
impl<T: Display> Display for Analyzed<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for (name, value) in &self.constants {
writeln!(f, "constant {name} = {value};")?;
}
let mut namespace = "Global".to_string();
let mut current_namespace = "Global".to_string();
let mut update_namespace = |name: &str, degree: DegreeType, f: &mut Formatter<'_>| {
if let Some(dot) = name.find('.') {
if name[..dot] != namespace {
namespace = name[..dot].to_string();
writeln!(f, "namespace {namespace}({degree});")?;
let new_name = if let Some(dot) = name.find('.') {
if name[..dot] != current_namespace {
current_namespace = name[..dot].to_string();
writeln!(f, "namespace {current_namespace}({degree});")?;
}
Ok(name[dot + 1..].to_string())
&name[dot + 1..]
} else {
Ok(name.to_string())
}
name
};
Ok((new_name.to_string(), &current_namespace != "Global"))
};
for statement in &self.source_order {
match statement {
StatementIdentifier::Definition(name) => {
let (symbol, definition) = &self.definitions[name];
let name = update_namespace(name, symbol.degree, f)?;
let (name, is_local) = update_namespace(name, symbol.degree, f)?;
match symbol.kind {
SymbolKind::Poly(poly_type) => {
let kind = match &poly_type {
@@ -47,6 +44,14 @@ impl<T: Display> Display for Analyzed<T> {
writeln!(f, ";")?
}
}
SymbolKind::Constant() => {
let indentation = if is_local { " " } else { "" };
writeln!(
f,
"{indentation}constant {name}{};",
definition.as_ref().unwrap()
)?;
}
SymbolKind::Other() => {
write!(f, " let {name}")?;
if let Some(value) = definition {
@@ -59,7 +64,7 @@ impl<T: Display> Display for Analyzed<T> {
StatementIdentifier::PublicDeclaration(name) => {
let decl = &self.public_declarations[name];
// TODO we do not know the degree of the namespace here.
let name = update_namespace(&decl.name, 0, f)?;
let (name, _) = update_namespace(&decl.name, 0, f)?;
writeln!(
f,
" public {name} = {}({});",

View File

@@ -25,8 +25,6 @@ pub enum StatementIdentifier {
#[derive(Debug)]
pub struct Analyzed<T> {
/// Constants are not namespaced!
pub constants: HashMap<String, T>,
pub definitions: HashMap<String, (Symbol, Option<FunctionValueDefinition<T>>)>,
pub public_declarations: HashMap<String, PublicDeclaration>,
pub identities: Vec<Identity<Expression<T>>>,
@@ -141,7 +139,9 @@ impl<T> Analyzed<T> {
let mut names_to_remove: HashSet<String> = Default::default();
self.definitions.retain(|name, (poly, _def)| {
if to_remove.contains(&(poly as &Symbol).into()) {
if matches!(poly.kind, SymbolKind::Poly(_))
&& to_remove.contains(&(poly as &Symbol).into())
{
names_to_remove.insert(name.clone());
false
} else {
@@ -157,15 +157,18 @@ impl<T> Analyzed<T> {
true
});
self.definitions.values_mut().for_each(|(poly, _def)| {
let poly_id = PolyID::from(poly as &Symbol);
assert!(!to_remove.contains(&poly_id));
poly.id = replacements[&poly_id].id;
if matches!(poly.kind, SymbolKind::Poly(_)) {
let poly_id = PolyID::from(poly as &Symbol);
assert!(!to_remove.contains(&poly_id));
poly.id = replacements[&poly_id].id;
}
});
self.pre_visit_expressions_mut(&mut |expr| {
if let Expression::Reference(Reference::Poly(poly)) = expr {
let poly_id = poly.poly_id.unwrap();
assert!(!to_remove.contains(&poly_id));
poly.poly_id = Some(replacements[&poly_id]);
poly.poly_id = poly.poly_id.map(|poly_id| {
assert!(!to_remove.contains(&poly_id));
replacements[&poly_id]
});
}
});
}
@@ -240,11 +243,16 @@ impl Symbol {
}
}
/// The "kind" of a symbol. In the future, this will be mostly
/// replaced by its type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SymbolKind {
/// Fixed, witness or intermediate polynomial
Poly(PolynomialType),
/// Other symbol, could be a constant, depends on the type.
/// A constant value.
Constant(),
/// Other symbol, depends on the type.
/// Examples include functions not of the type "int -> fe".
Other(),
}

View File

@@ -423,7 +423,6 @@ pub fn format_expressions<T: Display, Ref: Display>(expressions: &[Expression<T,
impl<T: Display, Ref: Display> Display for Expression<T, Ref> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Expression::Constant(name) => write!(f, "{name}"),
Expression::Reference(reference) => write!(f, "{reference}"),
Expression::PublicReference(name) => write!(f, ":{name}"),
Expression::Number(value) => write!(f, "{value}"),

View File

@@ -67,8 +67,6 @@ impl<Expr> Default for SelectedExpressions<Expr> {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum Expression<T, Ref = ShiftedPolynomialReference<T>> {
/// Reference to a constant, "%ConstantName"
Constant(String),
Reference(Ref),
PublicReference(String),
Number(T),

View File

@@ -114,7 +114,6 @@ impl<T, Ref> ExpressionVisitable<Expression<T, Ref>> for Expression<T, Ref> {
}
match self {
Expression::Reference(_)
| Expression::Constant(_)
| Expression::PublicReference(_)
| Expression::Number(_)
| Expression::String(_) => {}
@@ -152,7 +151,6 @@ impl<T, Ref> ExpressionVisitable<Expression<T, Ref>> for Expression<T, Ref> {
}
match self {
Expression::Reference(_)
| Expression::Constant(_)
| Expression::PublicReference(_)
| Expression::Number(_)
| Expression::String(_) => {}

View File

@@ -145,6 +145,7 @@ fn symbol_kind_to_json_string(k: SymbolKind) -> &'static str {
match k {
SymbolKind::Poly(poly_type) => polynomial_type_to_json_string(poly_type),
SymbolKind::Other() => panic!("Cannot translate \"other\" symbol to json."),
SymbolKind::Constant() => unreachable!(),
}
}
@@ -183,12 +184,15 @@ impl<'a, T: FieldElement> Exporter<'a, T> {
self.analyzed
.definitions
.iter()
.map(|(name, (symbol, _value))| {
let id = if symbol.kind == SymbolKind::Poly(PolynomialType::Intermediate) {
self.intermediate_poly_expression_ids[&symbol.id]
} else {
symbol.id
};
.filter_map(|(name, (symbol, _value))| {
let id = match symbol.kind {
SymbolKind::Poly(PolynomialType::Intermediate) => {
Some(self.intermediate_poly_expression_ids[&symbol.id])
}
SymbolKind::Poly(_) => Some(symbol.id),
SymbolKind::Other() | SymbolKind::Constant() => None,
}?;
let out = Reference {
polType: None,
type_: symbol_kind_to_json_string(symbol.kind).to_string(),
@@ -198,7 +202,7 @@ impl<'a, T: FieldElement> Exporter<'a, T> {
elementType: None,
len: symbol.length.map(|l| l as usize),
};
(name.clone(), out)
Some((name.clone(), out))
})
.collect::<HashMap<String, Reference>>()
}
@@ -235,11 +239,6 @@ impl<'a, T: FieldElement> Exporter<'a, T> {
/// returns the degree and the JSON value (intermediate polynomial IDs)
fn expression_to_json(&self, expr: &Expression<T>) -> (u32, StarkyExpr) {
match expr {
Expression::Constant(name) => {
panic!(
"Constant {name} was not inlined. optimize_constants needs to be run at least."
)
}
Expression::Reference(analyzed::Reference::Poly(reference)) => {
self.polynomial_reference_to_json(reference)
}

View File

@@ -41,7 +41,6 @@ fn generate_values<T: FieldElement>(
.into_par_iter()
.map(|i| {
Evaluator {
constants: &analyzed.constants,
definitions: &analyzed.definitions,
variables: &[i.into()],
function_cache: other_constants,
@@ -52,7 +51,6 @@ fn generate_values<T: FieldElement>(
.collect(),
FunctionValueDefinition::Array(values) => {
let evaluator = Evaluator {
constants: &analyzed.constants,
definitions: &analyzed.definitions,
variables: &[],
function_cache: other_constants,

View File

@@ -37,9 +37,6 @@ where
// @TODO if we iterate on processing the constraints in the same row,
// we could store the simplified values.
match expr {
Expression::Constant(name) => {
panic!("Constants should have been replaced, but {name} is still there.")
}
Expression::Reference(Reference::Poly(poly)) => self.variables.value(poly),
Expression::Number(n) => Ok((*n).into()),
Expression::BinaryOperation(left, op, right) => {

View File

@@ -256,9 +256,6 @@ fn expression_2_expr<T: FieldElement>(cd: &CircuitData<T>, expr: &Expression<T>)
_ => unimplemented!("{:?}", expr),
}
}
Expression::Constant(name) => {
panic!("Constant {name} was not inlined. optimize_constants needs to be run at least.")
}
_ => unimplemented!("{:?}", expr),
}

View File

@@ -462,7 +462,7 @@ UnaryOp: UnaryOperator = {
Term: Box<Expression<T>> = {
FunctionCall => Box::new(Expression::FunctionCall(<>)),
ConstantIdentifier => Box::new(Expression::Constant(<>)),
ConstantIdentifier => Box::new(Expression::Reference(PolynomialReference::new(<>).single().local().current())),
ShiftedPolynomialReference => Box::new(Expression::Reference(<>)),
PublicReference => Box::new(Expression::PublicReference(<>)),
FieldElement => Box::new(Expression::Number(<>)),

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap;
use ast::{
analyzed::{Analyzed, Expression, FunctionValueDefinition, Reference, Symbol},
analyzed::{Analyzed, Expression, FunctionValueDefinition, Reference, Symbol, SymbolKind},
evaluate_binary_operation, evaluate_unary_operation,
parsed::{FunctionCall, MatchArm, MatchPattern},
};
@@ -13,7 +13,6 @@ pub fn evaluate_expression<T: FieldElement>(
expression: &Expression<T>,
) -> Result<T, String> {
Evaluator {
constants: &analyzed.constants,
definitions: &analyzed.definitions,
function_cache: &Default::default(),
variables: &[],
@@ -21,8 +20,26 @@ pub fn evaluate_expression<T: FieldElement>(
.evaluate(expression)
}
/// Returns a HashMap of all symbols that have a constant single value.
pub fn compute_constants<T: FieldElement>(analyzed: &Analyzed<T>) -> HashMap<String, T> {
analyzed
.definitions
.iter()
.filter_map(|(name, (symbol, value))| {
(symbol.kind == SymbolKind::Constant()).then(|| {
let Some(FunctionValueDefinition::Expression(value)) = value else {
panic!()
};
(
name.to_owned(),
evaluate_expression(analyzed, value).unwrap(),
)
})
})
.collect()
}
pub struct Evaluator<'a, T> {
pub constants: &'a HashMap<String, T>,
pub definitions: &'a HashMap<String, (Symbol, Option<FunctionValueDefinition<T>>)>,
/// Contains full value tables of functions (columns) we already evaluated.
pub function_cache: &'a HashMap<&'a str, Vec<T>>,
@@ -32,21 +49,13 @@ pub struct Evaluator<'a, T> {
impl<'a, T: FieldElement> Evaluator<'a, T> {
pub fn evaluate(&self, expr: &Expression<T>) -> Result<T, String> {
match expr {
Expression::Constant(name) => Ok(self.constants[name]),
Expression::Reference(Reference::LocalVar(i, _name)) => Ok(self.variables[*i as usize]),
Expression::Reference(Reference::Poly(poly)) => {
if !poly.next && poly.index.is_none() {
let name = poly.name.to_owned();
if let Some(value) = self.constants.get(&name) {
Ok(*value)
} else {
let (_, value) = &self.definitions[&name];
match value {
Some(FunctionValueDefinition::Expression(value)) => {
self.evaluate(value)
}
_ => Err("Cannot evaluate function values".to_string()),
}
let (_, value) = &self.definitions[&poly.name.to_string()];
match value {
Some(FunctionValueDefinition::Expression(value)) => self.evaluate(value),
_ => Err("Cannot evaluate function-typed values".to_string()),
}
} else {
Err("Cannot evaluate arrays or next references.".to_string())

View File

@@ -35,8 +35,6 @@ pub fn process_pil_file_contents<T: FieldElement>(contents: &str) -> Analyzed<T>
struct PILAnalyzer<T> {
namespace: String,
polynomial_degree: DegreeType,
/// Constants are not namespaced!
constants: HashMap<String, T>,
definitions: HashMap<String, (Symbol, Option<FunctionValueDefinition<T>>)>,
public_declarations: HashMap<String, PublicDeclaration>,
identities: Vec<Identity<Expression<T>>>,
@@ -51,10 +49,9 @@ struct PILAnalyzer<T> {
macro_expander: MacroExpander<T>,
}
impl<T> From<PILAnalyzer<T>> for Analyzed<T> {
impl<T: Copy> From<PILAnalyzer<T>> for Analyzed<T> {
fn from(
PILAnalyzer {
constants,
definitions,
public_declarations,
identities,
@@ -66,9 +63,7 @@ impl<T> From<PILAnalyzer<T>> for Analyzed<T> {
.iter()
.map(|(name, (poly, _))| (name.clone(), poly.clone()))
.collect::<HashMap<_, _>>();
let constant_names = constants.keys().cloned().collect::<HashSet<_>>();
let mut result = Self {
constants,
definitions,
public_declarations,
identities,
@@ -78,13 +73,13 @@ impl<T> From<PILAnalyzer<T>> for Analyzed<T> {
let poly = ids
.get(&reference.name)
.unwrap_or_else(|| panic!("Column {} not found.", reference.name));
reference.poly_id = Some(poly.into());
if let SymbolKind::Poly(_) = &poly.kind {
reference.poly_id = Some(poly.into());
}
};
result.pre_visit_expressions_mut(&mut |e| {
if let Expression::Reference(Reference::Poly(reference)) = e {
if !constant_names.contains(&reference.name) {
assign_id(reference);
}
assign_id(reference);
}
});
result
@@ -103,6 +98,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
SymbolKind::Poly(PolynomialType::Committed),
SymbolKind::Poly(PolynomialType::Constant),
SymbolKind::Poly(PolynomialType::Intermediate),
SymbolKind::Constant(),
SymbolKind::Other(),
]
.into_iter()
@@ -153,7 +149,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
PilStatement::Include(_, include) => self.handle_include(include),
PilStatement::Namespace(_, name, degree) => self.handle_namespace(name, degree),
PilStatement::PolynomialDefinition(start, name, value) => {
self.handle_polynomial_definition(
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
@@ -171,7 +167,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
PolynomialType::Constant,
),
PilStatement::PolynomialConstantDefinition(start, name, definition) => {
self.handle_polynomial_definition(
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
@@ -188,7 +184,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
PilStatement::PolynomialCommitDeclaration(start, mut polynomials, Some(definition)) => {
assert!(polynomials.len() == 1);
let name = polynomials.pop().unwrap();
self.handle_polynomial_definition(
self.handle_symbol_definition(
self.to_source_ref(start),
name.name,
name.array_size,
@@ -196,8 +192,18 @@ impl<T: FieldElement> PILAnalyzer<T> {
Some(definition),
);
}
PilStatement::ConstantDefinition(_start, name, value) => {
self.handle_constant_definition(name, self.evaluate_expression(value).unwrap())
PilStatement::ConstantDefinition(start, name, value) => {
// Check it is a constant.
if let Err(err) = self.evaluate_expression(value.clone()) {
panic!("Could not evaluate constant: {name} = {value}: {err}");
}
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
SymbolKind::Constant(),
Some(FunctionDefinition::Expression(value)),
);
}
PilStatement::LetStatement(start, name, value) => {
self.handle_generic_definition(start, name, value)
@@ -232,7 +238,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
match value {
None => {
// No value provided => treat it as a witness column.
self.handle_polynomial_definition(
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
@@ -247,7 +253,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
body,
}) if params.len() == 1 => {
// Assigned value is a lambda expression with a single parameter => treat it as a fixed column.
self.handle_polynomial_definition(
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
@@ -256,19 +262,20 @@ impl<T: FieldElement> PILAnalyzer<T> {
);
}
_ => {
if let Ok(constant) = self.evaluate_expression(value.clone()) {
let symbol_kind = if self.evaluate_expression(value.clone()).is_ok() {
// Value evaluates to a constant number => treat it as a constant
self.handle_constant_definition(name.to_string(), constant);
SymbolKind::Constant()
} else {
// Otherwise, treat it as "generic definition"
self.handle_polynomial_definition(
self.to_source_ref(start),
name,
None,
SymbolKind::Other(),
Some(FunctionDefinition::Expression(value)),
);
}
SymbolKind::Other()
};
self.handle_symbol_definition(
self.to_source_ref(start),
name,
None,
symbol_kind,
Some(FunctionDefinition::Expression(value)),
);
}
}
}
@@ -347,7 +354,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
polynomial_type: PolynomialType,
) {
for PolynomialName { name, array_size } in polynomials {
self.handle_polynomial_definition(
self.handle_symbol_definition(
source.clone(),
name,
array_size,
@@ -357,7 +364,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
}
}
fn handle_polynomial_definition(
fn handle_symbol_definition(
&mut self,
source: SourceRef,
name: String,
@@ -375,7 +382,12 @@ impl<T: FieldElement> PILAnalyzer<T> {
let counter = self.symbol_counters.get_mut(&symbol_kind).unwrap();
let id = *counter;
*counter += length.unwrap_or(1);
let absolute_name = self.namespaced(&name);
let absolute_name = if symbol_kind == SymbolKind::Constant() {
// Constants are not namespaced.
name.to_owned()
} else {
self.prepend_current_namespace(&name)
};
let symbol = Symbol {
id,
source,
@@ -391,6 +403,7 @@ impl<T: FieldElement> PILAnalyzer<T> {
assert!(!have_array_size);
assert!(
symbol_kind == SymbolKind::Other()
|| symbol_kind == SymbolKind::Constant()
|| symbol_kind == SymbolKind::Poly(PolynomialType::Intermediate)
);
FunctionValueDefinition::Expression(self.process_expression(expr))
@@ -453,11 +466,6 @@ impl<T: FieldElement> PILAnalyzer<T> {
.push(StatementIdentifier::PublicDeclaration(name));
}
fn handle_constant_definition(&mut self, name: String, value: T) {
let is_new = self.constants.insert(name.to_string(), value).is_none();
assert!(is_new, "Constant {name} was defined twice.");
}
fn dispense_id(&mut self, kind: IdentityKind) -> u64 {
let cnt = self.identity_counter.entry(kind).or_default();
let id = *cnt;
@@ -465,12 +473,12 @@ impl<T: FieldElement> PILAnalyzer<T> {
id
}
pub fn namespaced(&self, name: &str) -> String {
self.namespaced_ref(&None, name)
fn prepend_current_namespace(&self, name: &str) -> String {
format!("{}.{name}", self.namespace)
}
fn namespaced_ref(&self, namespace: &Option<String>, name: &str) -> String {
if name.starts_with('%') || (namespace.is_none() && self.constants.contains_key(name)) {
pub fn namespaced_ref_to_absolute(&self, namespace: &Option<String>, name: &str) -> String {
if name.starts_with('%') || self.definitions.contains_key(&name.to_string()) {
assert!(namespace.is_none());
// Constants are not namespaced
name.to_string()
@@ -481,7 +489,6 @@ impl<T: FieldElement> PILAnalyzer<T> {
fn evaluate_expression(&self, expr: ::ast::parsed::Expression<T>) -> Result<T, String> {
Evaluator {
constants: &self.constants,
definitions: &self.definitions,
function_cache: &Default::default(),
variables: &[],
@@ -560,7 +567,6 @@ impl<'a, T: FieldElement> ExpressionProcessor<'a, T> {
pub fn process_expression(&mut self, expr: parsed::Expression<T>) -> Expression<T> {
use parsed::Expression as PExpression;
match expr {
PExpression::Constant(name) => Expression::Constant(name),
PExpression::Reference(poly) => {
if poly.namespace().is_none() && self.local_variables.contains_key(poly.name()) {
let id = self.local_variables[poly.name()];
@@ -595,7 +601,7 @@ impl<'a, T: FieldElement> ExpressionProcessor<'a, T> {
Expression::UnaryOperation(op, Box::new(self.process_expression(*value)))
}
PExpression::FunctionCall(c) => Expression::FunctionCall(parsed::FunctionCall {
id: self.analyzer.namespaced(&c.id),
id: self.analyzer.namespaced_ref_to_absolute(&None, &c.id),
arguments: self.process_expressions(c.arguments),
}),
PExpression::MatchExpression(scrutinee, arms) => Expression::MatchExpression(
@@ -652,7 +658,9 @@ impl<'a, T: FieldElement> ExpressionProcessor<'a, T> {
.as_ref()
.map(|i| self.analyzer.evaluate_expression(*i.clone()).unwrap())
.map(|i| i.to_degree());
let name = self.analyzer.namespaced_ref(poly.namespace(), poly.name());
let name = self
.analyzer
.namespaced_ref_to_absolute(poly.namespace(), poly.name());
PolynomialReference {
name,
poly_id: None,
@@ -852,18 +860,20 @@ namespace T(65536);
#[test]
fn let_definitions() {
let input = r#"namespace N(65536);
let input = r#"constant %r = 65536;
namespace N(%r);
let x;
let t = |i| i + 1;
let z = 7;
let other = [1, 2];
let z = 2;
let t = |i| i + z;
let other = [1, z];
let other_fun = |i, j| (i + 7, (|k| k - i));
"#;
let expected = r#"constant z = 7;
let expected = r#"constant %r = 65536;
namespace N(65536);
col witness x;
col fixed t(i) { (i + 1) };
let other = [1, 2];
constant z = 2;
col fixed t(i) { (i + z) };
let other = [1, z];
let other_fun = |i, j| ((i + 7), |k| (k - i));
"#;
let formatted = process_pil_file_contents::<GoldilocksField>(input).to_string();

View File

@@ -13,6 +13,7 @@ use ast::parsed::visitor::ExpressionVisitable;
use ast::parsed::UnaryOperator;
use ast::{evaluate_binary_operation, evaluate_unary_operation};
use number::FieldElement;
use pil_analyzer::evaluator::compute_constants;
pub fn optimize<T: FieldElement>(mut pil_file: Analyzed<T>) -> Analyzed<T> {
let col_count_pre = (pil_file.commitment_count(), pil_file.constant_count());
@@ -45,17 +46,15 @@ pub fn optimize_constants<T: FieldElement>(mut pil_file: Analyzed<T>) -> Analyze
/// Inlines references to symbols with a single constant value.
fn inline_constant_values<T: FieldElement>(pil_file: &mut Analyzed<T>) {
let constants = &pil_file.constants.clone();
pil_file.post_visit_expressions_mut(&mut |e| match e {
Expression::Reference(Reference::Poly(poly)) => {
let constants = compute_constants(pil_file);
pil_file.post_visit_expressions_mut(&mut |e| {
if let Expression::Reference(Reference::Poly(poly)) = e {
if !poly.next && poly.index.is_none() {
if let Some(value) = constants.get(&poly.name) {
*e = Expression::Number(*value)
}
}
}
Expression::Constant(name) => *e = Expression::Number(constants[name]),
_ => {}
});
}