1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

Merge branch 'add-ast-folder' into array

This commit is contained in:
schaeff 2018-10-24 11:31:09 +02:00
commit 27a1714294
13 changed files with 1035 additions and 505 deletions

View file

@ -0,0 +1,5 @@
def foo() -> (field[2]):
return [1, 2]
def main() -> (field[2]):
return foo()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,7 @@
def get(field[32] array, field index) -> (field):
return array[index]
def main() -> (field):
field[32] array = [0, 0, 0, 0, 0, 0, 7, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
field r = get(array, 7)
return r

File diff suppressed because one or more lines are too long

View file

@ -727,6 +727,9 @@ impl Flattener {
statements_flattened,
expressions[n.to_dec_string().parse::<usize>().unwrap()].clone()
).apply_recursive_substitution(&self.substitution)
},
FieldElementArrayExpression::FunctionCall(size, id, args) => {
unimplemented!()
}
}
}
@ -770,6 +773,9 @@ impl Flattener {
FieldElementArrayExpression::Value(size, expressions) => {
assert_eq!(size, expressions.len());
expressions[i].clone()
},
FieldElementArrayExpression::FunctionCall(size, id, args) => {
unimplemented!()
}
},
box FieldElementExpression::Number(T::from(0)),
@ -807,6 +813,18 @@ impl Flattener {
statements_flattened,
v
)).collect()
},
FieldElementArrayExpression::FunctionCall(size, ref id, ref param_expressions) => {
let exprs_flattened = self.flatten_function_call(
functions_flattened,
arguments_flattened,
statements_flattened,
id,
vec![Type::FieldElementArray(size)],
param_expressions
);
assert!(exprs_flattened.expressions.len() == size); // outside of MultipleDefinition, FunctionCalls must return a single value
exprs_flattened.expressions
}
}
}
@ -857,8 +875,8 @@ impl Flattener {
let rhs = rhs.into_iter().map(|e| e.apply_recursive_substitution(&self.substitution)).collect::<Vec<_>>();
match rhs.len() {
1 => {
match expr.get_type() {
Type::FieldElement | Type::Boolean => {
match assignee {
TypedAssignee::Identifier(ref v) => {
let debug_name = v.clone().id;
@ -951,7 +969,7 @@ impl Flattener {
}
}
},
_ => {
Type::FieldElementArray(..) => {
for (index, r) in rhs.into_iter().enumerate() {
let debug_name = match assignee {
TypedAssignee::Identifier(ref v) => format!("{}_c{}", v.id, index),
@ -1110,21 +1128,9 @@ impl Flattener {
// take each new variable being assigned
for v in vars {
// determine how many field elements it carries
let n = v.get_type().get_primitive_count();
match n {
1 => {
let debug_name = v.id;
let var = self.use_variable(&debug_name);
// handle return of function call
let var_to_replace = self.get_latest_var_substitution(&debug_name);
if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){
self.substitution.insert(var_to_replace.clone(),var.clone());
}
statements_flattened.push(FlatStatement::Definition(var, iterator.next().unwrap()));
},
n => {
for index in 0..n {
match v.get_type() {
Type::FieldElementArray(size) => {
for index in 0..size {
let debug_name = format!("{}_c{}", v.id, index);
let var = self.use_variable(&debug_name);
// handle return of function call
@ -1134,7 +1140,17 @@ impl Flattener {
}
statements_flattened.push(FlatStatement::Definition(var, iterator.next().unwrap()));
}
}
},
Type::Boolean | Type::FieldElement => {
let debug_name = v.id;
let var = self.use_variable(&debug_name);
// handle return of function call
let var_to_replace = self.get_latest_var_substitution(&debug_name);
if !(var == var_to_replace) && self.variables.contains(&var_to_replace) && !self.substitution.contains_key(&var_to_replace){
self.substitution.insert(var_to_replace.clone(),var.clone());
}
statements_flattened.push(FlatStatement::Definition(var, iterator.next().unwrap()));
},
}
}
@ -1218,6 +1234,7 @@ impl Flattener {
///
/// * `prog` - `Prog`ram that will be flattened.
pub fn flatten_program<T: Field>(&mut self, prog: TypedProg<T>) -> FlatProg<T> {
let mut functions_flattened = Vec::new();
self.load_stdlib(&mut functions_flattened);

View file

@ -489,7 +489,11 @@ impl Checker {
let f = &candidates[0];
// the return count has to be 1
match f.signature.outputs.len() {
1 => Ok(FieldElementExpression::FunctionCall(f.id.clone(), arguments_checked).into()),
1 => match f.signature.outputs[0] {
Type::FieldElement => Ok(FieldElementExpression::FunctionCall(f.id.clone(), arguments_checked).into()),
Type::FieldElementArray(size) => Ok(FieldElementArrayExpression::FunctionCall(size, f.id.clone(), arguments_checked).into()),
_ => unimplemented!()
}
n => Err(Error { message: format!("{} returns {} values but is called outside of a definition", f.id, n) })
}
},

View file

@ -0,0 +1,87 @@
use std::collections::HashSet;
use field::Field;
use typed_absy::Folder;
use typed_absy::*;
use typed_absy::folder::*;
use types::{Type, Signature};
pub struct DeadCode {
called: HashSet<String>,
}
impl DeadCode {
fn new() -> Self {
DeadCode {
called: HashSet::new()
}
}
pub fn clean<T: Field>(p: TypedProg<T>) -> TypedProg<T> {
DeadCode::new().fold_program(p)
}
}
impl<T: Field> Folder<T> for DeadCode {
fn fold_program(&mut self, p: TypedProg<T>) -> TypedProg<T> {
let p = fold_program(self, p);
// only keep functions which are being called, or `main`
TypedProg {
functions: p.functions.into_iter().filter(|f| f.id == "main" || self.called.contains(&f.to_slug())).collect(),
..p
}
}
// add extra statements before the modified statement
fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
match s {
TypedStatement::MultipleDefinition(variables, elist) => {
match elist {
TypedExpressionList::FunctionCall(id, exps, types) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(types.clone());
self.called.insert(format!("{}_{}", id, signature.to_slug()));
vec![TypedStatement::MultipleDefinition(variables, TypedExpressionList::FunctionCall(id, exps, types))]
}
}
},
s => fold_statement(self, s)
}
}
fn fold_field_expression(&mut self, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
match e {
FieldElementExpression::FunctionCall(id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElement]);
self.called.insert(format!("{}_{}", id, signature.to_slug()));
FieldElementExpression::FunctionCall(id, exps)
},
e => fold_field_expression(self, e)
}
}
fn fold_field_array_expression(&mut self, e: FieldElementArrayExpression<T>) -> FieldElementArrayExpression<T> {
match e {
FieldElementArrayExpression::FunctionCall(size, id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElementArray(size)]);
self.called.insert(format!("{}_{}", id, signature));
FieldElementArrayExpression::FunctionCall(size, id, exps)
},
e => fold_field_array_expression(self, e)
}
}
}

View file

@ -0,0 +1,194 @@
use std::collections::HashMap;
use field::Field;
use typed_absy::Folder;
use typed_absy::*;
use typed_absy::folder::*;
use types::{Type, Signature};
pub struct Inliner<T: Field> {
functions: Vec<TypedFunction<T>>,
statements_buffer: Vec<TypedStatement<T>>,
context: Vec<(String, usize)>,
call_count: HashMap<String, usize>
}
impl<T: Field> Inliner<T> {
pub fn new() -> Self {
Inliner {
functions: vec![],
statements_buffer: vec![],
context: vec![],
call_count: HashMap::new()
}
}
fn should_inline(&self, function: &Option<TypedFunction<T>>, _arguments: &Vec<TypedExpression<T>>) -> bool {
// we should define a heuristic here
// currently it doesn't seem like there's a tradeoff as everything gets inlined in flattening anyway
// however, using backends such as bellman, we could avoid flattening and "stream" the computation
// at proving time, the tradeoff becomes code size (not inlining keeps only one copy of each function) vs optimisation
// (inlining enables constant propagation through function calls, which cannot be achieved by our final optimiser in some cases)
match function {
Some(..) => true,
None => false
}
}
// inline a call to `function` taking `expressions` as inputs
// this function mutates `self.call` by incrementing the counter for `function`, and mutates `self.context`
fn inline_call(&mut self, function: &TypedFunction<T>, expressions: Vec<TypedExpression<T>>) -> Vec<TypedExpression<T>> {
self.call_count.entry(function.to_slug()).and_modify(|i| *i += 1).or_insert(1);
self.context.push((function.to_slug(), *self.call_count.get(&function.to_slug()).unwrap()));
// add definitions for the inputs
let mut inputs_bindings = function.arguments.iter().zip(expressions)
.map(|(a, e)|
TypedStatement::Definition(
TypedAssignee::Identifier(self.fold_variable(a.id.clone())),
e
)
).collect();
self.statements_buffer.append(&mut inputs_bindings);
// filter out the return statement and keep it aside
let (mut statements, ret) : (Vec<_>, Vec<_>) = function.statements.clone().into_iter().flat_map(|s| self.fold_statement(s)).partition(|s| match s {
TypedStatement::Return(..) => false,
_ => true
});
// add all statements to the buffer
self.statements_buffer.append(&mut statements);
// remove this call from the context
self.context.pop();
match ret[0].clone() {
TypedStatement::Return(exprs) => exprs,
_ => panic!("")
}
}
pub fn inline(prog: TypedProg<T>) -> TypedProg<T> {
Inliner::new().fold_program(prog)
}
}
impl<T: Field> Folder<T> for Inliner<T> {
// store the list of functions
fn fold_program(&mut self, p: TypedProg<T>) -> TypedProg<T> {
self.functions = p.functions.clone();
fold_program(self, p)
}
// add extra statements before the modified statement
fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
let mut statements = match s {
TypedStatement::MultipleDefinition(variables, elist) => {
match elist {
TypedExpressionList::FunctionCall(id, exps, types) => {
let variables: Vec<_> = variables.into_iter().map(|a| self.fold_variable(a)).collect();
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(types.clone());
// find the function
let function = self.functions.iter().find(|f| f.id == id && f.signature == passed_signature).cloned();
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(&function.unwrap(), exps);
variables.into_iter().zip(ret.into_iter()).map(|(v, e)| TypedStatement::Definition(TypedAssignee::Identifier(v), e)).collect()
},
false => {
vec![TypedStatement::MultipleDefinition(variables, TypedExpressionList::FunctionCall(id, exps, types))]
}
}
}
}
},
s => fold_statement(self, s)
};
// add the result of folding to the buffer
self.statements_buffer.append(&mut statements);
// return the whole buffer
self.statements_buffer.drain(..).collect()
}
// prefix all names with the context
fn fold_name(&mut self, n: String) -> String {
match self.context.len() {
0 => n,
_ => format!("{}_{}", self
.context
.iter()
.map(|(s, i)|
format!("{}_{}", s, i)
)
.collect::<Vec<_>>()
.join("_"), n)
}
}
// inline calls which return a field element
fn fold_field_expression(&mut self, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
match e {
FieldElementExpression::FunctionCall(id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElement]);
// find the function
let function = self.functions.iter().find(|f| f.id == id && f.signature == passed_signature).cloned();
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(&function.unwrap(), exps);
// unwrap the result to return a field element
match ret[0].clone() {
TypedExpression::FieldElement(e) => e,
_ => panic!("")
}
},
false => FieldElementExpression::FunctionCall(id, exps)
}
},
// default
e => fold_field_expression(self, e)
}
}
// inline calls which return a field element array
fn fold_field_array_expression(&mut self, e: FieldElementArrayExpression<T>) -> FieldElementArrayExpression<T> {
match e {
FieldElementArrayExpression::FunctionCall(size, id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElementArray(size)]);
// find the function
let function = self.functions.iter().find(|f| f.id == id && f.signature == passed_signature).cloned();
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(&function.unwrap(), exps);
// unwrap the result to return a field element
match ret[0].clone() {
TypedExpression::FieldElementArray(e) => e,
_ => panic!("")
}
},
false => FieldElementArrayExpression::FunctionCall(size, id, exps)
}
},
// default
e => fold_field_array_expression(self, e)
}
}
}

