Merge pull request #9 from AleoHQ/feature/mutability

Feature/mutability
This commit is contained in:
Collin Chin 2020-05-14 10:54:42 -07:00 committed by GitHub
commit 4997d6e13c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 610 additions and 572 deletions

View File

@ -2,6 +2,18 @@ circuit Circ {
x: u32
}
function main() {
let c = Circ { x: 4 };
function main() -> u32 {
let mut a = 1;
a = 0;
let b = 1;
//b = 0; // <- illegal
let mut arr = [1, 2];
arr[0] = 0;
let mut c = Circ { x: 1 };
c.x = 0;
return c.x
}

View File

@ -26,6 +26,23 @@ lazy_static! {
static ref PRECEDENCE_CLIMBER: PrecClimber<Rule> = precedence_climber();
}
// Identifiers
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::identifier))]
pub struct Identifier<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Identifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
// Visibility
#[derive(Clone, Debug, FromPest, PartialEq)]
@ -176,7 +193,7 @@ pub struct BooleanType<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::type_circuit))]
pub struct CircuitType<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -324,35 +341,35 @@ impl<'ast> fmt::Display for Value<'ast> {
}
}
// Variables
// Variables + Mutability
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::mutable))]
pub struct Mutable {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::variable))]
pub struct Variable<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
pub mutable: Option<Mutable>,
pub identifier: Identifier<'ast>,
pub _type: Option<Type<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Variable<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
if let Some(ref _mutable) = self.mutable {
write!(f, "mut ")?;
}
#[derive(Debug, FromPest, PartialEq, Clone)]
#[pest_ast(rule(Rule::optionally_typed_variable))]
pub struct OptionallyTypedVariable<'ast> {
pub _type: Option<Type<'ast>>,
pub id: Variable<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
write!(f, "{}", self.identifier)?;
impl<'ast> fmt::Display for OptionallyTypedVariable<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.id)
if let Some(ref _type) = self._type {
write!(f, ": {}", _type)?;
}
write!(f, "")
}
}
@ -423,7 +440,7 @@ pub struct ArrayAccess<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::access_member))]
pub struct MemberAccess<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -439,7 +456,7 @@ pub enum Access<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_postfix))]
pub struct PostfixExpression<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub accesses: Vec<Access<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -456,7 +473,7 @@ impl<'ast> fmt::Display for AssigneeAccess<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AssigneeAccess::Array(ref array) => write!(f, "[{}]", array.expression),
AssigneeAccess::Member(ref member) => write!(f, ".{}", member.variable),
AssigneeAccess::Member(ref member) => write!(f, ".{}", member.identifier),
}
}
}
@ -464,7 +481,7 @@ impl<'ast> fmt::Display for AssigneeAccess<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::assignee))]
pub struct Assignee<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub accesses: Vec<AssigneeAccess<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -472,7 +489,7 @@ pub struct Assignee<'ast> {
impl<'ast> fmt::Display for Assignee<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.variable)?;
write!(f, "{}", self.identifier)?;
for (i, access) in self.accesses.iter().enumerate() {
write!(f, "{}", access)?;
if i < self.accesses.len() - 1 {
@ -537,7 +554,7 @@ pub struct ArrayInitializerExpression<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::circuit_object))]
pub struct CircuitObject<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub _type: Type<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -546,7 +563,7 @@ pub struct CircuitObject<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::circuit_definition))]
pub struct Circuit<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub fields: Vec<CircuitObject<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -555,7 +572,7 @@ pub struct Circuit<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::inline_circuit_member))]
pub struct InlineCircuitMember<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -564,7 +581,7 @@ pub struct InlineCircuitMember<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_inline_circuit))]
pub struct CircuitInlineExpression<'ast> {
pub variable: Variable<'ast>,
pub identifier: Identifier<'ast>,
pub members: Vec<InlineCircuitMember<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -618,7 +635,7 @@ pub struct TernaryExpression<'ast> {
#[derive(Clone, Debug, PartialEq)]
pub enum Expression<'ast> {
Value(Value<'ast>),
Variable(Variable<'ast>),
Identifier(Identifier<'ast>),
Not(NotExpression<'ast>),
// Increment(IncrementExpression<'ast>),
// Decrement(DecrementExpression<'ast>),
@ -662,7 +679,7 @@ impl<'ast> Expression<'ast> {
pub fn span(&self) -> &Span<'ast> {
match self {
Expression::Value(expression) => &expression.span(),
Expression::Variable(expression) => &expression.span,
Expression::Identifier(expression) => &expression.span,
Expression::Not(expression) => &expression.span,
// Expression::Increment(expression) => &expression.span,
// Expression::Decrement(expression) => &expression.span,
@ -680,7 +697,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expression::Value(ref expression) => write!(f, "{}", expression),
Expression::Variable(ref expression) => write!(f, "{}", expression),
Expression::Identifier(ref expression) => write!(f, "{}", expression),
Expression::Not(ref expression) => write!(f, "!{}", expression.expression),
// Expression::Increment(ref expression) => write!(f, "{}++", expression.expression),
// Expression::Decrement(ref expression) => write!(f, "{}--", expression.expression),
@ -704,11 +721,13 @@ impl<'ast> fmt::Display for Expression<'ast> {
Expression::ArrayInitializer(ref expression) => {
write!(f, "[{} ; {}]", expression.expression, expression.count)
}
Expression::CircuitInline(ref expression) => {
write!(f, "inline circuit display not impl {}", expression.variable)
}
Expression::CircuitInline(ref expression) => write!(
f,
"inline circuit display not impl {}",
expression.identifier
),
Expression::Postfix(ref expression) => {
write!(f, "Postfix display not impl {}", expression.variable)
write!(f, "Postfix display not impl {}", expression.identifier)
}
}
}
@ -804,14 +823,14 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
Value::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
)
},
Rule::variable => Expression::Variable(
Variable::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
Rule::identifier => Expression::Identifier(
Identifier::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
),
rule => unreachable!("`expression_primitive` should contain one of [`value`, `variable`], found {:#?}", rule)
rule => unreachable!("`expression_primitive` should contain one of [`value`, `identifier`], found {:#?}", rule)
}
},
rule => unreachable!("`term` should contain one of ['value', 'variable', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}", rule)
rule => unreachable!("`term` should contain one of ['value', 'identifier', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}", rule)
}
}
rule => unreachable!(
@ -896,7 +915,7 @@ pub struct ConditionalStatement<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_for))]
pub struct ForStatement<'ast> {
pub index: Variable<'ast>,
pub index: Identifier<'ast>,
pub start: Expression<'ast>,
pub stop: Expression<'ast>,
pub statements: Vec<Statement<'ast>>,
@ -907,8 +926,8 @@ pub struct ForStatement<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_multiple_assignment))]
pub struct MultipleAssignmentStatement<'ast> {
pub assignees: Vec<OptionallyTypedVariable<'ast>>,
pub function_name: Variable<'ast>,
pub variables: Vec<Variable<'ast>>,
pub function_name: Identifier<'ast>,
pub arguments: Vec<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -918,7 +937,6 @@ pub struct MultipleAssignmentStatement<'ast> {
#[pest_ast(rule(Rule::statement_definition))]
pub struct DefinitionStatement<'ast> {
pub variable: Variable<'ast>,
pub _type: Option<Type<'ast>>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
@ -1016,9 +1034,9 @@ impl<'ast> fmt::Display for ForStatement<'ast> {
impl<'ast> fmt::Display for MultipleAssignmentStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, id) in self.assignees.iter().enumerate() {
for (i, id) in self.variables.iter().enumerate() {
write!(f, "{}", id)?;
if i < self.assignees.len() - 1 {
if i < self.variables.len() - 1 {
write!(f, ", ")?;
}
}
@ -1028,14 +1046,7 @@ impl<'ast> fmt::Display for MultipleAssignmentStatement<'ast> {
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self._type {
Some(ref _type) => write!(
f,
"let {} : {} = {};",
self.variable, _type, self.expression
),
None => write!(f, "let {} = {}", self.variable, self.expression),
}
write!(f, "let {} = {};", self.variable, self.expression)
}
}
@ -1073,29 +1084,21 @@ impl<'ast> fmt::Display for Statement<'ast> {
// Functions
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::parameter))]
pub struct Parameter<'ast> {
pub variable: Variable<'ast>,
#[pest_ast(rule(Rule::input_model))]
pub struct InputModel<'ast> {
pub mutable: Option<Mutable>,
pub identifier: Identifier<'ast>,
pub visibility: Option<Visibility>,
pub _type: Type<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::function_name))]
pub struct FunctionName<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::function_definition))]
pub struct Function<'ast> {
pub function_name: FunctionName<'ast>,
pub parameters: Vec<Parameter<'ast>>,
pub function_name: Identifier<'ast>,
pub parameters: Vec<InputModel<'ast>>,
pub returns: Vec<Type<'ast>>,
pub statements: Vec<Statement<'ast>>,
#[pest_ast(outer())]
@ -1122,8 +1125,8 @@ pub struct ImportSource<'ast> {
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::import_symbol))]
pub struct ImportSymbol<'ast> {
pub value: Variable<'ast>,
pub alias: Option<Variable<'ast>>,
pub value: Identifier<'ast>,
pub alias: Option<Identifier<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
@ -1143,7 +1146,7 @@ pub struct Import<'ast> {
#[pest_ast(rule(Rule::file))]
pub struct File<'ast> {
pub imports: Vec<Import<'ast>>,
pub structs: Vec<Circuit<'ast>>,
pub circuits: Vec<Circuit<'ast>>,
pub functions: Vec<Function<'ast>>,
pub eoi: EOI,
#[pest_ast(outer())]

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::BooleanError,
types::{InputModel, InputValue},
types::InputValue,
};
use snarkos_errors::gadgets::SynthesisError;
@ -19,7 +19,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn bool_from_input(
&mut self,
cs: &mut CS,
input_model: InputModel<F, G>,
name: String,
private: bool,
input_value: Option<InputValue<F, G>>,
) -> Result<ConstrainedValue<F, G>, BooleanError> {
// Check that the input value is the correct type
@ -35,8 +36,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
};
// Check visibility of input
let name = input_model.variable.name.clone();
let number = if input_model.private {
let number = if private {
Boolean::alloc(cs.ns(|| name), || {
bool_value.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -2,11 +2,11 @@
use crate::{
constraints::{
new_scope_from_variable, new_variable_from_variable, ConstrainedCircuitMember,
new_scope_from_variable, new_variable_from_variable, ConstrainedCircuitObject,
ConstrainedProgram, ConstrainedValue,
},
errors::ExpressionError,
types::{CircuitMember, Expression, RangeOrExpression, SpreadOrExpression, Variable},
types::{CircuitMember, Expression, Identifier, RangeOrExpression, SpreadOrExpression},
};
use snarkos_models::{
@ -16,10 +16,10 @@ use snarkos_models::{
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
/// Enforce a variable expression by getting the resolved value
pub(crate) fn enforce_variable(
pub(crate) fn evaluate_identifier(
&mut self,
scope: String,
unresolved_variable: Variable<F, G>,
unresolved_variable: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
// Evaluate the variable name in the current function scope
let variable_name = new_scope_from_variable(scope, &unresolved_variable);
@ -260,7 +260,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
for element in array.into_iter() {
match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Variable(variable) => {
Expression::Identifier(variable) => {
let array_name = new_scope_from_variable(function_scope.clone(), &variable);
match self.get(&array_name) {
Some(value) => match value {
@ -308,30 +308,38 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
array: Box<Expression<F, G>>,
index: RangeOrExpression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *array)? {
ConstrainedValue::Array(array) => {
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => from_index.to_usize(),
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(
array[from_resolved..to_resolved].to_owned(),
))
}
RangeOrExpression::Expression(index) => {
let index_resolved =
self.enforce_index(cs, file_scope, function_scope, index)?;
Ok(array[index_resolved].to_owned())
}
}
let array = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*array,
)? {
ConstrainedValue::Array(array) => array,
ConstrainedValue::Mutable(value) => match *value {
ConstrainedValue::Array(array) => array,
value => return Err(ExpressionError::InvalidArrayAccess(value.to_string())),
},
value => return Err(ExpressionError::InvalidArrayAccess(value.to_string())),
};
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => from_index.to_usize(),
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
Ok(ConstrainedValue::Array(
array[from_resolved..to_resolved].to_owned(),
))
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, file_scope, function_scope, index)?;
Ok(array[index_resolved].to_owned())
}
value => Err(ExpressionError::InvalidArrayAccess(value.to_string())),
}
}
@ -340,7 +348,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
variable: Variable<F, G>,
variable: Identifier<F, G>,
members: Vec<CircuitMember<F, G>>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let circuit_name = new_variable_from_variable(file_scope.clone(), &variable);
@ -355,10 +363,10 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
.into_iter()
.zip(members.clone().into_iter())
{
if field.variable != member.variable {
if field.identifier != member.identifier {
return Err(ExpressionError::InvalidCircuitObject(
field.variable.name,
member.variable.name,
field.identifier.name,
member.identifier.name,
));
}
// Resolve and enforce circuit fields
@ -372,7 +380,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Check member types
member_value.expect_type(&field._type)?;
resolved_members.push(ConstrainedCircuitMember(member.variable, member_value))
resolved_members.push(ConstrainedCircuitObject(member.identifier, member_value))
}
Ok(ConstrainedValue::CircuitExpression(
@ -390,21 +398,31 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
file_scope: String,
function_scope: String,
circuit_variable: Box<Expression<F, G>>,
circuit_member: Variable<F, G>,
circuit_member: Identifier<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match self.enforce_expression(cs, file_scope, function_scope, *circuit_variable)? {
ConstrainedValue::CircuitExpression(_name, members) => {
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(member) => Ok(member.1),
None => Err(ExpressionError::UndefinedCircuitObject(
circuit_member.to_string(),
)),
}
}
value => Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
let members = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
*circuit_variable.clone(),
)? {
ConstrainedValue::CircuitExpression(_name, members) => members,
ConstrainedValue::Mutable(value) => match *value {
ConstrainedValue::CircuitExpression(_name, members) => members,
value => return Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
},
value => return Err(ExpressionError::InvalidCircuitAccess(value.to_string())),
};
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(member) => Ok(member.1),
None => Err(ExpressionError::UndefinedCircuitObject(
circuit_member.to_string(),
)),
}
}
@ -413,7 +431,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
function: Variable<F, G>,
function: Identifier<F, G>,
arguments: Vec<Expression<F, G>>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let function_name = new_variable_from_variable(file_scope.clone(), &function);
@ -444,8 +462,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match expression {
// Variables
Expression::Variable(unresolved_variable) => {
self.enforce_variable(function_scope, unresolved_variable)
Expression::Identifier(unresolved_variable) => {
self.evaluate_identifier(function_scope, unresolved_variable)
}
// Values

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::FieldElementError,
types::{FieldElement, InputModel, InputValue, Integer},
types::{FieldElement, InputValue, Integer},
};
use snarkos_errors::gadgets::SynthesisError;
@ -16,7 +16,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn field_element_from_input(
&mut self,
cs: &mut CS,
input_model: InputModel<F, G>,
name: String,
private: bool,
input_value: Option<InputValue<F, G>>,
) -> Result<ConstrainedValue<F, G>, FieldElementError> {
// Check that the parameter value is the correct type
@ -32,8 +33,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
};
// Check visibility of parameter
let name = input_model.variable.name.clone();
let field_value = if input_model.private {
let field_value = if private {
cs.alloc(
|| name,
|| field_option.ok_or(SynthesisError::AssignmentMissing),

View File

@ -2,14 +2,9 @@
//! a resolved Leo program.
use crate::{
constraints::{
new_scope, new_scope_from_variable, new_variable_from_variables, ConstrainedProgram,
ConstrainedValue,
},
constraints::{new_scope, new_variable_from_variables, ConstrainedProgram, ConstrainedValue},
errors::{FunctionError, ImportError},
new_variable_from_variable,
types::{Expression, Function, InputValue, Program, Type},
InputModel, Variable,
types::{Expression, Function, Identifier, InputValue, Program, Type},
};
use snarkos_models::{
@ -36,7 +31,9 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
input: Expression<F, G>,
) -> Result<ConstrainedValue<F, G>, FunctionError> {
match input {
Expression::Variable(variable) => Ok(self.enforce_variable(caller_scope, variable)?),
Expression::Identifier(variable) => {
Ok(self.evaluate_identifier(caller_scope, variable)?)
}
expression => Ok(self.enforce_expression(cs, scope, function_name, expression)?),
}
}
@ -59,7 +56,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
function.inputs.clone().iter().zip(inputs.into_iter())
{
// First evaluate input expression
let input_value = self.enforce_input(
let mut input_value = self.enforce_input(
cs,
scope.clone(),
caller_scope.clone(),
@ -70,10 +67,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Check that input is correct type
input_value.expect_type(&input_model._type)?;
if input_model.mutable {
input_value = ConstrainedValue::Mutable(Box::new(input_value))
}
// Store input as variable with {function_name}_{input_name}
let variable_name =
new_scope_from_variable(function_name.clone(), &input_model.variable);
self.store(variable_name, input_value);
let input_program_identifier =
new_scope(function_name.clone(), input_model.identifier.name.clone());
self.store(input_program_identifier, input_value);
}
// Evaluate function statements
@ -99,8 +100,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
fn allocate_array(
&mut self,
cs: &mut CS,
array_name: Variable<F, G>,
array_private: bool,
name: String,
private: bool,
array_type: Type<F, G>,
array_dimensions: Vec<usize>,
input_value: Option<InputValue<F, G>>,
@ -115,18 +116,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Allocate each value in the current row
for (i, value) in arr.into_iter().enumerate() {
let array_input_model = InputModel {
private: array_private,
_type: array_type.next_dimension(&array_dimensions),
variable: new_variable_from_variables(
&array_name,
&Variable::new(i.to_string()),
),
};
let value_name = new_scope(name.clone(), i.to_string());
let value_type = array_type.next_dimension(&array_dimensions);
array_value.push(self.allocate_main_function_input(
cs,
array_input_model,
value_type,
value_name,
private,
Some(value),
)?)
}
@ -134,20 +131,14 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
None => {
// Allocate all row values as none
for i in 0..expected_length {
let array_input_model = InputModel {
private: array_private,
_type: array_type.next_dimension(&array_dimensions),
variable: new_variable_from_variables(
&array_name,
&Variable::new(i.to_string()),
),
};
let value_name = new_scope(name.clone(), i.to_string());
let value_type = array_type.next_dimension(&array_dimensions);
array_value.push(self.allocate_main_function_input(
cs,
array_input_model,
None,
)?);
array_value.push(
self.allocate_main_function_input(
cs, value_type, value_name, private, None,
)?,
);
}
}
_ => {
@ -163,25 +154,22 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
fn allocate_main_function_input(
&mut self,
cs: &mut CS,
input_model: InputModel<F, G>,
_type: Type<F, G>,
name: String,
private: bool,
input_value: Option<InputValue<F, G>>,
) -> Result<ConstrainedValue<F, G>, FunctionError> {
match input_model._type {
Type::IntegerType(ref _integer_type) => {
Ok(self.integer_from_parameter(cs, input_model, input_value)?)
match _type {
Type::IntegerType(integer_type) => {
Ok(self.integer_from_parameter(cs, integer_type, name, private, input_value)?)
}
Type::FieldElement => {
Ok(self.field_element_from_input(cs, input_model, input_value)?)
Ok(self.field_element_from_input(cs, name, private, input_value)?)
}
Type::Boolean => Ok(self.bool_from_input(cs, name, private, input_value)?),
Type::Array(_type, dimensions) => {
self.allocate_array(cs, name, private, *_type, dimensions, input_value)
}
Type::Boolean => Ok(self.bool_from_input(cs, input_model, input_value)?),
Type::Array(_type, dimensions) => self.allocate_array(
cs,
input_model.variable,
input_model.private,
*_type,
dimensions,
input_value,
),
_ => unimplemented!("main function input not implemented for type"),
}
}
@ -200,16 +188,26 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
// Iterate over main function inputs and allocate new passed-by variable values
let mut input_variables = vec![];
for (input_model, input_value) in
for (input_model, input_option) in
function.inputs.clone().into_iter().zip(inputs.into_iter())
{
let variable = new_variable_from_variable(scope.clone(), &input_model.variable);
let value = self.allocate_main_function_input(cs, input_model, input_value)?;
let input_name = new_scope(function_name.clone(), input_model.identifier.name.clone());
let mut input_value = self.allocate_main_function_input(
cs,
input_model._type,
input_name.clone(),
input_model.private,
input_option,
)?;
// store a new variable for every allocated main function input
self.store_variable(variable.clone(), value);
if input_model.mutable {
input_value = ConstrainedValue::Mutable(Box::new(input_value))
}
input_variables.push(Expression::Variable(variable));
// Store a new variable for every allocated main function input
self.store(input_name.clone(), input_value);
input_variables.push(Expression::Identifier(Identifier::new(input_name)));
}
self.enforce_function(cs, scope, function_name, function, input_variables)
@ -247,7 +245,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
.functions
.into_iter()
.for_each(|(function_name, function)| {
let resolved_function_name = new_scope(program_name.name.clone(), function_name.0);
let resolved_function_name =
new_scope(program_name.name.clone(), function_name.name);
self.store(resolved_function_name, ConstrainedValue::Function(function));
});

View File

@ -70,7 +70,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
None => {
// see if the imported symbol is a function
let matched_function = program.functions.clone().into_iter().find(
|(function_name, _function)| symbol.symbol.name == *function_name.0,
|(function_name, _function)| symbol.symbol.name == *function_name.name,
);
match matched_function {

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, InputValue, Integer, Type},
types::{InputValue, Integer},
IntegerType,
};
@ -41,35 +41,29 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn integer_from_parameter(
&mut self,
cs: &mut CS,
integer_model: InputModel<F, G>,
integer_type: IntegerType,
name: String,
private: bool,
integer_value: Option<InputValue<F, G>>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
let integer_type = match &integer_model._type {
Type::IntegerType(integer_type) => integer_type,
_type => return Err(IntegerError::InvalidType(_type.to_string())),
};
// Check that the parameter value is the correct type
// Check that the input value is the correct type
let integer_option = match integer_value {
Some(parameter) => {
if let InputValue::Integer(integer) = parameter {
Some(input) => {
if let InputValue::Integer(integer) = input {
Some(integer)
} else {
return Err(IntegerError::InvalidInteger(
integer_model._type.to_string(),
parameter.to_string(),
));
return Err(IntegerError::InvalidInteger(input.to_string()));
}
}
None => None,
};
match integer_type {
IntegerType::U8 => self.u8_from_input(cs, integer_model, integer_option),
IntegerType::U16 => self.u16_from_input(cs, integer_model, integer_option),
IntegerType::U32 => self.u32_from_input(cs, integer_model, integer_option),
IntegerType::U64 => self.u64_from_input(cs, integer_model, integer_option),
IntegerType::U128 => self.u128_from_integer(cs, integer_model, integer_option),
IntegerType::U8 => self.u8_from_input(cs, name, private, integer_option),
IntegerType::U16 => self.u16_from_input(cs, name, private, integer_option),
IntegerType::U32 => self.u32_from_input(cs, name, private, integer_option),
IntegerType::U64 => self.u64_from_input(cs, name, private, integer_option),
IntegerType::U128 => self.u128_from_input(cs, name, private, integer_option),
}
}

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer},
types::Integer,
};
use snarkos_errors::gadgets::SynthesisError;
@ -16,10 +16,11 @@ use snarkos_models::{
};
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
pub(crate) fn u128_from_integer(
pub(crate) fn u128_from_input(
&mut self,
cs: &mut CS,
parameter_model: InputModel<F, G>,
name: String,
private: bool,
integer_option: Option<usize>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
// Type cast to u128 in rust.
@ -27,8 +28,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let u128_option = integer_option.map(|integer| integer as u128);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
let integer_value = if private {
UInt128::alloc(cs.ns(|| name), || {
u128_option.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer},
types::Integer,
};
use snarkos_errors::gadgets::SynthesisError;
@ -19,7 +19,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn u16_from_input(
&mut self,
cs: &mut CS,
parameter_model: InputModel<F, G>,
name: String,
private: bool,
integer_option: Option<usize>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
// Type cast to u16 in rust.
@ -27,8 +28,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let u16_option = integer_option.map(|integer| integer as u16);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
let integer_value = if private {
UInt16::alloc(cs.ns(|| name), || {
u16_option.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer},
types::Integer,
};
use snarkos_errors::gadgets::SynthesisError;
@ -19,7 +19,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn u32_from_input(
&mut self,
cs: &mut CS,
parameter_model: InputModel<F, G>,
name: String,
private: bool,
integer_option: Option<usize>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
// Type cast to u32 in rust.
@ -27,8 +28,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let u32_option = integer_option.map(|integer| integer as u32);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
let integer_value = if private {
UInt32::alloc(cs.ns(|| name), || {
u32_option.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer},
types::Integer,
};
use snarkos_errors::gadgets::SynthesisError;
@ -19,7 +19,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn u64_from_input(
&mut self,
cs: &mut CS,
parameter_model: InputModel<F, G>,
name: String,
private: bool,
integer_option: Option<usize>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
// Type cast to u64 in rust.
@ -27,8 +28,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let u64_option = integer_option.map(|integer| integer as u64);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
let integer_value = if private {
UInt64::alloc(cs.ns(|| name), || {
u64_option.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -3,7 +3,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::IntegerError,
types::{InputModel, Integer},
types::Integer,
};
use snarkos_errors::gadgets::SynthesisError;
@ -19,7 +19,8 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
pub(crate) fn u8_from_input(
&mut self,
cs: &mut CS,
parameter_model: InputModel<F, G>,
name: String,
private: bool,
integer_option: Option<usize>,
) -> Result<ConstrainedValue<F, G>, IntegerError> {
// Type cast to u8 in rust.
@ -27,8 +28,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
let u8_option = integer_option.map(|integer| integer as u8);
// Check visibility of parameter
let name = parameter_model.variable.name.clone();
let integer_value = if parameter_model.private {
let integer_value = if private {
UInt8::alloc(cs.ns(|| name), || {
u8_option.ok_or(SynthesisError::AssignmentMissing)
})?

View File

@ -1,6 +1,6 @@
//! An in memory store to keep track of defined names when constraining a Leo program.
use crate::{constraints::ConstrainedValue, types::Variable};
use crate::{constraints::ConstrainedValue, types::Identifier};
use snarkos_models::{
curves::{Field, Group, PrimeField},
@ -9,7 +9,7 @@ use snarkos_models::{
use std::{collections::HashMap, marker::PhantomData};
pub struct ConstrainedProgram<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> {
pub resolved_names: HashMap<String, ConstrainedValue<F, G>>,
pub identifiers: HashMap<String, ConstrainedValue<F, G>>,
pub _cs: PhantomData<CS>,
}
@ -19,16 +19,16 @@ pub fn new_scope(outer: String, inner: String) -> String {
pub fn new_scope_from_variable<F: Field + PrimeField, G: Group>(
outer: String,
inner: &Variable<F, G>,
inner: &Identifier<F, G>,
) -> String {
new_scope(outer, inner.name.clone())
}
pub fn new_variable_from_variable<F: Field + PrimeField, G: Group>(
outer: String,
inner: &Variable<F, G>,
) -> Variable<F, G> {
Variable {
inner: &Identifier<F, G>,
) -> Identifier<F, G> {
Identifier {
name: new_scope_from_variable(outer, inner),
_engine: PhantomData::<F>,
_group: PhantomData::<G>,
@ -36,10 +36,10 @@ pub fn new_variable_from_variable<F: Field + PrimeField, G: Group>(
}
pub fn new_variable_from_variables<F: Field + PrimeField, G: Group>(
outer: &Variable<F, G>,
inner: &Variable<F, G>,
) -> Variable<F, G> {
Variable {
outer: &Identifier<F, G>,
inner: &Identifier<F, G>,
) -> Identifier<F, G> {
Identifier {
name: new_scope_from_variable(outer.name.clone(), inner),
_engine: PhantomData::<F>,
_group: PhantomData::<G>,
@ -49,42 +49,42 @@ pub fn new_variable_from_variables<F: Field + PrimeField, G: Group>(
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
pub fn new() -> Self {
Self {
resolved_names: HashMap::new(),
identifiers: HashMap::new(),
_cs: PhantomData::<CS>,
}
}
pub(crate) fn store(&mut self, name: String, value: ConstrainedValue<F, G>) {
self.resolved_names.insert(name, value);
self.identifiers.insert(name, value);
}
pub(crate) fn store_variable(
&mut self,
variable: Variable<F, G>,
variable: Identifier<F, G>,
value: ConstrainedValue<F, G>,
) {
self.store(variable.name, value);
}
pub(crate) fn contains_name(&self, name: &String) -> bool {
self.resolved_names.contains_key(name)
self.identifiers.contains_key(name)
}
pub(crate) fn contains_variable(&self, variable: &Variable<F, G>) -> bool {
pub(crate) fn contains_variable(&self, variable: &Identifier<F, G>) -> bool {
self.contains_name(&variable.name)
}
pub(crate) fn get(&self, name: &String) -> Option<&ConstrainedValue<F, G>> {
self.resolved_names.get(name)
self.identifiers.get(name)
}
pub(crate) fn get_mut(&mut self, name: &String) -> Option<&mut ConstrainedValue<F, G>> {
self.resolved_names.get_mut(name)
self.identifiers.get_mut(name)
}
pub(crate) fn get_mut_variable(
&mut self,
variable: &Variable<F, G>,
variable: &Identifier<F, G>,
) -> Option<&mut ConstrainedValue<F, G>> {
self.get_mut(&variable.name)
}

View File

@ -3,10 +3,12 @@
use crate::{
constraints::{new_scope_from_variable, ConstrainedProgram, ConstrainedValue},
errors::StatementError,
new_scope,
types::{
Assignee, ConditionalNestedOrEnd, ConditionalStatement, Expression, Integer,
RangeOrExpression, Statement, Type, Variable,
Assignee, ConditionalNestedOrEnd, ConditionalStatement, Expression, Identifier, Integer,
RangeOrExpression, Statement, Type,
},
Variable,
};
use snarkos_models::{
@ -17,7 +19,7 @@ use snarkos_models::{
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
fn resolve_assignee(&mut self, scope: String, assignee: Assignee<F, G>) -> String {
match assignee {
Assignee::Variable(name) => new_scope_from_variable(scope, &name),
Assignee::Identifier(name) => new_scope_from_variable(scope, &name),
Assignee::Array(array, _index) => self.resolve_assignee(scope, *array),
Assignee::CircuitMember(circuit_variable, _member) => {
self.resolve_assignee(scope, *circuit_variable)
@ -25,95 +27,46 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
fn store_assignment(
fn mutate_array(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignee: Assignee<F, G>,
return_value: &mut ConstrainedValue<F, G>,
name: String,
range_or_expression: RangeOrExpression<F, G>,
new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
match assignee {
Assignee::Variable(name) => {
// Store the variable in the current scope
let definition_name = new_scope_from_variable(function_scope.clone(), &name);
// Resolve index so we know if we are assigning to a single value or a range of values
match range_or_expression {
RangeOrExpression::Expression(index) => {
let index =
self.enforce_index(cs, file_scope.clone(), function_scope.clone(), index)?;
self.store(definition_name, return_value.to_owned());
}
Assignee::Array(array, index_expression) => {
// Check that array exists
let expected_array_name = self.resolve_assignee(function_scope.clone(), *array);
// Resolve index so we know if we are assigning to a single value or a range of values
match index_expression {
RangeOrExpression::Expression(index) => {
let index = self.enforce_index(
cs,
file_scope.clone(),
function_scope.clone(),
index,
)?;
// Modify the single value of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match value {
ConstrainedValue::Array(old) => {
old[index] = return_value.to_owned();
}
_ => return Err(StatementError::ArrayAssignIndex),
},
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
}
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => integer.to_usize(),
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(integer.to_usize()),
None => None,
};
// Modify the range of values of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match (value, return_value) {
(ConstrainedValue::Array(old), ConstrainedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => return Err(StatementError::ArrayAssignRange),
},
None => {
return Err(StatementError::UndefinedArray(expected_array_name))
}
}
// Modify the single value of the array in place
match self.get_mutable_variable(name)? {
ConstrainedValue::Array(old) => {
old[index] = new_value;
}
_ => return Err(StatementError::ArrayAssignIndex),
}
}
Assignee::CircuitMember(circuit_variable, circuit_member) => {
// Check that circuit exists
let expected_circuit_name =
self.resolve_assignee(function_scope.clone(), *circuit_variable);
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => integer.to_usize(),
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(integer.to_usize()),
None => None,
};
match self.get_mut(&expected_circuit_name) {
Some(ConstrainedValue::CircuitExpression(_variable, members)) => {
// Modify the circuit member in place
let matched_member = members
.into_iter()
.find(|member| member.0 == circuit_member);
match matched_member {
Some(mut member) => member.1 = return_value.to_owned(),
None => {
return Err(StatementError::UndefinedCircuitObject(
circuit_member.to_string(),
))
}
}
// Modify the range of values of the array in place
match (self.get_mutable_variable(name)?, new_value) {
(ConstrainedValue::Array(old), ConstrainedValue::Array(ref new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => return Err(StatementError::UndefinedCircuit(expected_circuit_name)),
_ => return Err(StatementError::ArrayAssignRange),
}
}
}
@ -121,6 +74,46 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
Ok(())
}
fn mutute_circuit_object(
&mut self,
circuit_name: String,
object_name: Identifier<F, G>,
new_value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
match self.get_mutable_variable(circuit_name)? {
ConstrainedValue::CircuitExpression(_variable, objects) => {
// Modify the circuit member in place
let matched_object = objects.into_iter().find(|object| object.0 == object_name);
match matched_object {
Some(mut object) => object.1 = new_value.to_owned(),
None => {
return Err(StatementError::UndefinedCircuitObject(
object_name.to_string(),
))
}
}
}
_ => return Err(StatementError::UndefinedCircuit(object_name.to_string())),
}
Ok(())
}
fn get_mutable_variable(
&mut self,
name: String,
) -> Result<&mut ConstrainedValue<F, G>, StatementError> {
// Check that assignee exists and is mutable
Ok(match self.get_mut(&name) {
Some(value) => match value {
ConstrainedValue::Mutable(mutable_value) => mutable_value,
_ => return Err(StatementError::ImmutableAssign(name)),
},
None => return Err(StatementError::UndefinedVariable(name)),
})
}
fn enforce_assign_statement(
&mut self,
cs: &mut CS,
@ -129,49 +122,71 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
assignee: Assignee<F, G>,
expression: Expression<F, G>,
) -> Result<(), StatementError> {
// Check that assignee exists
let name = self.resolve_assignee(function_scope.clone(), assignee.clone());
// Get the name of the variable we are assigning to
let variable_name = self.resolve_assignee(function_scope.clone(), assignee.clone());
match self.get(&name) {
Some(_assignee) => {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
// Evaluate new value
let new_value =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), expression)?;
self.store_assignment(cs, file_scope, function_scope, assignee, result_value)
// Mutate the old value into the new value
match assignee {
Assignee::Identifier(_identifier) => {
let old_value = self.get_mutable_variable(variable_name.clone())?;
*old_value = new_value;
Ok(())
}
Assignee::Array(_assignee, range_or_expression) => self.mutate_array(
cs,
file_scope,
function_scope,
variable_name,
range_or_expression,
new_value,
),
Assignee::CircuitMember(_assignee, object_name) => {
self.mutute_circuit_object(variable_name, object_name, new_value)
}
None => Err(StatementError::UndefinedVariable(assignee.to_string())),
}
}
fn store_definition(
&mut self,
function_scope: String,
variable: Variable<F, G>,
mut value: ConstrainedValue<F, G>,
) -> Result<(), StatementError> {
// Check optional explicit type
if let Some(_type) = variable._type {
value.expect_type(&_type)?;
}
// Store with given mutability
if variable.mutable {
value = ConstrainedValue::Mutable(Box::new(value));
}
let variable_program_identifier = new_scope(function_scope, variable.identifier.name);
self.store(variable_program_identifier, value);
Ok(())
}
fn enforce_definition_statement(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
assignee: Assignee<F, G>,
ty: Option<Type<F, G>>,
variable: Variable<F, G>,
expression: Expression<F, G>,
) -> Result<(), StatementError> {
let result_value = &mut self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expression,
)?;
let value =
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), expression)?;
match ty {
// Explicit type
Some(ty) => {
result_value.expect_type(&ty)?;
self.store_assignment(cs, file_scope, function_scope, assignee, result_value)
}
// Implicit type
None => self.store_assignment(cs, file_scope, function_scope, assignee, result_value),
}
self.store_definition(function_scope, variable, value)
}
fn enforce_multiple_definition_statement(
@ -179,7 +194,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
assignees: Vec<Assignee<F, G>>,
variables: Vec<Variable<F, G>>,
function: Expression<F, G>,
) -> Result<(), StatementError> {
// Expect return values from function
@ -196,20 +211,16 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
),
};
assignees
.into_iter()
.zip(return_values.into_iter())
.map(|(assignee, mut return_value)| {
self.store_assignment(
cs,
file_scope.clone(),
function_scope.clone(),
assignee,
&mut return_value,
)
})
.collect::<Result<Vec<_>, _>>()?;
if variables.len() != return_values.len() {
return Err(StatementError::InvalidNumberOfDefinitions(
variables.len(),
return_values.len(),
));
}
for (variable, value) in variables.into_iter().zip(return_values.into_iter()) {
self.store_definition(function_scope.clone(), variable, value)?;
}
Ok(())
}
@ -326,7 +337,7 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
cs: &mut CS,
file_scope: String,
function_scope: String,
index: Variable<F, G>,
index: Identifier<F, G>,
start: Integer,
stop: Integer,
statements: Vec<Statement<F, G>>,
@ -413,13 +424,12 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
return_types,
)?);
}
Statement::Definition(assignee, ty, expression) => {
Statement::Definition(variable, expression) => {
self.enforce_definition_statement(
cs,
file_scope,
function_scope,
assignee,
ty,
variable,
expression,
)?;
}
@ -432,12 +442,12 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
expression,
)?;
}
Statement::MultipleAssign(assignees, function) => {
Statement::MultipleAssign(variables, function) => {
self.enforce_multiple_definition_statement(
cs,
file_scope,
function_scope,
assignees,
variables,
function,
)?;
}

View File

@ -2,7 +2,7 @@
use crate::{
errors::ValueError,
types::{Circuit, FieldElement, Function, Type, Variable},
types::{Circuit, FieldElement, Function, Identifier, Type},
Integer,
};
@ -13,8 +13,8 @@ use snarkos_models::{
use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct ConstrainedCircuitMember<F: Field + PrimeField, G: Group>(
pub Variable<F, G>,
pub struct ConstrainedCircuitObject<F: Field + PrimeField, G: Group>(
pub Identifier<F, G>,
pub ConstrainedValue<F, G>,
);
@ -26,9 +26,10 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: Group> {
Boolean(Boolean),
Array(Vec<ConstrainedValue<F, G>>),
CircuitDefinition(Circuit<F, G>),
CircuitExpression(Variable<F, G>, Vec<ConstrainedCircuitMember<F, G>>),
CircuitExpression(Identifier<F, G>, Vec<ConstrainedCircuitObject<F, G>>),
Function(Function<F, G>),
Return(Vec<ConstrainedValue<F, G>>), // add Null for function returns
Return(Vec<ConstrainedValue<F, G>>),
Mutable(Box<ConstrainedValue<F, G>>),
}
impl<F: Field + PrimeField, G: Group> ConstrainedValue<F, G> {
@ -73,6 +74,9 @@ impl<F: Field + PrimeField, G: Group> ConstrainedValue<F, G> {
value.expect_type(_type)?;
}
}
(ConstrainedValue::Mutable(ref value), _type) => {
value.expect_type(&_type)?;
}
(value, _type) => {
return Err(ValueError::TypeError(format!(
"expected type {}, got {}",
@ -126,6 +130,7 @@ impl<F: Field + PrimeField, G: Group> fmt::Display for ConstrainedValue<F, G> {
unimplemented!("cannot return struct definition in program")
}
ConstrainedValue::Function(ref function) => write!(f, "{}();", function.function_name),
ConstrainedValue::Mutable(ref value) => write!(f, "mut {}", value),
}
}
}

View File

@ -5,8 +5,8 @@ pub enum IntegerError {
#[error("expected integer parameter type, got {}", _0)]
InvalidType(String),
#[error("Expected integer {} parameter, got {}", _0, _1)]
InvalidInteger(String, String),
#[error("Expected integer parameter, got {}", _0)]
InvalidInteger(String),
#[error("Cannot evaluate {}", _0)]
CannotEvaluate(String),

View File

@ -20,6 +20,7 @@ pub enum StatementError {
#[error("{}", _0)]
ValueError(ValueError),
// Arrays
#[error("Cannot assign single index to array of values")]
ArrayAssignIndex,
@ -29,12 +30,24 @@ pub enum StatementError {
#[error("Cannot assign to unknown array {}", _0)]
UndefinedArray(String),
// Circuits
#[error("Attempted to assign to unknown circuit {}", _0)]
UndefinedCircuit(String),
#[error("Attempted to assign to unknown circuit {}", _0)]
UndefinedCircuitObject(String),
// Statements
#[error("Cannot assign to immutable variable {}", _0)]
ImmutableAssign(String),
#[error(
"Multiple definition statement expected {} return values, got {}",
_0,
_1
)]
InvalidNumberOfDefinitions(usize, usize),
#[error("Function return statement expected {} return values, got {}", _0, _1)]
InvalidNumberOfReturns(usize, usize),

View File

@ -1,14 +1,14 @@
//! The Import type for a Leo program.
use crate::Variable;
use crate::Identifier;
use snarkos_models::curves::{Field, Group, PrimeField};
use std::fmt;
#[derive(Clone)]
pub struct ImportSymbol<F: Field + PrimeField, G: Group> {
pub symbol: Variable<F, G>,
pub alias: Option<Variable<F, G>>,
pub symbol: Identifier<F, G>,
pub alias: Option<Identifier<F, G>>,
}
#[derive(Clone)]

View File

@ -1,4 +1,10 @@
/// Visibili_type
/// Identifiers
protected_name = { visibility | "let" | "for"| "if" | "else" | "as" | "return" }
identifier = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
/// Visibility
visibility_public = { "public" }
visibility_private = { "private" }
@ -55,7 +61,8 @@ operation_assign = {
operation_mul_assign | operation_div_assign | operation_pow_assign
}
/// types
/// Types
type_u8 = {"u8"}
type_u16 = {"u16"}
type_u32 = {"u32"}
@ -73,7 +80,7 @@ type_field = {"field"}
type_group = {"group"}
type_bool = {"bool"}
type_basic = { type_field | type_group | type_bool | type_integer }
type_circuit = { variable }
type_circuit = { identifier }
type_array = {type_basic ~ ("[" ~ value ~ "]")+ }
_type = {type_array | type_basic | type_circuit}
type_list = _{(_type ~ ("," ~ _type)*)?}
@ -86,22 +93,13 @@ value_field = { value_number ~ type_field }
value_group = { value_number ~ type_group }
value_boolean = { "true" | "false" }
value = { value_field | value_group | value_boolean | value_integer }
expression_primitive = { value | identifier }
/// Variables
/// Variables + Mutability
// TODO: Include "import" and "conditional"
protected_name = { visibility | "let" | "for"| "if" | "else" | "as" | "return" }
// keyword = @{ "as" | "in" | "return" | "export" | "false" |
// "def" | "in" | "return" | "circuit" | "true" }
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
optionally_typed_variable = { (_type ~ variable) | (variable)}
optionally_typed_variable_tuple = _{ optionally_typed_variable ~ ("," ~ optionally_typed_variable)* }
expression_primitive = { value | variable }
mutable = {"mut"}
variable = { mutable? ~ identifier ~ (":" ~ _type)? }
variable_tuple = _{ variable ~ ("," ~ variable)* }
/// Access
@ -113,13 +111,13 @@ range_or_expression = { range | expression }
access_array = { "[" ~ range_or_expression ~ "]" }
access_call = { "(" ~ expression_tuple ~ ")" }
access_member = { "." ~ variable }
access_member = { "." ~ identifier }
access = { access_array | access_call | access_member }
expression_postfix = { variable ~ access+ }
expression_postfix = { identifier ~ access+ }
assignee_access = { access_array | access_member }
assignee = { variable ~ assignee_access* }
assignee = { identifier ~ assignee_access* }
spread = { "..." ~ expression }
spread_or_expression = { spread | expression }
@ -132,13 +130,13 @@ expression_array_initializer = { "[" ~ spread_or_expression ~ ";" ~ value ~ "]"
/// Circuits
circuit_object = { variable ~ ":" ~ _type }
circuit_object = { identifier ~ ":" ~ _type }
circuit_object_list = _{(circuit_object ~ (NEWLINE+ ~ circuit_object)*)? }
circuit_definition = { "circuit" ~ variable ~ "{" ~ NEWLINE* ~ circuit_object_list ~ NEWLINE* ~ "}" ~ NEWLINE* }
circuit_definition = { "circuit" ~ identifier ~ "{" ~ NEWLINE* ~ circuit_object_list ~ NEWLINE* ~ "}" ~ NEWLINE* }
inline_circuit_member = { variable ~ ":" ~ expression }
inline_circuit_member = { identifier ~ ":" ~ expression }
inline_circuit_member_list = _{(inline_circuit_member ~ ("," ~ NEWLINE* ~ inline_circuit_member)*)? ~ ","? }
expression_inline_circuit = { variable ~ "{" ~ NEWLINE* ~ inline_circuit_member_list ~ NEWLINE* ~ "}" }
expression_inline_circuit = { identifier ~ "{" ~ NEWLINE* ~ inline_circuit_member_list ~ NEWLINE* ~ "}" }
/// Conditionals
@ -172,11 +170,11 @@ conditional_nested_or_end = { statement_conditional | "{" ~ NEWLINE* ~ statement
/// Statements
statement_return = { "return" ~ expression_tuple }
statement_definition = { "let" ~ variable ~ (":" ~ _type)? ~ "=" ~ expression }
statement_definition = { "let" ~ variable ~ "=" ~ expression }
statement_assign = { assignee ~ operation_assign ~ expression }
statement_multiple_assignment = { "let" ~ "(" ~ optionally_typed_variable_tuple ~ ")" ~ "=" ~ variable ~ "(" ~ expression_tuple ~ ")" }
statement_multiple_assignment = { "let" ~ "(" ~ variable_tuple ~ ")" ~ "=" ~ identifier ~ "(" ~ expression_tuple ~ ")" }
statement_conditional = {"if" ~ (expression | "(" ~ expression ~ ")") ~ "{" ~ NEWLINE* ~ statement+ ~ "}" ~ ("else" ~ conditional_nested_or_end)?}
statement_for = { "for" ~ variable ~ "in" ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
statement_for = { "for" ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
statement_assert = {
assert_eq
// | assert_true |
@ -198,11 +196,10 @@ statement = {
/// Functions
parameter = {variable ~ ":" ~ visibility? ~ _type}
parameter_list = _{(parameter ~ ("," ~ parameter)*)?}
input_model = {mutable? ~ identifier ~ ":" ~ visibility? ~ _type}
input_model_list = _{(input_model ~ ("," ~ input_model)*)?}
function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ ("->" ~ (_type | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
function_definition = {"function" ~ identifier ~ "(" ~ input_model_list ~ ")" ~ ("->" ~ (_type | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
/// Utilities
@ -213,7 +210,7 @@ LINE_END = _{";" ~ NEWLINE*}
/// Imports
import_source = @{(!"\"" ~ ANY)*}
import_symbol = { variable ~ ("as" ~ variable)? }
import_symbol = { identifier ~ ("as" ~ identifier)? }
import_symbol_tuple = _{ import_symbol ~ ("," ~ NEWLINE* ~ import_symbol)* }
import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ ("*" | ("{" ~ NEWLINE* ~ import_symbol_tuple ~ NEWLINE* ~ "}")) ~ LINE_END}

View File

@ -3,7 +3,6 @@
use crate::{errors::IntegerError, Import};
use crate::errors::ValueError;
use snarkos_models::curves::{Field, Group, PrimeField};
use snarkos_models::gadgets::{
r1cs::Variable as R1CSVariable,
@ -15,15 +14,15 @@ use snarkos_models::gadgets::{
use std::collections::HashMap;
use std::marker::PhantomData;
/// A variable in a constraint system.
/// An identifier in the constrained program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Variable<F: Field + PrimeField, G: Group> {
pub struct Identifier<F: Field + PrimeField, G: Group> {
pub name: String,
pub(crate) _group: PhantomData<G>,
pub(crate) _engine: PhantomData<F>,
}
impl<F: Field + PrimeField, G: Group> Variable<F, G> {
impl<F: Field + PrimeField, G: Group> Identifier<F, G> {
pub fn new(name: String) -> Self {
Self {
name,
@ -33,6 +32,14 @@ impl<F: Field + PrimeField, G: Group> Variable<F, G> {
}
}
/// A variable that is assigned to a value in the constrained program
#[derive(Clone, PartialEq, Eq)]
pub struct Variable<F: Field + PrimeField, G: Group> {
pub identifier: Identifier<F, G>,
pub mutable: bool,
pub _type: Option<Type<F, G>>,
}
/// An integer type enum wrapping the integer value. Used only in expressions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Integer {
@ -108,8 +115,8 @@ pub enum SpreadOrExpression<F: Field + PrimeField, G: Group> {
/// Expression that evaluates to a value
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expression<F: Field + PrimeField, G: Group> {
// Variable
Variable(Variable<F, G>),
// Identifier
Identifier(Identifier<F, G>),
// Values
Integer(Integer),
@ -146,19 +153,19 @@ pub enum Expression<F: Field + PrimeField, G: Group> {
ArrayAccess(Box<Expression<F, G>>, Box<RangeOrExpression<F, G>>), // (array name, range)
// Circuits
Circuit(Variable<F, G>, Vec<CircuitMember<F, G>>),
CircuitMemberAccess(Box<Expression<F, G>>, Variable<F, G>), // (circuit name, circuit member name)
Circuit(Identifier<F, G>, Vec<CircuitMember<F, G>>),
CircuitMemberAccess(Box<Expression<F, G>>, Identifier<F, G>), // (circuit name, circuit object name)
// Functions
FunctionCall(Variable<F, G>, Vec<Expression<F, G>>),
FunctionCall(Identifier<F, G>, Vec<Expression<F, G>>),
}
/// Definition assignee: v, arr[0..2], Point p.x
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Assignee<F: Field + PrimeField, G: Group> {
Variable(Variable<F, G>),
Identifier(Identifier<F, G>),
Array(Box<Assignee<F, G>>, RangeOrExpression<F, G>),
CircuitMember(Box<Assignee<F, G>>, Variable<F, G>),
CircuitMember(Box<Assignee<F, G>>, Identifier<F, G>), // (circuit name, circuit object name)
}
/// Explicit integer type
@ -179,7 +186,7 @@ pub enum Type<F: Field + PrimeField, G: Group> {
GroupElement,
Boolean,
Array(Box<Type<F, G>>, Vec<usize>),
Circuit(Variable<F, G>),
Circuit(Identifier<F, G>),
}
impl<F: Field + PrimeField, G: Group> Type<F, G> {
@ -213,32 +220,31 @@ pub struct ConditionalStatement<F: Field + PrimeField, G: Group> {
/// Program statement that defines some action (or expression) to be carried out.
#[derive(Clone, PartialEq, Eq)]
pub enum Statement<F: Field + PrimeField, G: Group> {
// Declaration(Variable),
Return(Vec<Expression<F, G>>),
Definition(Assignee<F, G>, Option<Type<F, G>>, Expression<F, G>),
Definition(Variable<F, G>, Expression<F, G>),
Assign(Assignee<F, G>, Expression<F, G>),
MultipleAssign(Vec<Assignee<F, G>>, Expression<F, G>),
MultipleAssign(Vec<Variable<F, G>>, Expression<F, G>),
Conditional(ConditionalStatement<F, G>),
For(Variable<F, G>, Integer, Integer, Vec<Statement<F, G>>),
For(Identifier<F, G>, Integer, Integer, Vec<Statement<F, G>>),
AssertEq(Expression<F, G>, Expression<F, G>),
Expression(Expression<F, G>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CircuitMember<F: Field + PrimeField, G: Group> {
pub variable: Variable<F, G>,
pub identifier: Identifier<F, G>,
pub expression: Expression<F, G>,
}
#[derive(Clone, PartialEq, Eq)]
pub struct CircuitObject<F: Field + PrimeField, G: Group> {
pub variable: Variable<F, G>,
pub identifier: Identifier<F, G>,
pub _type: Type<F, G>,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Circuit<F: Field + PrimeField, G: Group> {
pub variable: Variable<F, G>,
pub identifier: Identifier<F, G>,
pub fields: Vec<CircuitObject<F, G>>,
}
@ -246,18 +252,10 @@ pub struct Circuit<F: Field + PrimeField, G: Group> {
#[derive(Clone, PartialEq, Eq)]
pub struct InputModel<F: Field + PrimeField, G: Group> {
pub identifier: Identifier<F, G>,
pub mutable: bool,
pub private: bool,
pub _type: Type<F, G>,
pub variable: Variable<F, G>,
}
impl<F: Field + PrimeField, G: Group> InputModel<F, G> {
pub fn inner_type(&self) -> Result<Type<F, G>, ValueError> {
match &self._type {
Type::Array(ref _type, _length) => Ok(*_type.clone()),
ref _type => Err(ValueError::ArrayModel(_type.to_string())),
}
}
}
#[derive(Clone, PartialEq, Eq)]
@ -269,13 +267,9 @@ pub enum InputValue<F: Field + PrimeField, G: Group> {
Array(Vec<InputValue<F, G>>),
}
/// The given name for a defined function in the program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FunctionName(pub String);
#[derive(Clone, PartialEq, Eq)]
pub struct Function<F: Field + PrimeField, G: Group> {
pub function_name: FunctionName,
pub function_name: Identifier<F, G>,
pub inputs: Vec<InputModel<F, G>>,
pub returns: Vec<Type<F, G>>,
pub statements: Vec<Statement<F, G>>,
@ -283,28 +277,24 @@ pub struct Function<F: Field + PrimeField, G: Group> {
impl<F: Field + PrimeField, G: Group> Function<F, G> {
pub fn get_name(&self) -> String {
self.function_name.0.clone()
self.function_name.name.clone()
}
}
/// A simple program with statement expressions, program arguments and program returns.
#[derive(Debug, Clone)]
pub struct Program<F: Field + PrimeField, G: Group> {
pub name: Variable<F, G>,
pub name: Identifier<F, G>,
pub num_parameters: usize,
pub imports: Vec<Import<F, G>>,
pub circuits: HashMap<Variable<F, G>, Circuit<F, G>>,
pub functions: HashMap<FunctionName, Function<F, G>>,
pub circuits: HashMap<Identifier<F, G>, Circuit<F, G>>,
pub functions: HashMap<Identifier<F, G>, Function<F, G>>,
}
impl<'ast, F: Field + PrimeField, G: Group> Program<F, G> {
pub fn new() -> Self {
Self {
name: Variable {
name: "".into(),
_group: PhantomData::<G>,
_engine: PhantomData::<F>,
},
name: Identifier::new("".into()),
num_parameters: 0,
imports: vec![],
circuits: HashMap::new(),
@ -317,11 +307,7 @@ impl<'ast, F: Field + PrimeField, G: Group> Program<F, G> {
}
pub fn name(mut self, name: String) -> Self {
self.name = Variable {
name,
_group: PhantomData::<G>,
_engine: PhantomData::<F>,
};
self.name = Identifier::new(name);
self
}
}

View File

@ -2,24 +2,40 @@
use crate::{
Assignee, Circuit, CircuitObject, ConditionalNestedOrEnd, ConditionalStatement, Expression,
FieldElement, Function, FunctionName, InputModel, InputValue, Integer, IntegerType,
FieldElement, Function, Identifier, InputModel, InputValue, Integer, IntegerType,
RangeOrExpression, SpreadOrExpression, Statement, Type, Variable,
};
use snarkos_models::curves::{Field, Group, PrimeField};
use std::fmt;
impl<F: Field + PrimeField, G: Group> fmt::Display for Variable<F, G> {
impl<F: Field + PrimeField, G: Group> fmt::Display for Identifier<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl<F: Field + PrimeField, G: Group> fmt::Debug for Variable<F, G> {
impl<F: Field + PrimeField, G: Group> fmt::Debug for Identifier<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl<F: Field + PrimeField, G: Group> fmt::Display for Variable<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.mutable {
write!(f, "mut ")?;
}
write!(f, "{}", self.identifier)?;
if self._type.is_some() {
write!(f, ": {}", self._type.as_ref().unwrap())?;
}
write!(f, "")
}
}
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.to_usize(), self.get_type())
@ -84,7 +100,7 @@ impl<'ast, F: Field + PrimeField, G: Group> fmt::Display for Expression<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
// Variables
Expression::Variable(ref variable) => write!(f, "{}", variable),
Expression::Identifier(ref variable) => write!(f, "{}", variable),
// Values
Expression::Integer(ref integer) => write!(f, "{}", integer),
@ -131,7 +147,7 @@ impl<'ast, F: Field + PrimeField, G: Group> fmt::Display for Expression<F, G> {
Expression::Circuit(ref var, ref members) => {
write!(f, "{} {{", var)?;
for (i, member) in members.iter().enumerate() {
write!(f, "{}: {}", member.variable, member.expression)?;
write!(f, "{}: {}", member.identifier, member.expression)?;
if i < members.len() - 1 {
write!(f, ", ")?;
}
@ -160,7 +176,7 @@ impl<'ast, F: Field + PrimeField, G: Group> fmt::Display for Expression<F, G> {
impl<F: Field + PrimeField, G: Group> fmt::Display for Assignee<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Assignee::Variable(ref variable) => write!(f, "{}", variable),
Assignee::Identifier(ref variable) => write!(f, "{}", variable),
Assignee::Array(ref array, ref index) => write!(f, "{}[{}]", array, index),
Assignee::CircuitMember(ref circuit_variable, ref member) => {
write!(f, "{}.{}", circuit_variable, member)
@ -210,10 +226,9 @@ impl<F: Field + PrimeField, G: Group> fmt::Display for Statement<F, G> {
}
write!(f, ")\n")
}
Statement::Definition(ref assignee, ref ty, ref expression) => match ty {
Some(ref ty) => write!(f, "let {}: {} = {};", assignee, ty, expression),
None => write!(f, "let {} = {};", assignee, expression),
},
Statement::Definition(ref variable, ref expression) => {
write!(f, "let {} = {};", variable, expression)
}
Statement::Assign(ref variable, ref statement) => {
write!(f, "{} = {};", variable, statement)
}
@ -276,13 +291,13 @@ impl<F: Field + PrimeField, G: Group> fmt::Display for Type<F, G> {
impl<F: Field + PrimeField, G: Group> fmt::Display for CircuitObject<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.variable, self._type)
write!(f, "{}: {}", self.identifier, self._type)
}
}
impl<F: Field + PrimeField, G: Group> Circuit<F, G> {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "circuit {} {{ \n", self.variable)?;
write!(f, "circuit {} {{ \n", self.identifier)?;
for field in self.fields.iter() {
write!(f, " {}\n", field)?;
}
@ -304,8 +319,17 @@ impl<F: Field + PrimeField, G: Group> fmt::Debug for Circuit<F, G> {
impl<F: Field + PrimeField, G: Group> fmt::Display for InputModel<F, G> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private" } else { "public" };
write!(f, "{}: {} {}", self.variable, visibility, self._type,)
// mut var: private bool
if self.mutable {
write!(f, "mut ")?;
}
write!(f, "{}: ", self.identifier)?;
if self.private {
write!(f, "private ")?;
} else {
write!(f, "public ")?;
}
write!(f, "{}", self._type)
}
}
@ -330,24 +354,6 @@ impl<F: Field + PrimeField, G: Group> fmt::Display for InputValue<F, G> {
}
}
impl FunctionName {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl fmt::Debug for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl<F: Field + PrimeField, G: Group> Function<F, G> {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "function {}", self.function_name)?;

View File

@ -7,25 +7,38 @@ use snarkos_models::gadgets::utilities::{
boolean::Boolean, uint128::UInt128, uint16::UInt16, uint32::UInt32, uint64::UInt64,
uint8::UInt8,
};
use std::{collections::HashMap, marker::PhantomData};
use std::collections::HashMap;
/// pest ast -> types::Identifier
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Identifier<'ast>>
for types::Identifier<F, G>
{
fn from(identifier: ast::Identifier<'ast>) -> Self {
types::Identifier::new(identifier.value)
}
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Identifier<'ast>>
for types::Expression<F, G>
{
fn from(identifier: ast::Identifier<'ast>) -> Self {
types::Expression::Identifier(types::Identifier::from(identifier))
}
}
/// pest ast -> types::Variable
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Variable<'ast>> for types::Variable<F, G> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Variable {
name: variable.value,
_engine: PhantomData::<F>,
_group: PhantomData::<G>,
identifier: types::Identifier::from(variable.identifier),
mutable: variable.mutable.is_some(),
_type: variable._type.map(|_type| types::Type::from(_type)),
}
}
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Variable<'ast>> for types::Expression<F, G> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Expression::Variable(types::Variable::from(variable))
}
}
/// pest ast - types::Integer
impl<'ast> types::Integer {
@ -268,7 +281,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::InlineCircuitMember<'ast>>
{
fn from(member: ast::InlineCircuitMember<'ast>) -> Self {
types::CircuitMember {
variable: types::Variable::from(member.variable),
identifier: types::Identifier::from(member.identifier),
expression: types::Expression::from(member.expression),
}
}
@ -278,7 +291,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::CircuitInlineExpression<'a
for types::Expression<F, G>
{
fn from(expression: ast::CircuitInlineExpression<'ast>) -> Self {
let variable = types::Variable::from(expression.variable);
let variable = types::Identifier::from(expression.identifier);
let members = expression
.members
.into_iter()
@ -293,7 +306,8 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::PostfixExpression<'ast>>
for types::Expression<F, G>
{
fn from(expression: ast::PostfixExpression<'ast>) -> Self {
let variable = types::Expression::Variable(types::Variable::from(expression.variable));
let variable =
types::Expression::Identifier(types::Identifier::from(expression.identifier));
// ast::PostFixExpression contains an array of "accesses": `a(34)[42]` is represented as `[a, [Call(34), Select(42)]]`, but Access call expressions
// are recursive, so it is `Select(Call(a, 34), 42)`. We apply this transformation here
@ -304,7 +318,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::PostfixExpression<'ast>>
.into_iter()
.fold(variable, |acc, access| match access {
ast::Access::Call(function) => match acc {
types::Expression::Variable(variable) => types::Expression::FunctionCall(
types::Expression::Identifier(variable) => types::Expression::FunctionCall(
variable,
function
.expressions
@ -318,7 +332,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::PostfixExpression<'ast>>
},
ast::Access::Member(struct_member) => types::Expression::CircuitMemberAccess(
Box::new(acc),
types::Variable::from(struct_member.variable),
types::Identifier::from(struct_member.identifier),
),
ast::Access::Array(array) => types::Expression::ArrayAccess(
Box::new(acc),
@ -334,7 +348,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Expression<'ast>>
fn from(expression: ast::Expression<'ast>) -> Self {
match expression {
ast::Expression::Value(value) => types::Expression::from(value),
ast::Expression::Variable(variable) => types::Expression::from(variable),
ast::Expression::Identifier(variable) => types::Expression::from(variable),
ast::Expression::Not(expression) => types::Expression::from(expression),
ast::Expression::Binary(expression) => types::Expression::from(expression),
ast::Expression::Ternary(expression) => types::Expression::from(expression),
@ -362,7 +376,7 @@ impl<'ast, F: Field + PrimeField, G: Group> types::Expression<F, G> {
// ast::Assignee -> types::Expression for operator assign statements
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Assignee<'ast>> for types::Expression<F, G> {
fn from(assignee: ast::Assignee<'ast>) -> Self {
let variable = types::Expression::Variable(types::Variable::from(assignee.variable));
let variable = types::Expression::Identifier(types::Identifier::from(assignee.identifier));
// we start with the id, and we fold the array of accesses by wrapping the current value
assignee
@ -372,7 +386,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Assignee<'ast>> for types:
ast::AssigneeAccess::Member(struct_member) => {
types::Expression::CircuitMemberAccess(
Box::new(acc),
types::Variable::from(struct_member.variable),
types::Identifier::from(struct_member.identifier),
)
}
ast::AssigneeAccess::Array(array) => types::Expression::ArrayAccess(
@ -385,15 +399,15 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Assignee<'ast>> for types:
/// pest ast -> types::Assignee
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Variable<'ast>> for types::Assignee<F, G> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Assignee::Variable(types::Variable::from(variable))
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Identifier<'ast>> for types::Assignee<F, G> {
fn from(variable: ast::Identifier<'ast>) -> Self {
types::Assignee::Identifier(types::Identifier::from(variable))
}
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Assignee<'ast>> for types::Assignee<F, G> {
fn from(assignee: ast::Assignee<'ast>) -> Self {
let variable = types::Assignee::from(assignee.variable);
let variable = types::Assignee::from(assignee.identifier);
// we start with the id, and we fold the array of accesses by wrapping the current value
assignee
@ -406,7 +420,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Assignee<'ast>> for types:
),
ast::AssigneeAccess::Member(struct_member) => types::Assignee::CircuitMember(
Box::new(acc),
types::Variable::from(struct_member.variable),
types::Identifier::from(struct_member.identifier),
),
})
}
@ -433,10 +447,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::DefinitionStatement<'ast>>
{
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
types::Statement::Definition(
types::Assignee::from(statement.variable),
statement
._type
.map(|_type| types::Type::<F, G>::from(_type)),
types::Variable::from(statement.variable),
types::Expression::from(statement.expression),
)
}
@ -504,16 +515,16 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::MultipleAssignmentStatemen
for types::Statement<F, G>
{
fn from(statement: ast::MultipleAssignmentStatement<'ast>) -> Self {
let assignees = statement
.assignees
let variables = statement
.variables
.into_iter()
.map(|i| types::Assignee::Variable(types::Variable::from(i.id)))
.map(|typed_variable| types::Variable::from(typed_variable))
.collect();
types::Statement::MultipleAssign(
assignees,
variables,
types::Expression::FunctionCall(
types::Variable::from(statement.function_name),
types::Identifier::from(statement.function_name),
statement
.arguments
.into_iter()
@ -575,7 +586,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::ForStatement<'ast>>
};
types::Statement::For(
types::Variable::from(statement.index),
types::Identifier::from(statement.index),
from,
to,
statement
@ -667,7 +678,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::ArrayType<'ast>> for types
impl<'ast, F: Field + PrimeField, G: Group> From<ast::CircuitType<'ast>> for types::Type<F, G> {
fn from(struct_type: ast::CircuitType<'ast>) -> Self {
types::Type::Circuit(types::Variable::from(struct_type.variable))
types::Type::Circuit(types::Identifier::from(struct_type.identifier))
}
}
@ -688,7 +699,7 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::CircuitObject<'ast>>
{
fn from(struct_field: ast::CircuitObject<'ast>) -> Self {
types::CircuitObject {
variable: types::Variable::from(struct_field.variable),
identifier: types::Identifier::from(struct_field.identifier),
_type: types::Type::from(struct_field._type),
}
}
@ -696,55 +707,43 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::CircuitObject<'ast>>
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Circuit<'ast>> for types::Circuit<F, G> {
fn from(struct_definition: ast::Circuit<'ast>) -> Self {
let variable = types::Variable::from(struct_definition.variable);
let variable = types::Identifier::from(struct_definition.identifier);
let fields = struct_definition
.fields
.into_iter()
.map(|struct_field| types::CircuitObject::from(struct_field))
.collect();
types::Circuit { variable, fields }
types::Circuit {
identifier: variable,
fields,
}
}
}
/// pest ast -> function types::Parameters
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Parameter<'ast>> for types::InputModel<F, G> {
fn from(parameter: ast::Parameter<'ast>) -> Self {
let _type = types::Type::from(parameter._type);
let variable = types::Variable::from(parameter.variable);
if parameter.visibility.is_some() {
let private = match parameter.visibility.unwrap() {
ast::Visibility::Private(_) => true,
ast::Visibility::Public(_) => false,
};
types::InputModel {
private,
_type,
variable,
}
} else {
types::InputModel {
private: true,
_type,
variable,
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::InputModel<'ast>>
for types::InputModel<F, G>
{
fn from(parameter: ast::InputModel<'ast>) -> Self {
types::InputModel {
identifier: types::Identifier::from(parameter.identifier),
mutable: parameter.mutable.is_some(),
// private by default
private: parameter.visibility.map_or(true, |visibility| {
visibility.eq(&ast::Visibility::Private(ast::Private {}))
}),
_type: types::Type::from(parameter._type),
}
}
}
/// pest ast -> types::Function
impl<'ast> From<ast::FunctionName<'ast>> for types::FunctionName {
fn from(name: ast::FunctionName<'ast>) -> Self {
types::FunctionName(name.value)
}
}
impl<'ast, F: Field + PrimeField, G: Group> From<ast::Function<'ast>> for types::Function<F, G> {
fn from(function_definition: ast::Function<'ast>) -> Self {
let function_name = types::FunctionName::from(function_definition.function_name);
let function_name = types::Identifier::from(function_definition.function_name);
let parameters = function_definition
.parameters
.into_iter()
@ -775,8 +774,8 @@ impl<'ast, F: Field + PrimeField, G: Group> From<ast::Function<'ast>> for types:
impl<'ast, F: Field + PrimeField, G: Group> From<ast::ImportSymbol<'ast>> for ImportSymbol<F, G> {
fn from(symbol: ast::ImportSymbol<'ast>) -> Self {
ImportSymbol {
symbol: types::Variable::from(symbol.value),
alias: symbol.alias.map(|alias| types::Variable::from(alias)),
symbol: types::Identifier::from(symbol.value),
alias: symbol.alias.map(|alias| types::Identifier::from(alias)),
}
}
}
@ -809,29 +808,25 @@ impl<'ast, F: Field + PrimeField, G: Group> types::Program<F, G> {
let mut functions = HashMap::new();
let mut num_parameters = 0usize;
file.structs.into_iter().for_each(|struct_def| {
file.circuits.into_iter().for_each(|struct_def| {
structs.insert(
types::Variable::from(struct_def.variable.clone()),
types::Identifier::from(struct_def.identifier.clone()),
types::Circuit::from(struct_def),
);
});
file.functions.into_iter().for_each(|function_def| {
functions.insert(
types::FunctionName::from(function_def.function_name.clone()),
types::Identifier::from(function_def.function_name.clone()),
types::Function::from(function_def),
);
});
if let Some(main_function) = functions.get(&types::FunctionName("main".into())) {
if let Some(main_function) = functions.get(&types::Identifier::new("main".into())) {
num_parameters = main_function.inputs.len();
}
types::Program {
name: types::Variable {
name,
_group: PhantomData::<G>,
_engine: PhantomData::<F>,
},
name: types::Identifier::new(name),
num_parameters,
imports,
circuits: structs,