View file

@ -7,11 +7,16 @@
mod propagation;
mod unroll;
mod flat_propagation;
mod inline;
mod dead_code;
use flat_absy::FlatProg;
use field::Field;
use typed_absy::TypedProg;
use self::unroll::Unroll;
use self::unroll::Unroller;
use self::inline::Inliner;
use self::propagation::Propagator;
use self::dead_code::DeadCode;
pub trait Analyse {
fn analyse(self) -> Self;
@ -19,10 +24,16 @@ pub trait Analyse {
impl<T: Field> Analyse for TypedProg<T> {
fn analyse(self) -> Self {
//self.unroll().propagate()
let r = self.unroll();
let r = r.propagate();
print!("propagated! {}", r);
// unroll
let r = Unroller::unroll(self);
//propagate a first time for constants to reach function calls
let r = Propagator::propagate(r);
// apply inlining strategy
let r = Inliner::inline(r);
// Propagate again
let r = Propagator::propagate(r);
// remove unused functions
let r = DeadCode::clean(r);
r
}
}

View file

@ -8,223 +8,39 @@ use absy::variable::Variable;
use std::collections::HashMap;
use field::Field;
use typed_absy::*;
use typed_absy::folder::*;
pub trait Propagate<T: Field> {
fn propagate(self, functions: &Vec<TypedFunction<T>>) -> Self;
pub struct Propagator<T: Field> {
constants: HashMap<TypedAssignee<T>, TypedExpression<T>>,
}
pub trait PropagateWithContext<T: Field> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> Self;
}
impl<T: Field> PropagateWithContext<T> for TypedExpression<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> TypedExpression<T> {
match self {
TypedExpression::FieldElement(e) => e.propagate(constants, functions).into(),
TypedExpression::Boolean(e) => e.propagate(constants, functions).into(),
TypedExpression::FieldElementArray(e) => e.propagate(constants, functions).into(),
impl<T: Field> Propagator<T> {
fn new() -> Self {
Propagator {
constants: HashMap::new()
}
}
pub fn propagate(p: TypedProg<T>) -> TypedProg<T> {
Propagator::new().fold_program(p)
}
}
impl<T: Field> PropagateWithContext<T> for FieldElementExpression<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> FieldElementExpression<T> {
match self {
FieldElementExpression::Number(n) => FieldElementExpression::Number(n),
FieldElementExpression::Identifier(id) => {
match constants.get(&TypedAssignee::Identifier(Variable::field_element(id.clone()))) {
Some(e) => match e {
TypedExpression::FieldElement(e) => e.clone(),
_ => panic!("constant stored for a field element should be a field element")
},
None => FieldElementExpression::Identifier(id)
}
},
FieldElementExpression::Add(box e1, box e2) => {
match (e1.propagate(constants, functions), e2.propagate(constants, functions)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 + n2),
(e1, e2) => FieldElementExpression::Add(box e1, box e2),
}
},
FieldElementExpression::Sub(box e1, box e2) => {
match (e1.propagate(constants, functions), e2.propagate(constants, functions)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 - n2),
(e1, e2) => FieldElementExpression::Sub(box e1, box e2),
}
},
FieldElementExpression::Mult(box e1, box e2) => {
match (e1.propagate(constants, functions), e2.propagate(constants, functions)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 * n2),
(e1, e2) => FieldElementExpression::Mult(box e1, box e2),
}
},
FieldElementExpression::Div(box e1, box e2) => {
match (e1.propagate(constants, functions), e2.propagate(constants, functions)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 / n2),
(e1, e2) => FieldElementExpression::Div(box e1, box e2),
}
},
FieldElementExpression::Pow(box e1, box e2) => {
match (e1.propagate(constants, functions), e2.propagate(constants, functions)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1.pow(n2)),
(e1, e2) => FieldElementExpression::Pow(box e1, box e2),
}
},
FieldElementExpression::IfElse(box condition, box consequence, box alternative) => {
let consequence = consequence.propagate(constants, functions);
let alternative = alternative.propagate(constants, functions);
match condition.propagate(constants, functions) {
BooleanExpression::Value(true) => consequence,
BooleanExpression::Value(false) => alternative,
c => FieldElementExpression::IfElse(box c, box consequence, box alternative)
}
},
FieldElementExpression::FunctionCall(id, arguments) => {
// propagation through function calls is handled after flattening, we only propagate arguments
let arguments = arguments.into_iter().map(|a| a.propagate(constants, functions)).collect();
FieldElementExpression::FunctionCall(id, arguments)
}
FieldElementExpression::Select(box array, box index) => {
let array = array.propagate(constants, functions);
let index = index.propagate(constants, functions);
match (array, index) {
(FieldElementArrayExpression::Value(size, v), FieldElementExpression::Number(n)) => {
let n_as_usize = n.to_dec_string().parse::<usize>().unwrap();
if n_as_usize < size {
v[n_as_usize].clone()
} else {
panic!(format!("out of bounds index ({} >= {}) found during static analysis", n_as_usize, size));
}
},
(FieldElementArrayExpression::Identifier(size, id), FieldElementExpression::Number(n)) => {
match constants.get(&TypedAssignee::ArrayElement(box TypedAssignee::Identifier(Variable::field_array(id.clone(), size)), box FieldElementExpression::Number(n.clone()).into())) {
Some(e) => match e {
TypedExpression::FieldElement(e) => e.clone(),
_ => panic!("")
},
None => FieldElementExpression::Select(box FieldElementArrayExpression::Identifier(size, id), box FieldElementExpression::Number(n))
}
},
(a, i) => FieldElementExpression::Select(box a, box i),
}
}
}
impl<T: Field> Folder<T> for Propagator<T> {
fn fold_function(&mut self, f: TypedFunction<T>) -> TypedFunction<T> {
self.constants = HashMap::new();
fold_function(self, f)
}
}
impl<T: Field> PropagateWithContext<T> for FieldElementArrayExpression<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> FieldElementArrayExpression<T> {
match self {
FieldElementArrayExpression::Identifier(size, id) => {
match constants.get(&TypedAssignee::Identifier(Variable::field_array(id.clone(), size))) {
Some(e) => match e {
TypedExpression::FieldElementArray(e) => e.clone(),
_ => panic!("constant stored for an array should be an array")
},
None => FieldElementArrayExpression::Identifier(size, id)
}
},
FieldElementArrayExpression::Value(size, exprs) => {
FieldElementArrayExpression::Value(size, exprs.into_iter().map(|e| e.propagate(constants, functions)).collect())
}
}
}
}
impl<T: Field> PropagateWithContext<T> for BooleanExpression<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> BooleanExpression<T> {
match self {
BooleanExpression::Value(v) => BooleanExpression::Value(v),
BooleanExpression::Identifier(id) => {
match constants.get(&TypedAssignee::Identifier(Variable::boolean(id.clone()))) {
Some(e) => match e {
TypedExpression::Boolean(e) => e.clone(),
_ => panic!("constant stored for a boolean should be a boolean")
},
None => BooleanExpression::Identifier(id)
}
},
BooleanExpression::Eq(box e1, box e2) => {
let e1 = e1.propagate(constants, functions);
let e2 = e2.propagate(constants, functions);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 == n2)
}
(e1, e2) => BooleanExpression::Eq(box e1, box e2)
}
}
BooleanExpression::Lt(box e1, box e2) => {
let e1 = e1.propagate(constants, functions);
let e2 = e2.propagate(constants, functions);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 < n2)
}
(e1, e2) => BooleanExpression::Lt(box e1, box e2)
}
}
BooleanExpression::Le(box e1, box e2) => {
let e1 = e1.propagate(constants, functions);
let e2 = e2.propagate(constants, functions);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 <= n2)
}
(e1, e2) => BooleanExpression::Le(box e1, box e2)
}
}
BooleanExpression::Gt(box e1, box e2) => {
let e1 = e1.propagate(constants, functions);
let e2 = e2.propagate(constants, functions);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 > n2)
}
(e1, e2) => BooleanExpression::Gt(box e1, box e2)
}
}
BooleanExpression::Ge(box e1, box e2) => {
let e1 = e1.propagate(constants, functions);
let e2 = e2.propagate(constants, functions);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 >= n2)
}
(e1, e2) => BooleanExpression::Ge(box e1, box e2)
}
}
}
}
}
impl<T: Field> TypedExpressionList<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> TypedExpressionList<T> {
match self {
TypedExpressionList::FunctionCall(id, arguments, types) => {
TypedExpressionList::FunctionCall(id, arguments.into_iter().map(|e| e.propagate(constants, functions)).collect(), types)
}
}
}
}
impl<T: Field> TypedStatement<T> {
fn propagate(self, constants: &mut HashMap<TypedAssignee<T>, TypedExpression<T>>, functions: &Vec<TypedFunction<T>>) -> Option<TypedStatement<T>> {
match self {
fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
let res = match s {
TypedStatement::Declaration(v) => Some(TypedStatement::Declaration(v)),
TypedStatement::Return(expressions) => Some(TypedStatement::Return(expressions.into_iter().map(|e| e.propagate(constants, functions)).collect())),
TypedStatement::Return(expressions) => Some(TypedStatement::Return(expressions.into_iter().map(|e| self.fold_expression(e)).collect())),
// propagation to the defined variable if rhs is a constant
TypedStatement::Definition(TypedAssignee::Identifier(var), expr) => {
match expr.propagate(constants, functions) {
match self.fold_expression(expr) {
e @ TypedExpression::Boolean(BooleanExpression::Value(..)) | e @ TypedExpression::FieldElement(FieldElementExpression::Number(..)) => {
constants.insert(TypedAssignee::Identifier(var), e);
self.constants.insert(TypedAssignee::Identifier(var), e);
None
},
TypedExpression::FieldElementArray(FieldElementArrayExpression::Value(size, array)) => {
@ -234,11 +50,11 @@ impl<T: Field> TypedStatement<T> {
}) {
true => {
// all elements of the array are constants
constants.insert(TypedAssignee::Identifier(var), FieldElementArrayExpression::Value(size, array).into());
return None;
self.constants.insert(TypedAssignee::Identifier(var), FieldElementArrayExpression::Value(size, array).into());
None
},
false => {
return Some(TypedStatement::Definition(TypedAssignee::Identifier(var), FieldElementArrayExpression::Value(size, array).into()));
Some(TypedStatement::Definition(TypedAssignee::Identifier(var), FieldElementArrayExpression::Value(size, array).into()))
}
}
},
@ -249,8 +65,8 @@ impl<T: Field> TypedStatement<T> {
},
// a[b] = c
TypedStatement::Definition(TypedAssignee::ArrayElement(box TypedAssignee::Identifier(var), box index), expr) => {
let index = index.propagate(constants, functions);
let expr = expr.propagate(constants, functions);
let index = self.fold_field_expression(index);
let expr = self.fold_expression(expr);
match (index, expr) {
(
@ -259,7 +75,7 @@ impl<T: Field> TypedStatement<T> {
) => {
// a[42] = 33
// -> store (a[42] -> 33) in the constants, possibly overwriting the previous entry
constants.entry(TypedAssignee::Identifier(var)).and_modify(|e| {
self.constants.entry(TypedAssignee::Identifier(var)).and_modify(|e| {
match *e {
TypedExpression::FieldElementArray(FieldElementArrayExpression::Value(size, ref mut v)) => {
let n_as_usize = n.to_dec_string().parse::<usize>().unwrap();
@ -277,7 +93,7 @@ impl<T: Field> TypedStatement<T> {
(index, expr) => {
// a[42] = e
// -> remove a from the constants as one of its elements is not constant
constants.remove(&TypedAssignee::Identifier(var.clone()));
self.constants.remove(&TypedAssignee::Identifier(var.clone()));
Some(TypedStatement::Definition(TypedAssignee::ArrayElement(box TypedAssignee::Identifier(var), box index), expr))
}
}
@ -286,42 +102,182 @@ impl<T: Field> TypedStatement<T> {
// propagate lhs and rhs for conditions
TypedStatement::Condition(e1, e2) => {
// could stop execution here if condition is known to fail
Some(TypedStatement::Condition(e1.propagate(constants, functions), e2.propagate(constants, functions)))
Some(TypedStatement::Condition(self.fold_expression(e1), self.fold_expression(e2)))
},
// we unrolled for loops in the previous step
TypedStatement::For(..) => panic!("for loop is unexpected, it should have been unrolled"),
TypedStatement::MultipleDefinition(variables, expression_list) => {
let expression_list = expression_list.propagate(constants, functions);
let expression_list = self.fold_expression_list(expression_list);
Some(TypedStatement::MultipleDefinition(variables, expression_list))
}
};
match res {
Some(v) => vec![v],
None => vec![]
}
}
}
impl<T: Field> Propagate<T> for TypedFunction<T> {
fn propagate(self, functions: &Vec<TypedFunction<T>>) -> TypedFunction<T> {
fn fold_field_expression(&mut self, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
match e {
FieldElementExpression::Identifier(id) => {
match self.constants.get(&TypedAssignee::Identifier(Variable::field_element(id.clone()))) {
Some(e) => match e {
TypedExpression::FieldElement(e) => e.clone(),
_ => panic!("constant stored for a field element should be a field element")
},
None => FieldElementExpression::Identifier(id)
}
},
FieldElementExpression::Add(box e1, box e2) => {
match (self.fold_field_expression(e1), self.fold_field_expression(e2)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 + n2),
(e1, e2) => FieldElementExpression::Add(box e1, box e2),
}
},
FieldElementExpression::Sub(box e1, box e2) => {
match (self.fold_field_expression(e1), self.fold_field_expression(e2)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 - n2),
(e1, e2) => FieldElementExpression::Sub(box e1, box e2),
}
},
FieldElementExpression::Mult(box e1, box e2) => {
match (self.fold_field_expression(e1), self.fold_field_expression(e2)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 * n2),
(e1, e2) => FieldElementExpression::Mult(box e1, box e2),
}
},
FieldElementExpression::Div(box e1, box e2) => {
match (self.fold_field_expression(e1), self.fold_field_expression(e2)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1 / n2),
(e1, e2) => FieldElementExpression::Div(box e1, box e2),
}
},
FieldElementExpression::Pow(box e1, box e2) => {
match (self.fold_field_expression(e1), self.fold_field_expression(e2)) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => FieldElementExpression::Number(n1.pow(n2)),
(e1, e2) => FieldElementExpression::Pow(box e1, box e2),
}
},
FieldElementExpression::IfElse(box condition, box consequence, box alternative) => {
let consequence = self.fold_field_expression(consequence);
let alternative = self.fold_field_expression(alternative);
match self.fold_boolean_expression(condition) {
BooleanExpression::Value(true) => consequence,
BooleanExpression::Value(false) => alternative,
c => FieldElementExpression::IfElse(box c, box consequence, box alternative)
}
},
FieldElementExpression::Select(box array, box index) => {
let array = self.fold_field_array_expression(array);
let index = self.fold_field_expression(index);
let mut constants = HashMap::new();
TypedFunction {
statements: self.statements.into_iter().filter_map(|s| s.propagate(&mut constants, functions)).collect(),
..self
match (array, index) {
(FieldElementArrayExpression::Value(size, v), FieldElementExpression::Number(n)) => {
let n_as_usize = n.to_dec_string().parse::<usize>().unwrap();
if n_as_usize < size {
v[n_as_usize].clone()
} else {
panic!(format!("out of bounds index ({} >= {}) found during static analysis", n_as_usize, size));
}
},
(FieldElementArrayExpression::Identifier(size, id), FieldElementExpression::Number(n)) => {
match self.constants.get(&TypedAssignee::ArrayElement(box TypedAssignee::Identifier(Variable::field_array(id.clone(), size)), box FieldElementExpression::Number(n.clone()).into())) {
Some(e) => match e {
TypedExpression::FieldElement(e) => e.clone(),
_ => panic!("")
},
None => FieldElementExpression::Select(box FieldElementArrayExpression::Identifier(size, id), box FieldElementExpression::Number(n))
}
},
(a, i) => FieldElementExpression::Select(box a, box i),
}
},
e => fold_field_expression(self, e)
}
}
}
impl<T: Field> TypedProg<T> {
pub fn propagate(self) -> TypedProg<T> {
let mut functions = vec![];
for f in self.functions {
let fun = f.propagate(&mut functions);
functions.push(fun);
fn fold_field_array_expression(&mut self, e: FieldElementArrayExpression<T>) -> FieldElementArrayExpression<T> {
match e {
FieldElementArrayExpression::Identifier(size, id) => {
match self.constants.get(&TypedAssignee::Identifier(Variable::field_array(id.clone(), size))) {
Some(e) => match e {
TypedExpression::FieldElementArray(e) => e.clone(),
_ => panic!("constant stored for an array should be an array")
},
None => FieldElementArrayExpression::Identifier(size, id)
}
},
e => fold_field_array_expression(self, e)
}
}
TypedProg {
functions,
..self
fn fold_boolean_expression(&mut self, e: BooleanExpression<T>) -> BooleanExpression<T> {
match e {
BooleanExpression::Identifier(id) => {
match self.constants.get(&TypedAssignee::Identifier(Variable::boolean(id.clone()))) {
Some(e) => match e {
TypedExpression::Boolean(e) => e.clone(),
_ => panic!("constant stored for a boolean should be a boolean")
},
None => BooleanExpression::Identifier(id)
}
},
BooleanExpression::Eq(box e1, box e2) => {
let e1 = self.fold_field_expression(e1);
let e2 = self.fold_field_expression(e2);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 == n2)
}
(e1, e2) => BooleanExpression::Eq(box e1, box e2)
}
}
BooleanExpression::Lt(box e1, box e2) => {
let e1 = self.fold_field_expression(e1);
let e2 = self.fold_field_expression(e2);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 < n2)
}
(e1, e2) => BooleanExpression::Lt(box e1, box e2)
}
}
BooleanExpression::Le(box e1, box e2) => {
let e1 = self.fold_field_expression(e1);
let e2 = self.fold_field_expression(e2);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 <= n2)
}
(e1, e2) => BooleanExpression::Le(box e1, box e2)
}
}
BooleanExpression::Gt(box e1, box e2) => {
let e1 = self.fold_field_expression(e1);
let e2 = self.fold_field_expression(e2);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 > n2)
}
(e1, e2) => BooleanExpression::Gt(box e1, box e2)
}
}
BooleanExpression::Ge(box e1, box e2) => {
let e1 = self.fold_field_expression(e1);
let e2 = self.fold_field_expression(e2);
match (e1, e2) {
(FieldElementExpression::Number(n1), FieldElementExpression::Number(n2)) => {
BooleanExpression::Value(n1 >= n2)
}
(e1, e2) => BooleanExpression::Ge(box e1, box e2)
}
},
e => fold_boolean_expression(self, e)
}
}
}
@ -346,7 +302,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(3))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(5)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(5)));
}
#[test]
@ -356,7 +312,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(1)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(1)));
}
#[test]
@ -366,7 +322,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(6)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(6)));
}
#[test]
@ -376,7 +332,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(3)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(3)));
}
#[test]
@ -386,7 +342,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(3))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(8)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(8)));
}
#[test]
@ -397,7 +353,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(3))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(2)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(2)));
}
#[test]
@ -408,7 +364,7 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(3))
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(3)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(3)));
}
#[test]
@ -421,7 +377,7 @@ mod tests {
),
);
assert_eq!(e.propagate(&mut HashMap::new(), &mut vec![]), FieldElementExpression::Number(FieldPrime::from(3)));
assert_eq!(Propagator::new().fold_field_expression(e), FieldElementExpression::Number(FieldPrime::from(3)));
}
}
@ -441,8 +397,8 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e_true.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(true));
assert_eq!(e_false.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(false));
assert_eq!(Propagator::new().fold_boolean_expression(e_true), BooleanExpression::Value(true));
assert_eq!(Propagator::new().fold_boolean_expression(e_false), BooleanExpression::Value(false));
}
#[test]
@ -457,8 +413,8 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e_true.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(true));
assert_eq!(e_false.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(false));
assert_eq!(Propagator::new().fold_boolean_expression(e_true), BooleanExpression::Value(true));
assert_eq!(Propagator::new().fold_boolean_expression(e_false), BooleanExpression::Value(false));
}
#[test]
@ -473,8 +429,8 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(2))
);
assert_eq!(e_true.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(true));
assert_eq!(e_false.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(false));
assert_eq!(Propagator::new().fold_boolean_expression(e_true), BooleanExpression::Value(true));
assert_eq!(Propagator::new().fold_boolean_expression(e_false), BooleanExpression::Value(false));
}
#[test]
@ -489,8 +445,8 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(5))
);
assert_eq!(e_true.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(true));
assert_eq!(e_false.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(false));
assert_eq!(Propagator::new().fold_boolean_expression(e_true), BooleanExpression::Value(true));
assert_eq!(Propagator::new().fold_boolean_expression(e_false), BooleanExpression::Value(false));
}
#[test]
@ -505,8 +461,8 @@ mod tests {
box FieldElementExpression::Number(FieldPrime::from(5))
);
assert_eq!(e_true.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(true));
assert_eq!(e_false.propagate(&mut HashMap::new(), &mut vec![]), BooleanExpression::Value(false));
assert_eq!(Propagator::new().fold_boolean_expression(e_true), BooleanExpression::Value(true));
assert_eq!(Propagator::new().fold_boolean_expression(e_false), BooleanExpression::Value(false));
}
}
}
@ -544,11 +500,10 @@ mod tests {
FieldElementExpression::Number(FieldPrime::from(42)).into()
);
let mut constants = HashMap::new();
let mut functions = vec![];
let mut p = Propagator::new();
declaration.propagate(&mut constants, &mut functions);
definition.propagate(&mut constants, &mut functions);
p.fold_statement(declaration);
p.fold_statement(definition);
let expected_value: TypedExpression<FieldPrime> = FieldElementArrayExpression::Value(
2,
vec![
@ -556,11 +511,11 @@ mod tests {
FieldElementExpression::Number(FieldPrime::from(22))
]).into();
assert_eq!(constants.get(&TypedAssignee::Identifier(
assert_eq!(p.constants.get(&TypedAssignee::Identifier(
Variable::field_array("a", 2)
)).unwrap(), &expected_value);
overwrite.propagate(&mut constants, &mut functions);
p.fold_statement(overwrite);
let expected_value: TypedExpression<FieldPrime> = FieldElementArrayExpression::Value(
2,
vec![
@ -568,7 +523,7 @@ mod tests {
FieldElementExpression::Number(FieldPrime::from(42))
]).into();
assert_eq!(constants.get(&TypedAssignee::Identifier(
assert_eq!(p.constants.get(&TypedAssignee::Identifier(
Variable::field_array("a", 2)
)).unwrap(), &expected_value);
}
@ -597,13 +552,12 @@ mod tests {
FieldElementExpression::Number(FieldPrime::from(42)).into()
);
let mut constants = HashMap::new();
let mut functions = vec![];
let mut p = Propagator::new();
declaration.propagate(&mut constants, &mut functions);
overwrite.propagate(&mut constants, &mut functions);
p.fold_statement(declaration);
p.fold_statement(overwrite);
assert_eq!(constants.get(&TypedAssignee::Identifier(
assert_eq!(p.constants.get(&TypedAssignee::Identifier(
Variable::field_array("a", 2)
)), None);
}

View file

@ -4,111 +4,80 @@
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
//! @date 2018
use absy::parameter::Parameter;
use absy::variable::Variable;
use std::collections::HashMap;
use field::Field;
use typed_absy::*;
use typed_absy::folder::*;
use types::Type;
pub trait Unroll {
fn unroll(self) -> Self;
pub struct Unroller {
substitution: HashMap<String, usize>
}
pub trait UnrollWithContext<T: Field> {
fn unroll(self, substitution: &mut HashMap<String, usize>) -> Self;
}
impl<T: Field> TypedExpression<T> {
fn unroll(self, substitution: &HashMap<String, usize>) -> TypedExpression<T> {
match self {
TypedExpression::FieldElement(e) => e.unroll(substitution).into(),
TypedExpression::Boolean(e) => e.unroll(substitution).into(),
TypedExpression::FieldElementArray(e) => e.unroll(substitution).into(),
impl Unroller {
fn new() -> Self {
Unroller {
substitution: HashMap::new()
}
}
}
impl<T: Field> FieldElementExpression<T> {
fn unroll(self, substitution: &HashMap<String, usize>) -> FieldElementExpression<T> {
match self {
FieldElementExpression::Identifier(id) => FieldElementExpression::Identifier(format!("{}_{}", id, substitution.get(&id).unwrap().clone())),
FieldElementExpression::Number(n) => FieldElementExpression::Number(n),
FieldElementExpression::Add(box e1, box e2) => FieldElementExpression::Add(box e1.unroll(substitution), box e2.unroll(substitution)),
FieldElementExpression::Sub(box e1, box e2) => FieldElementExpression::Sub(box e1.unroll(substitution), box e2.unroll(substitution)),
FieldElementExpression::Mult(box e1, box e2) => FieldElementExpression::Mult(box e1.unroll(substitution), box e2.unroll(substitution)),
FieldElementExpression::Div(box e1, box e2) => FieldElementExpression::Div(box e1.unroll(substitution), box e2.unroll(substitution)),
FieldElementExpression::Pow(box e1, box e2) => FieldElementExpression::Div(box e1.unroll(substitution), box e2.unroll(substitution)),
FieldElementExpression::IfElse(box cond, box cons, box alt) => FieldElementExpression::IfElse(box cond.unroll(substitution), box cons.unroll(substitution), box alt.unroll(substitution)),
FieldElementExpression::FunctionCall(id, args) => FieldElementExpression::FunctionCall(id, args.into_iter().map(|a| a.unroll(substitution)).collect()),
FieldElementExpression::Select(box array, box index) => FieldElementExpression::Select(box array.unroll(substitution), box index.unroll(substitution)),
fn get_current_ssa_variable(&self, v: Variable) -> Variable {
Variable {
id: format!("{}_{}", v.id, self.substitution.get(&v.id).unwrap()),
..v
}
}
}
impl<T: Field> FieldElementArrayExpression<T> {
fn unroll(self, substitution: &HashMap<String, usize>) -> FieldElementArrayExpression<T> {
match self {
FieldElementArrayExpression::Identifier(size, id) => FieldElementArrayExpression::Identifier(size, format!("{}_{}", id, substitution.get(&id).unwrap().clone())),
FieldElementArrayExpression::Value(size, v) => FieldElementArrayExpression::Value(size, v.into_iter().map(|e| e.unroll(substitution)).collect()),
}
}
}
impl<T: Field> BooleanExpression<T> {
fn unroll(self, substitution: &HashMap<String, usize>) -> BooleanExpression<T> {
match self {
BooleanExpression::Identifier(id) => BooleanExpression::Identifier(format!("{}_{}", id, substitution.get(&id).unwrap().clone())),
BooleanExpression::Value(v) => BooleanExpression::Value(v),
BooleanExpression::Eq(box e1, box e2) => BooleanExpression::Eq(box e1.unroll(substitution), box e2.unroll(substitution)),
BooleanExpression::Lt(box e1, box e2) => BooleanExpression::Lt(box e1.unroll(substitution), box e2.unroll(substitution)),
BooleanExpression::Le(box e1, box e2) => BooleanExpression::Le(box e1.unroll(substitution), box e2.unroll(substitution)),
BooleanExpression::Gt(box e1, box e2) => BooleanExpression::Gt(box e1.unroll(substitution), box e2.unroll(substitution)),
BooleanExpression::Ge(box e1, box e2) => BooleanExpression::Ge(box e1.unroll(substitution), box e2.unroll(substitution)),
}
}
}
impl<T: Field> TypedExpressionList<T> {
fn unroll(self, substitution: &HashMap<String, usize>) -> TypedExpressionList<T> {
match self {
TypedExpressionList::FunctionCall(id, arguments, types) => {
TypedExpressionList::FunctionCall(id, arguments.into_iter().map(|a| a.unroll(substitution)).collect(), types)
fn issue_next_ssa_variable(&mut self, v: Variable) -> Variable {
let res = match self.substitution.get(&v.id) {
Some(i) => {
Variable { id: format!("{}_{}", v.id, i + 1), ..v}
},
None => {
Variable { id: format!("{}_{}", v.id, 0), ..v}
}
}
};
self.substitution.entry(v.id)
.and_modify(|e| { *e += 1 })
.or_insert(0);
res
}
pub fn unroll<T: Field>(p: TypedProg<T>) -> TypedProg<T> {
Unroller::new().fold_program(p)
}
}
impl<T: Field> TypedStatement<T> {
fn unroll(self, substitution: &mut HashMap<String, usize>) -> Vec<TypedStatement<T>> {
match self {
impl<T: Field> Folder<T> for Unroller {
fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
match s {
TypedStatement::Declaration(_) => {
vec![]
},
TypedStatement::Definition(TypedAssignee::Identifier(variable), expr) => {
let expr = expr.unroll(substitution);
let expr = self.fold_expression(expr);
let res = match substitution.get(&variable.id) {
Some(i) => {
vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable { id: format!("{}_{}", variable.id, i + 1), ..variable}), expr)]
},
None => {
vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable { id: format!("{}_{}", variable.id, 0), ..variable}), expr)]
}
};
substitution.entry(variable.id)
.and_modify(|e| { *e += 1 })
.or_insert(0);
res
vec![TypedStatement::Definition(TypedAssignee::Identifier(self.issue_next_ssa_variable(variable)), expr)]
},
TypedStatement::Definition(TypedAssignee::ArrayElement(box TypedAssignee::Identifier(variable), box index), expr) => {
let expr = expr.unroll(substitution);
let index = index.unroll(substitution);
TypedStatement::Definition(TypedAssignee::ArrayElement(array @ box TypedAssignee::Identifier(..), box index), expr) => {
let expr = self.fold_expression(expr);
let index = self.fold_field_expression(index);
let current_array = self.fold_assignee(*array.clone());
let array_size = match variable.get_type() {
let current_ssa_variable = match current_array {
TypedAssignee::Identifier(v) => v,
_ => panic!("assignee should be an identifier")
};
let original_variable = match *array {
TypedAssignee::Identifier(v) => v,
_ => panic!("assignee should be an identifier")
};
let array_size = match original_variable.get_type() {
Type::FieldElementArray(size) => size,
_ => panic!("array identifier has to be a field element array")
_ => panic!("array identifier should be a field element array")
};
let expr = match expr {
@ -116,56 +85,35 @@ impl<T: Field> TypedStatement<T> {
_ => panic!("right side of array element definition must be a field element")
};
let res = match substitution.get(&variable.id) {
Some(ssa_id) => {
let new_array = FieldElementArrayExpression::Value(array_size, (0..array_size)
.map(|i|
FieldElementExpression::IfElse(
box BooleanExpression::Eq(
box index.clone(),
box FieldElementExpression::Number(T::from(i))
),
box expr.clone(),
box FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(array_size, format!("{}_{}", variable.id, ssa_id)),
box FieldElementExpression::Number(T::from(i))
)
)
let new_variable = self.issue_next_ssa_variable(original_variable);
let new_array = FieldElementArrayExpression::Value(array_size, (0..array_size)
.map(|i|
FieldElementExpression::IfElse(
box BooleanExpression::Eq(
box index.clone(),
box FieldElementExpression::Number(T::from(i))
),
box expr.clone(),
box FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(array_size, current_ssa_variable.id.clone()),
box FieldElementExpression::Number(T::from(i))
)
.collect());
vec![
TypedStatement::Definition(TypedAssignee::Identifier(Variable { id: format!("{}_{}", variable.id, ssa_id + 1), ..variable.clone()}), new_array.into()),
]
},
None => {
vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable { id: format!("{}_{}", variable.id, 0), ..variable}), expr.into())]
}
};
substitution.entry(variable.id)
.and_modify(|e| { *e += 1 })
.or_insert(0);
res
)
)
.collect()
);
vec![
TypedStatement::Definition(TypedAssignee::Identifier(new_variable), new_array.into()),
]
},
TypedStatement::MultipleDefinition(variables, exprs) => {
let exprs = exprs.unroll(substitution);
let variables = variables.into_iter().map(|v| {
let res = match substitution.get(&v.id) {
Some(i) => {
Variable { id: format!("{}_{}", v.id, i + 1), ..v}
},
None => {
Variable { id: format!("{}_{}", v.id, 0), ..v}
}
};
substitution.entry(v.id)
.and_modify(|e| { *e += 1 })
.or_insert(0);
res
}).collect();
let exprs = self.fold_expression_list(exprs);
let variables = variables.into_iter().map(|v| self.issue_next_ssa_variable(v)).collect();
vec![TypedStatement::MultipleDefinition(variables, exprs)]
},
TypedStatement::Condition(e1, e2) => vec![TypedStatement::Condition(e1.unroll(substitution), e2.unroll(substitution))],
TypedStatement::For(v, from, to, stats) => {
let mut values: Vec<T> = vec![];
let mut current = from;
@ -182,49 +130,25 @@ impl<T: Field> TypedStatement<T> {
],
stats.clone()
].into_iter().flat_map(|x| x)
}).flat_map(|x| x).flat_map(|x| x.unroll(substitution)).collect();
}).flat_map(|x| x).flat_map(|x| self.fold_statement(x)).collect();
res
}
TypedStatement::Return(exprs) => {
vec![TypedStatement::Return(exprs.into_iter().map(|e| e.unroll(substitution)).collect())]
},
_ => vec![self]
s => fold_statement(self, s)
}
}
}
impl<T: Field> Unroll for TypedFunction<T> {
fn unroll(self) -> TypedFunction<T> {
let mut substitution = HashMap::new();
let arguments = self.arguments.into_iter().map(|p|
Parameter {
id: Variable {
id: format!("{}_{}", p.id.id.clone(), substitution.entry(p.id.id)
.and_modify(|e| { *e += 1 })
.or_insert(0)),
..p.id
},
..p
}
).collect();
TypedFunction {
arguments: arguments,
statements: self.statements.into_iter().flat_map(|s| s.unroll(&mut substitution)).collect(),
..self
fn fold_function(&mut self, f: TypedFunction<T>) -> TypedFunction<T> {
self.substitution = HashMap::new();
for arg in &f.arguments {
self.substitution.insert(arg.id.id.clone(), 0);
}
fold_function(self, f)
}
}
impl<T: Field> Unroll for TypedProg<T> {
fn unroll(self) -> TypedProg<T> {
TypedProg {
functions: self.functions.into_iter().map(|f| f.unroll()).collect(),
..self
}
fn fold_name(&mut self, n: String) -> String {
format!("{}_{}", n, self.substitution.get(&n).unwrap())
}
}
@ -239,6 +163,18 @@ mod tests {
#[test]
fn for_loop() {
// for field i in 2..5
// field foo = i
// should be unrolled to
// i_0 = 2
// foo_0 = i_0
// i_1 = 3
// foo_1 = i_1
// i_2 = 4
// foo_2 = i_2
let s = TypedStatement::For(Variable::field_element("i"), FieldPrime::from(2), FieldPrime::from(5), vec![
TypedStatement::Declaration(Variable::field_element("foo")),
TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("foo")), FieldElementExpression::Identifier(String::from("i")).into())]
@ -255,7 +191,9 @@ mod tests {
TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("foo_2")), FieldElementExpression::Identifier(String::from("i_2")).into()),
];
assert_eq!(s.unroll(&mut HashMap::new()), expected);
let mut u = Unroller::new();
assert_eq!(u.fold_statement(s), expected);
}
#[test]
@ -271,19 +209,19 @@ mod tests {
// a_1 = 6
// a_1
let mut substitution = HashMap::new();
let mut u = Unroller::new();
let s: TypedStatement<FieldPrime> = TypedStatement::Declaration(Variable::field_element("a"));
assert_eq!(s.unroll(&mut substitution), vec![]);
assert_eq!(u.fold_statement(s), vec![]);
let s = TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a")), FieldElementExpression::Number(FieldPrime::from(5)).into());
assert_eq!(s.unroll(&mut substitution), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(5)).into())]);
assert_eq!(u.fold_statement(s), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(5)).into())]);
let s = TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a")), FieldElementExpression::Number(FieldPrime::from(6)).into());
assert_eq!(s.unroll(&mut substitution), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_1")), FieldElementExpression::Number(FieldPrime::from(6)).into())]);
assert_eq!(u.fold_statement(s), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_1")), FieldElementExpression::Number(FieldPrime::from(6)).into())]);
let e: FieldElementExpression<FieldPrime> = FieldElementExpression::Identifier(String::from("a"));
assert_eq!(e.unroll(&mut substitution), FieldElementExpression::Identifier(String::from("a_1")));
assert_eq!(u.fold_field_expression(e), FieldElementExpression::Identifier(String::from("a_1")));
}
#[test]
@ -297,13 +235,13 @@ mod tests {
// a_0 = 5
// a_1 = a_0 + 1
let mut substitution = HashMap::new();
let mut u = Unroller::new();
let s: TypedStatement<FieldPrime> = TypedStatement::Declaration(Variable::field_element("a"));
assert_eq!(s.unroll(&mut substitution), vec![]);
assert_eq!(u.fold_statement(s), vec![]);
let s = TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a")), FieldElementExpression::Number(FieldPrime::from(5)).into());
assert_eq!(s.unroll(&mut substitution), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(5)).into())]);
assert_eq!(u.fold_statement(s), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(5)).into())]);
let s = TypedStatement::Definition(
TypedAssignee::Identifier(Variable::field_element("a")),
@ -313,7 +251,7 @@ mod tests {
).into()
);
assert_eq!(
s.unroll(&mut substitution),
u.fold_statement(s),
vec![
TypedStatement::Definition(
TypedAssignee::Identifier(Variable::field_element("a_1")),
@ -339,13 +277,13 @@ mod tests {
// a_0 = 2
// a_1 = foo(a_0)
let mut substitution = HashMap::new();
let mut u = Unroller::new();
let s: TypedStatement<FieldPrime> = TypedStatement::Declaration(Variable::field_element("a"));
assert_eq!(s.unroll(&mut substitution), vec![]);
assert_eq!(u.fold_statement(s), vec![]);
let s = TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a")), FieldElementExpression::Number(FieldPrime::from(2)).into());
assert_eq!(s.unroll(&mut substitution), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(2)).into())]);
assert_eq!(u.fold_statement(s), vec![TypedStatement::Definition(TypedAssignee::Identifier(Variable::field_element("a_0")), FieldElementExpression::Number(FieldPrime::from(2)).into())]);
let s: TypedStatement<FieldPrime> = TypedStatement::MultipleDefinition(
vec![Variable::field_element("a")],
@ -356,7 +294,7 @@ mod tests {
)
);
assert_eq!(
s.unroll(&mut substitution),
u.fold_statement(s),
vec![
TypedStatement::MultipleDefinition(
vec![Variable::field_element("a_1")],
@ -369,5 +307,91 @@ mod tests {
]
);
}
#[test]
fn incremental_array_definition() {
// field[2] a = [1, 1]
// a[1] = 2
// should be turned into
// a_0 = [1, 1]
// a_1 = [if 0 == 1 then 2 else a_0[0], if 1 == 1 then 2 else a_0[1]]
let mut u = Unroller::new();
let s: TypedStatement<FieldPrime> = TypedStatement::Declaration(Variable::field_array("a", 2));
assert_eq!(u.fold_statement(s), vec![]);
let s = TypedStatement::Definition(
TypedAssignee::Identifier(
Variable::field_array("a", 2)
),
FieldElementArrayExpression::Value(
2,
vec![
FieldElementExpression::Number(FieldPrime::from(1)),
FieldElementExpression::Number(FieldPrime::from(1))
]
).into()
);
assert_eq!(
u.fold_statement(s),
vec![
TypedStatement::Definition(
TypedAssignee::Identifier(
Variable::field_array("a_0", 2)
),
FieldElementArrayExpression::Value(
2,
vec![
FieldElementExpression::Number(FieldPrime::from(1)),
FieldElementExpression::Number(FieldPrime::from(1))
]
).into()
)
]
);
let s: TypedStatement<FieldPrime> = TypedStatement::Definition(
TypedAssignee::ArrayElement(box TypedAssignee::Identifier(Variable::field_array("a", 2)), box FieldElementExpression::Number(FieldPrime::from(1))),
FieldElementExpression::Number(FieldPrime::from(2)).into()
);
assert_eq!(
u.fold_statement(s),
vec![
TypedStatement::Definition(
TypedAssignee::Identifier(Variable::field_array("a_1", 2)),
FieldElementArrayExpression::Value(
2,
vec![
FieldElementExpression::IfElse(
box BooleanExpression::Eq(
box FieldElementExpression::Number(FieldPrime::from(1)), box FieldElementExpression::Number(FieldPrime::from(0))
),
box FieldElementExpression::Number(FieldPrime::from(2)),
box FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(2, String::from("a_0")),
box FieldElementExpression::Number(FieldPrime::from(0))
),
),
FieldElementExpression::IfElse(
box BooleanExpression::Eq(
box FieldElementExpression::Number(FieldPrime::from(1)), box FieldElementExpression::Number(FieldPrime::from(1))
),
box FieldElementExpression::Number(FieldPrime::from(2)),
box FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(2, String::from("a_0")),
box FieldElementExpression::Number(FieldPrime::from(1))
),
),
]
).into()
)
]
);
}
}
}

View file

@ -0,0 +1,189 @@
// Generic walk through a typed AST. Not mutating in place
use absy::variable::Variable;
use field::Field;
use typed_absy::*;
pub trait Folder<T: Field> : Sized {
fn fold_program(&mut self, p: TypedProg<T>) -> TypedProg<T> {
fold_program(self, p)
}
fn fold_function(&mut self, f: TypedFunction<T>) -> TypedFunction<T> {
fold_function(self, f)
}
fn fold_parameter(&mut self, p: Parameter) -> Parameter {
Parameter {
id: self.fold_variable(p.id),
..p
}
}
fn fold_name(&mut self, n: String) -> String { n }
fn fold_variable(&mut self, v: Variable) -> Variable {
Variable {
id: self.fold_name(v.id),
..v
}
}
fn fold_assignee(&mut self, a: TypedAssignee<T>) -> TypedAssignee<T> {
match a {
TypedAssignee::Identifier(v) => TypedAssignee::Identifier(self.fold_variable(v)),
TypedAssignee::ArrayElement(box a, box index) => TypedAssignee::ArrayElement(box self.fold_assignee(a), box self.fold_field_expression(index))
}
}
fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
fold_statement(self, s)
}
fn fold_expression(&mut self, e: TypedExpression<T>) -> TypedExpression<T> {
match e {
TypedExpression::FieldElement(e) => self.fold_field_expression(e).into(),
TypedExpression::Boolean(e) => self.fold_boolean_expression(e).into(),
TypedExpression::FieldElementArray(e) => self.fold_field_array_expression(e).into(),
}
}
fn fold_expression_list(&mut self, es: TypedExpressionList<T>) -> TypedExpressionList<T> {
match es {
TypedExpressionList::FunctionCall(id, arguments, types) => {
TypedExpressionList::FunctionCall(id, arguments.into_iter().map(|a| self.fold_expression(a)).collect(), types)
}
}
}
fn fold_field_expression(&mut self, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
fold_field_expression(self, e)
}
fn fold_boolean_expression(&mut self, e: BooleanExpression<T>) -> BooleanExpression<T> {
fold_boolean_expression(self, e)
}
fn fold_field_array_expression(&mut self, e: FieldElementArrayExpression<T>) -> FieldElementArrayExpression<T> {
fold_field_array_expression(self, e)
}
}
pub fn fold_program<T: Field, F: Folder<T>>(f: &mut F, p: TypedProg<T>) -> TypedProg<T> {
TypedProg {
functions: p.functions.into_iter().map(|fun| f.fold_function(fun)).collect(),
..p
}
}
pub fn fold_statement<T: Field, F: Folder<T>>(f: &mut F, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
let res = match s {
TypedStatement::Return(expressions) => TypedStatement::Return(expressions.into_iter().map(|e| f.fold_expression(e)).collect()),
TypedStatement::Definition(a, e) => TypedStatement::Definition(f.fold_assignee(a), f.fold_expression(e)),
TypedStatement::Declaration(v) => TypedStatement::Declaration(f.fold_variable(v)),
TypedStatement::Condition(left, right) => TypedStatement::Condition(f.fold_expression(left), f.fold_expression(right)),
TypedStatement::For(v, from, to, statements) => TypedStatement::For(f.fold_variable(v), from, to, statements.into_iter().flat_map(|s| f.fold_statement(s)).collect()),
TypedStatement::MultipleDefinition(variables, elist) => TypedStatement::MultipleDefinition(variables.into_iter().map(|v| f.fold_variable(v)).collect(), f.fold_expression_list(elist)),
};
vec![res]
}
pub fn fold_field_array_expression<T: Field, F: Folder<T>>(f: &mut F, e: FieldElementArrayExpression<T>) -> FieldElementArrayExpression<T> {
match e {
FieldElementArrayExpression::Identifier(size, id) => {
FieldElementArrayExpression::Identifier(size, f.fold_name(id))
},
FieldElementArrayExpression::Value(size, exprs) => {
FieldElementArrayExpression::Value(size, exprs.into_iter().map(|e| f.fold_field_expression(e)).collect())
}
FieldElementArrayExpression::FunctionCall(size, id, exps) => {
let exps = exps.into_iter().map(|e| f.fold_expression(e)).collect();
FieldElementArrayExpression::FunctionCall(size, id, exps)
}
}
}
pub fn fold_field_expression<T: Field, F: Folder<T>>(f: &mut F, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
match e {
FieldElementExpression::Number(n) => FieldElementExpression::Number(n),
FieldElementExpression::Identifier(id) => FieldElementExpression::Identifier(f.fold_name(id)),
FieldElementExpression::Add(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
FieldElementExpression::Add(box e1, box e2)
},
FieldElementExpression::Sub(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
FieldElementExpression::Sub(box e1, box e2)
},
FieldElementExpression::Mult(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
FieldElementExpression::Mult(box e1, box e2)
},
FieldElementExpression::Div(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
FieldElementExpression::Div(box e1, box e2)
},
FieldElementExpression::Pow(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
FieldElementExpression::Pow(box e1, box e2)
},
FieldElementExpression::IfElse(box cond, box cons, box alt) => {
let cond = f.fold_boolean_expression(cond);
let cons = f.fold_field_expression(cons);
let alt = f.fold_field_expression(alt);
FieldElementExpression::IfElse(box cond, box cons, box alt)
},
FieldElementExpression::FunctionCall(id, exps) => {
let exps = exps.into_iter().map(|e| f.fold_expression(e)).collect();
FieldElementExpression::FunctionCall(id, exps)
},
FieldElementExpression::Select(box array, box index) => {
let array = f.fold_field_array_expression(array);
let index = f.fold_field_expression(index);
FieldElementExpression::Select(box array, box index)
},
}
}
pub fn fold_boolean_expression<T: Field, F: Folder<T>>(f: &mut F, e: BooleanExpression<T>) -> BooleanExpression<T> {
match e {
BooleanExpression::Value(v) => BooleanExpression::Value(v),
BooleanExpression::Identifier(id) => BooleanExpression::Identifier(f.fold_name(id)),
BooleanExpression::Eq(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
BooleanExpression::Eq(box e1, box e2)
}
BooleanExpression::Lt(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
BooleanExpression::Lt(box e1, box e2)
}
BooleanExpression::Le(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
BooleanExpression::Le(box e1, box e2)
}
BooleanExpression::Gt(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
BooleanExpression::Gt(box e1, box e2)
}
BooleanExpression::Ge(box e1, box e2) => {
let e1 = f.fold_field_expression(e1);
let e2 = f.fold_field_expression(e2);
BooleanExpression::Ge(box e1, box e2)
}
}
}
pub fn fold_function<T: Field, F: Folder<T>>(f: &mut F, fun: TypedFunction<T>) -> TypedFunction<T> {
TypedFunction {
arguments: fun.arguments.into_iter().map(|a| f.fold_parameter(a)).collect(),
statements: fun.statements.into_iter().flat_map(|s| f.fold_statement(s)).collect(),
..fun
}
}

View file

@ -5,6 +5,8 @@
//! @author Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>
//! @date 2017
pub mod folder;
use types::Signature;
use absy::parameter::Parameter;
use absy::variable::Variable;
@ -15,6 +17,8 @@ use imports::Import;
use flat_absy::*;
use types::Type;
pub use self::folder::Folder;
#[derive(Serialize, Deserialize, Clone, PartialEq)]
pub struct TypedProg<T: Field> {
/// Functions of the program
@ -309,6 +313,7 @@ impl<T: Field> Typed for TypedExpression<T> {
TypedExpression::FieldElement(_) => Type::FieldElement,
TypedExpression::FieldElementArray(FieldElementArrayExpression::Identifier(n, _)) => Type::FieldElementArray(n),
TypedExpression::FieldElementArray(FieldElementArrayExpression::Value(n, _)) => Type::FieldElementArray(n),
TypedExpression::FieldElementArray(FieldElementArrayExpression::FunctionCall(n, ..)) => Type::FieldElementArray(n),
}
}
}
@ -361,12 +366,13 @@ pub enum BooleanExpression<T: Field> {
pub enum FieldElementArrayExpression<T: Field> {
Identifier(usize, String),
Value(usize, Vec<FieldElementExpression<T>>),
FunctionCall(usize, String, Vec<TypedExpression<T>>),
}
impl<T: Field> FieldElementArrayExpression<T> {
pub fn size(&self) -> usize {
match *self {
FieldElementArrayExpression::Identifier(s, _) | FieldElementArrayExpression::Value(s, _) => s
FieldElementArrayExpression::Identifier(s, _) | FieldElementArrayExpression::Value(s, _) | FieldElementArrayExpression::FunctionCall(s, ..) => s
}
}
}
@ -424,6 +430,16 @@ impl<T: Field> fmt::Display for FieldElementArrayExpression<T> {
match *self {
FieldElementArrayExpression::Identifier(_, ref var) => write!(f, "{}", var),
FieldElementArrayExpression::Value(_, ref values) => write!(f, "[{}]", values.iter().map(|o| o.to_string()).collect::<Vec<String>>().join(", ")),
FieldElementArrayExpression::FunctionCall(_, ref i, ref p) => {
try!(write!(f, "{}(", i,));
for (i, param) in p.iter().enumerate() {
try!(write!(f, "{}", param));
if i < p.len() - 1 {
try!(write!(f, ", "));
}
}
write!(f, ")")
},
}
}
}
@ -468,6 +484,11 @@ impl<T: Field> fmt::Debug for FieldElementArrayExpression<T> {
match *self {
FieldElementArrayExpression::Identifier(_, ref var) => write!(f, "{:?}", var),
FieldElementArrayExpression::Value(_, ref values) => write!(f, "{:?}", values),
FieldElementArrayExpression::FunctionCall(_, ref i, ref p) => {
try!(write!(f, "FunctionCall({:?}, (", i));
try!(f.debug_list().entries(p.iter()).finish());
write!(f, ")")
}
}
}
}
@ -500,3 +521,9 @@ impl<T: Field> fmt::Debug for TypedExpressionList<T> {
}
}
}
impl<T: Field> TypedFunction<T> {
pub fn to_slug(&self) -> String {
format!("{}_{}", self.id, self.signature.to_slug())
}
}