mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-27 12:13:41 +03:00
Merge branch 'master' of https://github.com/AleoHQ/language into feature/compiler
This commit is contained in:
commit
85eb6cf6a8
@ -1,4 +1,4 @@
|
||||
function main() -> (u32) {
|
||||
a = 1 + 1
|
||||
return a
|
||||
function main(a: private u32) -> (u32) {
|
||||
b = a + 5;
|
||||
return a
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
struct Point {
|
||||
u32 x
|
||||
u32 y
|
||||
}
|
||||
}
|
||||
|
||||
function test() -> (u32) {
|
||||
return 5
|
||||
}
|
||||
|
@ -268,6 +268,21 @@ impl<'ast> fmt::Display for Variable<'ast> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, FromPest, PartialEq, Clone)]
|
||||
#[pest_ast(rule(Rule::optionally_typed_variable))]
|
||||
pub struct OptionallyTypedVariable<'ast> {
|
||||
pub ty: Option<Type<'ast>>,
|
||||
pub id: Variable<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for OptionallyTypedVariable<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Access
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
@ -782,25 +797,6 @@ impl<'ast> FromPest<'ast> for Expression<'ast> {
|
||||
|
||||
// Statements
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement_assign))]
|
||||
pub struct AssignStatement<'ast> {
|
||||
pub assignee: Assignee<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement_definition))]
|
||||
pub struct DefinitionStatement<'ast> {
|
||||
pub ty: Type<'ast>,
|
||||
pub variable: Variable<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement_return))]
|
||||
pub struct ReturnStatement<'ast> {
|
||||
@ -820,25 +816,43 @@ pub struct ForStatement<'ast> {
|
||||
pub span: Span<'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 arguments: Vec<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement_definition))]
|
||||
pub struct DefinitionStatement<'ast> {
|
||||
pub ty: Type<'ast>,
|
||||
pub variable: Variable<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement_assign))]
|
||||
pub struct AssignStatement<'ast> {
|
||||
pub assignee: Assignee<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::statement))]
|
||||
pub enum Statement<'ast> {
|
||||
Assign(AssignStatement<'ast>),
|
||||
Definition(DefinitionStatement<'ast>),
|
||||
Return(ReturnStatement<'ast>),
|
||||
Iteration(ForStatement<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for AssignStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} = {}", self.assignee, self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {} = {}", self.ty, self.variable, self.expression)
|
||||
}
|
||||
MultipleAssignment(MultipleAssignmentStatement<'ast>),
|
||||
Definition(DefinitionStatement<'ast>),
|
||||
Assign(AssignStatement<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for ReturnStatement<'ast> {
|
||||
@ -863,13 +877,38 @@ 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() {
|
||||
write!(f, "{}", id)?;
|
||||
if i < self.assignees.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, " = {}", self.function_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {} = {}", self.ty, self.variable, self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for AssignStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} = {}", self.assignee, self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Statement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Statement::Assign(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Definition(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Return(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Iteration(ref statement) => write!(f, "{}", statement),
|
||||
Statement::MultipleAssignment(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Assign(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Definition(ref statement) => write!(f, "{}", statement),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -924,19 +963,9 @@ pub struct ImportSource<'ast> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::main_import))]
|
||||
pub struct MainImport<'ast> {
|
||||
pub source: ImportSource<'ast>,
|
||||
pub alias: Option<Variable<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::from_import))]
|
||||
pub struct FromImport<'ast> {
|
||||
pub source: ImportSource<'ast>,
|
||||
pub symbol: Variable<'ast>,
|
||||
#[pest_ast(rule(Rule::import_symbol))]
|
||||
pub struct ImportSymbol<'ast> {
|
||||
pub value: Variable<'ast>,
|
||||
pub alias: Option<Variable<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
@ -944,9 +973,11 @@ pub struct FromImport<'ast> {
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::import))]
|
||||
pub enum Import<'ast> {
|
||||
Main(MainImport<'ast>),
|
||||
From(FromImport<'ast>),
|
||||
pub struct Import<'ast> {
|
||||
pub source: ImportSource<'ast>,
|
||||
pub symbols: Vec<ImportSymbol<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
// File
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! Methods to enforce constraints a resolved aleo program.
|
||||
//! Methods to enforce constraints and construct a resolved aleo program.
|
||||
|
||||
use crate::ast;
|
||||
use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::constraints::{
|
||||
new_scope, new_scope_from_variable, new_variable_from_variables, ResolvedProgram, ResolvedValue,
|
||||
};
|
||||
use crate::{Expression, Function, Import, Program, Statement, Type};
|
||||
|
||||
use from_pest::FromPest;
|
||||
@ -13,9 +15,12 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn enforce_function(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
function: Function<F>,
|
||||
arguments: Vec<Expression<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
let function_name = new_scope(scope.clone(), function.get_name());
|
||||
|
||||
// Make sure we are given the correct number of arguments
|
||||
if function.parameters.len() != arguments.len() {
|
||||
unimplemented!(
|
||||
@ -35,11 +40,16 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
// Check that argument is correct type
|
||||
match parameter.ty.clone() {
|
||||
Type::U32 => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
match self.enforce_expression(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
argument,
|
||||
) {
|
||||
ResolvedValue::U32(number) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::U32(number));
|
||||
@ -50,11 +60,16 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
}
|
||||
Type::FieldElement => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
match self.enforce_expression(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
argument,
|
||||
) {
|
||||
ResolvedValue::FieldElement(field) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::FieldElement(field));
|
||||
@ -63,11 +78,16 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
}
|
||||
Type::Boolean => {
|
||||
match self.enforce_expression(cs, function.get_name(), argument) {
|
||||
match self.enforce_expression(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
argument,
|
||||
) {
|
||||
ResolvedValue::Boolean(bool) => {
|
||||
// Store argument as variable with {function_name}_{parameter name}
|
||||
let variable_name = new_scope_from_variable(
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
¶meter.variable,
|
||||
);
|
||||
self.store(variable_name, ResolvedValue::Boolean(bool));
|
||||
@ -90,38 +110,66 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
.clone()
|
||||
.into_iter()
|
||||
.for_each(|statement| match statement {
|
||||
Statement::Definition(variable, expression) => {
|
||||
self.enforce_definition_statement(
|
||||
Statement::Return(expressions) => {
|
||||
return_values = self.enforce_return_statement(
|
||||
cs,
|
||||
function.get_name(),
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
expressions,
|
||||
function.returns.to_owned(),
|
||||
);
|
||||
}
|
||||
Statement::Assign(variable, expression) => {
|
||||
self.enforce_assign_statement(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
variable,
|
||||
expression,
|
||||
);
|
||||
}
|
||||
Statement::Definition(ty, assignee, expression) => {
|
||||
self.enforce_definition_statement(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
ty,
|
||||
assignee,
|
||||
expression,
|
||||
);
|
||||
}
|
||||
Statement::MultipleDefinition(assignees, function_call) => {
|
||||
self.enforce_multiple_definition_statement(
|
||||
cs,
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
assignees,
|
||||
function_call,
|
||||
);
|
||||
}
|
||||
Statement::For(index, start, stop, statements) => {
|
||||
self.enforce_for_statement(
|
||||
cs,
|
||||
function.get_name(),
|
||||
scope.clone(),
|
||||
function_name.clone(),
|
||||
index,
|
||||
start,
|
||||
stop,
|
||||
statements,
|
||||
);
|
||||
}
|
||||
Statement::Return(expressions) => {
|
||||
return_values = self.enforce_return_statement(
|
||||
cs,
|
||||
function.get_name(),
|
||||
expressions,
|
||||
function.returns.to_owned(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
return_values
|
||||
}
|
||||
|
||||
fn enforce_main_function(&mut self, cs: &mut CS, function: Function<F>) -> ResolvedValue<F> {
|
||||
fn enforce_main_function(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
function: Function<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
let function_name = new_scope(scope.clone(), function.get_name());
|
||||
let mut arguments = vec![];
|
||||
|
||||
// Iterate over main function parameters
|
||||
@ -134,30 +182,33 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
// append each variable to arguments vector
|
||||
arguments.push(Expression::Variable(match parameter.ty {
|
||||
Type::U32 => {
|
||||
self.integer_from_parameter(cs, function.get_name(), i + 1, parameter)
|
||||
}
|
||||
Type::FieldElement => {
|
||||
self.field_element_from_parameter(cs, function.get_name(), i + 1, parameter)
|
||||
self.u32_from_parameter(cs, function_name.clone(), i + 1, parameter)
|
||||
}
|
||||
Type::FieldElement => self.field_element_from_parameter(
|
||||
cs,
|
||||
function_name.clone(),
|
||||
i + 1,
|
||||
parameter,
|
||||
),
|
||||
Type::Boolean => {
|
||||
self.bool_from_parameter(cs, function.get_name(), i + 1, parameter)
|
||||
self.bool_from_parameter(cs, function_name.clone(), i + 1, parameter)
|
||||
}
|
||||
Type::Array(ref ty, _length) => match *ty.clone() {
|
||||
Type::U32 => self.integer_array_from_parameter(
|
||||
Type::U32 => self.u32_array_from_parameter(
|
||||
cs,
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
i + 1,
|
||||
parameter,
|
||||
),
|
||||
Type::FieldElement => self.field_element_array_from_parameter(
|
||||
cs,
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
i + 1,
|
||||
parameter,
|
||||
),
|
||||
Type::Boolean => self.boolean_array_from_parameter(
|
||||
cs,
|
||||
function.get_name(),
|
||||
function_name.clone(),
|
||||
i + 1,
|
||||
parameter,
|
||||
),
|
||||
@ -167,58 +218,138 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}))
|
||||
});
|
||||
|
||||
self.enforce_function(cs, function, arguments)
|
||||
self.enforce_function(cs, scope, function, arguments)
|
||||
}
|
||||
|
||||
fn enforce_import(&mut self, cs: &mut CS, import: Import) {
|
||||
fn enforce_import(&mut self, cs: &mut CS, scope: String, import: Import<F>) {
|
||||
// Resolve program file path
|
||||
let unparsed_file = fs::read_to_string(import.get_file()).expect("cannot read file");
|
||||
let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse");
|
||||
let unparsed_file = fs::read_to_string(import.get_file())
|
||||
.expect(&format!("cannot parse import {}", import.get_file()));
|
||||
let mut file = ast::parse(&unparsed_file)
|
||||
.expect(&format!("cannot parse import {}", import.get_file()));
|
||||
|
||||
// generate ast from file
|
||||
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
|
||||
|
||||
// generate aleo program from file
|
||||
let program = Program::from(syntax_tree);
|
||||
let mut program = Program::from(syntax_tree);
|
||||
|
||||
// recursively evaluate program statements TODO: in file scope
|
||||
self.resolve_definitions(cs, program);
|
||||
// Use same namespace as calling function for imported symbols
|
||||
program = program.name(scope);
|
||||
|
||||
// store import under designated name
|
||||
// self.store(name, value)
|
||||
// * -> import all imports, structs, functions in the current scope
|
||||
if import.is_star() {
|
||||
// recursively evaluate program statements
|
||||
self.resolve_definitions(cs, program);
|
||||
} else {
|
||||
let program_name = program.name.clone();
|
||||
|
||||
// match each import symbol to a symbol in the imported file
|
||||
import.symbols.into_iter().for_each(|symbol| {
|
||||
// see if the imported symbol is a struct
|
||||
let matched_struct = program
|
||||
.structs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.find(|(struct_name, _struct_def)| symbol.symbol == *struct_name);
|
||||
|
||||
match matched_struct {
|
||||
Some((_struct_name, struct_def)) => {
|
||||
// take the alias if it is present
|
||||
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
|
||||
let resolved_struct_name =
|
||||
new_variable_from_variables(&program_name.clone(), &resolved_name);
|
||||
|
||||
// store imported struct under resolved name
|
||||
self.store_variable(
|
||||
resolved_struct_name,
|
||||
ResolvedValue::StructDefinition(struct_def),
|
||||
);
|
||||
}
|
||||
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,
|
||||
);
|
||||
|
||||
match matched_function {
|
||||
Some((_function_name, function)) => {
|
||||
// take the alias if it is present
|
||||
let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
|
||||
let resolved_function_name = new_variable_from_variables(
|
||||
&program_name.clone(),
|
||||
&resolved_name,
|
||||
);
|
||||
|
||||
// store imported function under resolved name
|
||||
self.store_variable(
|
||||
resolved_function_name,
|
||||
ResolvedValue::Function(function),
|
||||
)
|
||||
}
|
||||
None => unimplemented!(
|
||||
"cannot find imported symbol {} in imported file {}",
|
||||
symbol,
|
||||
program_name.clone()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// evaluate all import statements in imported file
|
||||
program.imports.into_iter().for_each(|nested_import| {
|
||||
self.enforce_import(cs, program_name.name.clone(), nested_import)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_definitions(&mut self, cs: &mut CS, program: Program<F>) {
|
||||
let program_name = program.name.clone();
|
||||
|
||||
// evaluate and store all imports
|
||||
program
|
||||
.imports
|
||||
.into_iter()
|
||||
.for_each(|import| self.enforce_import(cs, import));
|
||||
.for_each(|import| self.enforce_import(cs, program_name.name.clone(), import));
|
||||
|
||||
// evaluate and store all struct definitions
|
||||
program
|
||||
.structs
|
||||
.into_iter()
|
||||
.for_each(|(variable, struct_def)| {
|
||||
self.store_variable(variable, ResolvedValue::StructDefinition(struct_def));
|
||||
let resolved_struct_name =
|
||||
new_variable_from_variables(&program_name.clone(), &variable);
|
||||
self.store_variable(
|
||||
resolved_struct_name,
|
||||
ResolvedValue::StructDefinition(struct_def),
|
||||
);
|
||||
});
|
||||
|
||||
// evaluate and store all function definitions
|
||||
program
|
||||
.functions
|
||||
.into_iter()
|
||||
.for_each(|(function_name, function)| {
|
||||
self.store(function_name.0, ResolvedValue::Function(function));
|
||||
let resolved_function_name = new_scope(program_name.name.clone(), function_name.0);
|
||||
self.store(resolved_function_name, ResolvedValue::Function(function));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn generate_constraints(cs: &mut CS, program: Program<F>) {
|
||||
let mut resolved_program = ResolvedProgram::new();
|
||||
let program_name = program.get_name();
|
||||
let main_function_name = new_scope(program_name.clone(), "main".into());
|
||||
|
||||
resolved_program.resolve_definitions(cs, program);
|
||||
|
||||
let main = resolved_program
|
||||
.get(&"main".into())
|
||||
.get(&main_function_name)
|
||||
.expect("main function not defined");
|
||||
|
||||
let result = match main.clone() {
|
||||
ResolvedValue::Function(function) => {
|
||||
resolved_program.enforce_main_function(cs, function)
|
||||
resolved_program.enforce_main_function(cs, program_name, function)
|
||||
}
|
||||
_ => unimplemented!("main must be a function"),
|
||||
};
|
||||
|
@ -1,7 +1,10 @@
|
||||
//! Methods to enforce constraints on expressions in a resolved aleo program.
|
||||
|
||||
use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
|
||||
use crate::{Expression, RangeOrExpression, SpreadOrExpression, StructMember, Variable};
|
||||
use crate::{
|
||||
new_variable_from_variable, Expression, RangeOrExpression, ResolvedStructMember,
|
||||
SpreadOrExpression, StructMember, Variable,
|
||||
};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::r1cs::ConstraintSystem;
|
||||
@ -138,14 +141,15 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn enforce_array_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
array: Vec<Box<SpreadOrExpression<F>>>,
|
||||
) -> ResolvedValue<F> {
|
||||
let mut result = vec![];
|
||||
array.into_iter().for_each(|element| match *element {
|
||||
SpreadOrExpression::Spread(spread) => match spread {
|
||||
Expression::Variable(variable) => {
|
||||
let array_name = new_scope_from_variable(scope.clone(), &variable);
|
||||
let array_name = new_scope_from_variable(function_scope.clone(), &variable);
|
||||
match self.get(&array_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::Array(array) => result.extend(array.clone()),
|
||||
@ -162,7 +166,12 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
value => unimplemented!("spreads only implemented for arrays, got {}", value),
|
||||
},
|
||||
SpreadOrExpression::Expression(expression) => {
|
||||
result.push(self.enforce_expression(cs, scope.clone(), expression));
|
||||
result.push(self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expression,
|
||||
));
|
||||
}
|
||||
});
|
||||
ResolvedValue::Array(result)
|
||||
@ -171,10 +180,11 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn enforce_index(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
index: Expression<F>,
|
||||
) -> usize {
|
||||
match self.enforce_expression(cs, scope.clone(), index) {
|
||||
match self.enforce_expression(cs, file_scope, function_scope, index) {
|
||||
ResolvedValue::U32(number) => number.value.unwrap() as usize,
|
||||
value => unimplemented!("From index must resolve to an integer, got {}", value),
|
||||
}
|
||||
@ -183,11 +193,12 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn enforce_array_access_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
array: Box<Expression<F>>,
|
||||
index: RangeOrExpression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope.clone(), *array) {
|
||||
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *array) {
|
||||
ResolvedValue::Array(array) => {
|
||||
match index {
|
||||
RangeOrExpression::Range(from, to) => {
|
||||
@ -202,7 +213,8 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
ResolvedValue::Array(array[from_resolved..to_resolved].to_owned())
|
||||
}
|
||||
RangeOrExpression::Expression(index) => {
|
||||
let index_resolved = self.enforce_index(cs, scope.clone(), index);
|
||||
let index_resolved =
|
||||
self.enforce_index(cs, file_scope, function_scope, index);
|
||||
array[index_resolved].to_owned()
|
||||
}
|
||||
}
|
||||
@ -214,51 +226,62 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn enforce_struct_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
variable: Variable<F>,
|
||||
members: Vec<StructMember<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
if let Some(resolved_value) = self.get_mut_variable(&variable) {
|
||||
let struct_name = new_variable_from_variable(file_scope.clone(), &variable);
|
||||
|
||||
if let Some(resolved_value) = self.get_mut_variable(&struct_name) {
|
||||
match resolved_value {
|
||||
ResolvedValue::StructDefinition(struct_definition) => {
|
||||
struct_definition
|
||||
let resolved_members = struct_definition
|
||||
.fields
|
||||
.clone()
|
||||
.iter()
|
||||
.zip(members.clone().into_iter())
|
||||
.for_each(|(field, member)| {
|
||||
.map(|(field, member)| {
|
||||
if field.variable != member.variable {
|
||||
unimplemented!("struct field variables do not match")
|
||||
}
|
||||
// Resolve and possibly enforce struct fields
|
||||
// do we need to store the results here?
|
||||
let _result =
|
||||
self.enforce_expression(cs, scope.clone(), member.expression);
|
||||
});
|
||||
// Resolve and enforce struct fields
|
||||
let member_value = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
member.expression,
|
||||
);
|
||||
|
||||
ResolvedValue::StructExpression(variable, members)
|
||||
ResolvedStructMember(member.variable, member_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
ResolvedValue::StructExpression(variable, resolved_members)
|
||||
}
|
||||
_ => unimplemented!("Inline struct type is not defined as a struct"),
|
||||
}
|
||||
} else {
|
||||
unimplemented!("Struct must be declared before it is used in an inline expression")
|
||||
unimplemented!(
|
||||
"Struct {} must be declared before it is used in an inline expression",
|
||||
struct_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_struct_access_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
struct_variable: Box<Expression<F>>,
|
||||
struct_member: Variable<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope.clone(), *struct_variable) {
|
||||
match self.enforce_expression(cs, file_scope, function_scope, *struct_variable) {
|
||||
ResolvedValue::StructExpression(_name, members) => {
|
||||
let matched_member = members
|
||||
.into_iter()
|
||||
.find(|member| member.variable == struct_member);
|
||||
let matched_member = members.into_iter().find(|member| member.0 == struct_member);
|
||||
match matched_member {
|
||||
Some(member) => self.enforce_expression(cs, scope.clone(), member.expression),
|
||||
Some(member) => member.1,
|
||||
None => unimplemented!("Cannot access struct member {}", struct_member.name),
|
||||
}
|
||||
}
|
||||
@ -266,29 +289,52 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_function_access_expression(
|
||||
fn enforce_function_call_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
function: Box<Expression<F>>,
|
||||
file_scope: String,
|
||||
function: Variable<F>,
|
||||
arguments: Vec<Expression<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
match self.enforce_expression(cs, scope, *function) {
|
||||
ResolvedValue::Function(function) => self.enforce_function(cs, function, arguments),
|
||||
value => unimplemented!("Cannot call unknown function {}", value),
|
||||
let function_name = new_variable_from_variable(file_scope.clone(), &function);
|
||||
match self.get_mut_variable(&function_name) {
|
||||
Some(value) => match value.clone() {
|
||||
ResolvedValue::Function(function) => {
|
||||
// this function call is inline so we unwrap the return value
|
||||
match self.enforce_function(cs, file_scope, function, arguments) {
|
||||
ResolvedValue::Return(return_values) => {
|
||||
if return_values.len() == 0 {
|
||||
ResolvedValue::Return(vec![])
|
||||
} else if return_values.len() == 1 {
|
||||
return_values[0].clone()
|
||||
} else {
|
||||
unimplemented!("function {} returns multiple values but is used in an expression that expects one", function_name)
|
||||
}
|
||||
}
|
||||
value => unimplemented!(
|
||||
"function {} has no return value {}",
|
||||
function_name,
|
||||
value
|
||||
),
|
||||
}
|
||||
}
|
||||
value => unimplemented!("Cannot make function call to {}", value),
|
||||
},
|
||||
None => unimplemented!("Cannot call unknown function {}", function_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_expression(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expression: Expression<F>,
|
||||
) -> ResolvedValue<F> {
|
||||
match expression {
|
||||
// Variables
|
||||
Expression::Variable(unresolved_variable) => {
|
||||
self.enforce_variable(scope, unresolved_variable)
|
||||
self.enforce_variable(function_scope, unresolved_variable)
|
||||
}
|
||||
|
||||
// Values
|
||||
@ -298,55 +344,74 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
|
||||
// Binary operations
|
||||
Expression::Add(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_add_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::Sub(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_sub_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::Mul(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_mul_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::Div(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_div_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::Pow(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_pow_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
|
||||
// Boolean operations
|
||||
Expression::Not(expression) => {
|
||||
Self::enforce_not(self.enforce_expression(cs, scope, *expression))
|
||||
}
|
||||
Expression::Not(expression) => Self::enforce_not(self.enforce_expression(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
*expression,
|
||||
)),
|
||||
Expression::Or(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_or(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::And(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_and(cs, resolved_left, resolved_right)
|
||||
}
|
||||
Expression::Eq(left, right) => {
|
||||
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
|
||||
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
|
||||
let resolved_left =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *left);
|
||||
let resolved_right =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), *right);
|
||||
|
||||
self.enforce_eq_expression(cs, resolved_left, resolved_right)
|
||||
}
|
||||
@ -365,35 +430,47 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
|
||||
// Conditionals
|
||||
Expression::IfElse(first, second, third) => {
|
||||
let resolved_first = match self.enforce_expression(cs, scope.clone(), *first) {
|
||||
let resolved_first = match self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
*first,
|
||||
) {
|
||||
ResolvedValue::Boolean(resolved) => resolved,
|
||||
_ => unimplemented!("if else conditional must resolve to boolean"),
|
||||
};
|
||||
|
||||
if resolved_first.eq(&Boolean::Constant(true)) {
|
||||
self.enforce_expression(cs, scope, *second)
|
||||
self.enforce_expression(cs, file_scope, function_scope, *second)
|
||||
} else {
|
||||
self.enforce_expression(cs, scope, *third)
|
||||
self.enforce_expression(cs, file_scope, function_scope, *third)
|
||||
}
|
||||
}
|
||||
|
||||
// Arrays
|
||||
Expression::Array(array) => self.enforce_array_expression(cs, scope, array),
|
||||
Expression::Array(array) => {
|
||||
self.enforce_array_expression(cs, file_scope, function_scope, array)
|
||||
}
|
||||
Expression::ArrayAccess(array, index) => {
|
||||
self.enforce_array_access_expression(cs, scope, array, *index)
|
||||
self.enforce_array_access_expression(cs, file_scope, function_scope, array, *index)
|
||||
}
|
||||
|
||||
// Structs
|
||||
Expression::Struct(struct_name, members) => {
|
||||
self.enforce_struct_expression(cs, scope, struct_name, members)
|
||||
}
|
||||
Expression::StructMemberAccess(struct_variable, struct_member) => {
|
||||
self.enforce_struct_access_expression(cs, scope, struct_variable, struct_member)
|
||||
self.enforce_struct_expression(cs, file_scope, function_scope, struct_name, members)
|
||||
}
|
||||
Expression::StructMemberAccess(struct_variable, struct_member) => self
|
||||
.enforce_struct_access_expression(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
struct_variable,
|
||||
struct_member,
|
||||
),
|
||||
|
||||
// Functions
|
||||
Expression::FunctionCall(function, arguments) => {
|
||||
self.enforce_function_access_expression(cs, scope, function, arguments)
|
||||
self.enforce_function_call_expression(cs, file_scope, function, arguments)
|
||||
} // _ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use snarkos_models::gadgets::{
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn integer_from_parameter(
|
||||
pub(crate) fn u32_from_parameter(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
@ -46,7 +46,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
parameter_variable
|
||||
}
|
||||
|
||||
pub(crate) fn integer_array_from_parameter(
|
||||
pub(crate) fn u32_array_from_parameter(
|
||||
&mut self,
|
||||
_cs: &mut CS,
|
||||
_scope: String,
|
||||
|
@ -34,6 +34,16 @@ pub fn new_variable_from_variable<F: Field + PrimeField>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_variable_from_variables<F: Field + PrimeField>(
|
||||
outer: &Variable<F>,
|
||||
inner: &Variable<F>,
|
||||
) -> Variable<F> {
|
||||
Variable {
|
||||
name: new_scope_from_variable(outer.name.clone(), inner),
|
||||
_field: PhantomData::<F>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! The in memory stored value for a defined name in a resolved aleo program.
|
||||
|
||||
use crate::types::{Function, Struct, StructMember, Type, Variable};
|
||||
use crate::types::{Function, Struct, Type, Variable};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use snarkos_models::gadgets::{utilities::boolean::Boolean, utilities::uint32::UInt32};
|
||||
@ -13,18 +13,29 @@ pub enum ResolvedValue<F: Field + PrimeField> {
|
||||
Boolean(Boolean),
|
||||
Array(Vec<ResolvedValue<F>>),
|
||||
StructDefinition(Struct<F>),
|
||||
StructExpression(Variable<F>, Vec<StructMember<F>>),
|
||||
StructExpression(Variable<F>, Vec<ResolvedStructMember<F>>),
|
||||
Function(Function<F>),
|
||||
Return(Vec<ResolvedValue<F>>), // add Null for function returns
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ResolvedStructMember<F: Field + PrimeField>(pub Variable<F>, pub ResolvedValue<F>);
|
||||
|
||||
impl<F: Field + PrimeField> ResolvedValue<F> {
|
||||
pub(crate) fn match_type(&self, ty: &Type<F>) -> bool {
|
||||
match (self, ty) {
|
||||
(ResolvedValue::U32(ref _a), Type::U32) => true,
|
||||
(ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true,
|
||||
(ResolvedValue::Boolean(ref _a), Type::Boolean) => true,
|
||||
(ResolvedValue::Array(ref arr), Type::Array(ref _ty, ref len)) => arr.len() == *len, // todo: add array types
|
||||
(ResolvedValue::Array(ref arr), Type::Array(ref ty, ref len)) => {
|
||||
// check array lengths are equal
|
||||
let mut res = arr.len() == *len;
|
||||
// check each value in array matches
|
||||
for value in arr {
|
||||
res &= value.match_type(ty)
|
||||
}
|
||||
res
|
||||
}
|
||||
(
|
||||
ResolvedValue::StructExpression(ref actual_name, ref _members),
|
||||
Type::Struct(ref expected_name),
|
||||
@ -60,7 +71,7 @@ impl<F: Field + PrimeField> fmt::Display for ResolvedValue<F> {
|
||||
ResolvedValue::StructExpression(ref variable, ref members) => {
|
||||
write!(f, "{} {{", variable)?;
|
||||
for (i, member) in members.iter().enumerate() {
|
||||
write!(f, "{}: {}", member.variable, member.expression)?;
|
||||
write!(f, "{}: {}", member.0, member.1)?;
|
||||
if i < members.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
@ -17,41 +17,40 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_definition_statement(
|
||||
fn store_assignment(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
assignee: Assignee<F>,
|
||||
expression: Expression<F>,
|
||||
return_value: &mut ResolvedValue<F>,
|
||||
) {
|
||||
// Create or modify the lhs variable in the current function scope
|
||||
match assignee {
|
||||
Assignee::Variable(name) => {
|
||||
// Store the variable in the current scope
|
||||
let definition_name = new_scope_from_variable(scope.clone(), &name);
|
||||
let definition_name = new_scope_from_variable(function_scope.clone(), &name);
|
||||
|
||||
// Evaluate the rhs expression in the current function scope
|
||||
let result = self.enforce_expression(cs, scope, expression);
|
||||
|
||||
self.store(definition_name, result);
|
||||
self.store(definition_name, return_value.to_owned());
|
||||
}
|
||||
Assignee::Array(array, index_expression) => {
|
||||
// Evaluate the rhs expression in the current function scope
|
||||
let result = &mut self.enforce_expression(cs, scope.clone(), expression);
|
||||
|
||||
// Check that array exists
|
||||
let expected_array_name = self.resolve_assignee(scope.clone(), *array);
|
||||
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, scope.clone(), 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 {
|
||||
ResolvedValue::Array(old) => {
|
||||
old[index] = result.to_owned();
|
||||
old[index] = return_value.to_owned();
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("Cannot assign single index to array of values ")
|
||||
@ -75,7 +74,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
|
||||
// Modify the range of values of the array in place
|
||||
match self.get_mut(&expected_array_name) {
|
||||
Some(value) => match (value, result) {
|
||||
Some(value) => match (value, return_value) {
|
||||
(ResolvedValue::Array(old), ResolvedValue::Array(new)) => {
|
||||
let to_index = to_index_option.unwrap_or(old.len());
|
||||
old.splice(from_index..to_index, new.iter().cloned());
|
||||
@ -94,17 +93,17 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
Assignee::StructMember(struct_variable, struct_member) => {
|
||||
// Check that struct exists
|
||||
let expected_struct_name = self.resolve_assignee(scope.clone(), *struct_variable);
|
||||
let expected_struct_name =
|
||||
self.resolve_assignee(function_scope.clone(), *struct_variable);
|
||||
|
||||
match self.get_mut(&expected_struct_name) {
|
||||
Some(value) => match value {
|
||||
ResolvedValue::StructExpression(_variable, members) => {
|
||||
// Modify the struct member in place
|
||||
let matched_member = members
|
||||
.into_iter()
|
||||
.find(|member| member.variable == struct_member);
|
||||
let matched_member =
|
||||
members.into_iter().find(|member| member.0 == struct_member);
|
||||
match matched_member {
|
||||
Some(mut member) => member.expression = expression,
|
||||
Some(mut member) => member.1 = return_value.to_owned(),
|
||||
None => unimplemented!(
|
||||
"struct member {} does not exist in {}",
|
||||
struct_member,
|
||||
@ -122,13 +121,88 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_assign_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
assignee: Assignee<F>,
|
||||
expression: Expression<F>,
|
||||
) {
|
||||
let result_value = &mut self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expression,
|
||||
);
|
||||
|
||||
self.store_assignment(cs, file_scope, function_scope, assignee, result_value);
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_definition_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
ty: Type<F>,
|
||||
assignee: Assignee<F>,
|
||||
expression: Expression<F>,
|
||||
) {
|
||||
let result_value = &mut self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expression,
|
||||
);
|
||||
|
||||
if result_value.match_type(&ty) {
|
||||
self.store_assignment(cs, file_scope, function_scope, assignee, result_value);
|
||||
} else {
|
||||
unimplemented!("incompatible types {} = {}", assignee, result_value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_multiple_definition_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
assignees: Vec<Assignee<F>>,
|
||||
function: Expression<F>,
|
||||
) {
|
||||
// Expect return values from function
|
||||
let return_values =
|
||||
match self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), function)
|
||||
{
|
||||
ResolvedValue::Return(values) => values,
|
||||
value => unimplemented!(
|
||||
"multiple assignment only implemented for functions, got {}",
|
||||
value
|
||||
),
|
||||
};
|
||||
|
||||
assignees
|
||||
.into_iter()
|
||||
.zip(return_values.into_iter())
|
||||
.for_each(|(assignee, mut return_value)| {
|
||||
self.store_assignment(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
assignee,
|
||||
&mut return_value,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn enforce_return_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
statements: Vec<Expression<F>>,
|
||||
return_types: Vec<Type<F>>,
|
||||
) -> ResolvedValue<F> {
|
||||
@ -137,7 +211,12 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
.into_iter()
|
||||
.zip(return_types.into_iter())
|
||||
.map(|(expression, ty)| {
|
||||
let result = self.enforce_expression(cs, scope.clone(), expression);
|
||||
let result = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expression,
|
||||
);
|
||||
if !result.match_type(&ty) {
|
||||
unimplemented!("expected return type {}, got {}", ty, result)
|
||||
} else {
|
||||
@ -151,20 +230,54 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
fn enforce_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
statement: Statement<F>,
|
||||
return_types: Vec<Type<F>>,
|
||||
) {
|
||||
match statement {
|
||||
Statement::Definition(variable, expression) => {
|
||||
self.enforce_definition_statement(cs, scope, variable, expression);
|
||||
}
|
||||
Statement::For(index, start, stop, statements) => {
|
||||
self.enforce_for_statement(cs, scope, index, start, stop, statements);
|
||||
}
|
||||
Statement::Return(statements) => {
|
||||
// TODO: add support for early termination
|
||||
let _res = self.enforce_return_statement(cs, scope, statements, return_types);
|
||||
let _res = self.enforce_return_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
statements,
|
||||
return_types,
|
||||
);
|
||||
}
|
||||
Statement::Assign(variable, expression) => {
|
||||
self.enforce_assign_statement(cs, file_scope, function_scope, variable, expression);
|
||||
}
|
||||
Statement::Definition(ty, assignee, expression) => {
|
||||
self.enforce_definition_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
ty,
|
||||
assignee,
|
||||
expression,
|
||||
);
|
||||
}
|
||||
Statement::MultipleDefinition(assignees, function) => {
|
||||
self.enforce_multiple_definition_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
assignees,
|
||||
function,
|
||||
);
|
||||
}
|
||||
Statement::For(index, start, stop, statements) => {
|
||||
self.enforce_for_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
index,
|
||||
start,
|
||||
stop,
|
||||
statements,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -172,7 +285,8 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
pub(crate) fn enforce_for_statement(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
scope: String,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
index: Variable<F>,
|
||||
start: Integer,
|
||||
stop: Integer,
|
||||
@ -181,14 +295,19 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
|
||||
for i in start.to_usize()..stop.to_usize() {
|
||||
// Store index in current function scope.
|
||||
// For loop scope is not implemented.
|
||||
let index_name = new_scope_from_variable(scope.clone(), &index);
|
||||
let index_name = new_scope_from_variable(function_scope.clone(), &index);
|
||||
self.store(index_name, ResolvedValue::U32(UInt32::constant(i as u32)));
|
||||
|
||||
// Evaluate statements (for loop statements should not have a return type)
|
||||
statements
|
||||
.clone()
|
||||
.into_iter()
|
||||
.for_each(|statement| self.enforce_statement(cs, scope.clone(), statement, vec![]));
|
||||
statements.clone().into_iter().for_each(|statement| {
|
||||
self.enforce_statement(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
statement,
|
||||
vec![],
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,27 @@
|
||||
use crate::Variable;
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
type ImportPath<'ast> = &'ast Path;
|
||||
pub(crate) type PathString<'ast> = &'ast str;
|
||||
// pub(crate) type Variable<'ast = &'ast str;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Import<'ast> {
|
||||
source: ImportPath<'ast>,
|
||||
symbol: Option<PathString<'ast>>,
|
||||
alias: Option<PathString<'ast>>,
|
||||
pub struct ImportSymbol<F: Field + PrimeField> {
|
||||
pub symbol: Variable<F>,
|
||||
pub alias: Option<Variable<F>>,
|
||||
}
|
||||
|
||||
impl<'ast> Import<'ast> {
|
||||
pub fn new(symbol: Option<PathString<'ast>>, source: ImportPath<'ast>) -> Import<'ast> {
|
||||
Import {
|
||||
source,
|
||||
symbol,
|
||||
alias: None,
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Import<'ast, F: Field + PrimeField> {
|
||||
pub(crate) source: ImportPath<'ast>,
|
||||
pub(crate) symbols: Vec<ImportSymbol<F>>,
|
||||
}
|
||||
|
||||
pub fn new_with_alias(
|
||||
symbol: Option<PathString<'ast>>,
|
||||
source: ImportPath<'ast>,
|
||||
alias: PathString<'ast>,
|
||||
) -> Import<'ast> {
|
||||
Import {
|
||||
source,
|
||||
symbol,
|
||||
alias: Some(alias),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias(mut self, alias: Option<PathString<'ast>>) -> Self {
|
||||
self.alias = alias;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_alias(&self) -> &Option<PathString<'ast>> {
|
||||
&self.alias
|
||||
impl<'ast, F: Field + PrimeField> Import<'ast, F> {
|
||||
pub fn new(source: ImportPath<'ast>, symbols: Vec<ImportSymbol<F>>) -> Import<'ast, F> {
|
||||
Import { source, symbols }
|
||||
}
|
||||
|
||||
pub fn get_source(&self) -> &Path {
|
||||
@ -49,27 +32,54 @@ impl<'ast> Import<'ast> {
|
||||
let path = self.get_source().to_str().unwrap();
|
||||
format!("{}.leo", path)
|
||||
}
|
||||
|
||||
pub fn is_star(&self) -> bool {
|
||||
self.symbols.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Import<'ast> {
|
||||
impl<F: Field + PrimeField> fmt::Display for ImportSymbol<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.alias {
|
||||
Some(ref alias) => write!(f, "import {} as {}", self.source.display(), alias),
|
||||
None => write!(f, "import {}", self.source.display()),
|
||||
if self.alias.is_some() {
|
||||
write!(f, "\t{} as {}", self.symbol, self.alias.as_ref().unwrap())
|
||||
} else {
|
||||
write!(f, "\t{}", self.symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Debug for Import<'ast> {
|
||||
impl<'ast, F: Field + PrimeField> fmt::Display for Import<'ast, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.alias {
|
||||
Some(ref alias) => write!(
|
||||
f,
|
||||
"import(source: {}, alias: {})",
|
||||
self.source.display(),
|
||||
alias
|
||||
),
|
||||
None => write!(f, "import( source: {})", self.source.display()),
|
||||
write!(f, "from {} import ", self.source.display())?;
|
||||
if self.symbols.is_empty() {
|
||||
write!(f, "*")
|
||||
} else {
|
||||
write!(f, "{{\n")?;
|
||||
for (i, symbol) in self.symbols.iter().enumerate() {
|
||||
write!(f, "{}", symbol)?;
|
||||
if i < self.symbols.len() - 1 {
|
||||
write!(f, ",\n")?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> fmt::Debug for Import<'ast, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "from {} import ", self.source.display())?;
|
||||
if self.symbols.is_empty() {
|
||||
write!(f, "*")
|
||||
} else {
|
||||
write!(f, "{{\n")?;
|
||||
for (i, symbol) in self.symbols.iter().enumerate() {
|
||||
write!(f, "{}", symbol)?;
|
||||
if i < self.symbols.len() - 1 {
|
||||
write!(f, ",\n")?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,10 @@ protected_name = { visibility | value_boolean | "return" }
|
||||
// "def" | "in" | "return" | "struct" | "true" }
|
||||
|
||||
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
|
||||
optionally_typed_variable = { (ty ~ variable) | (variable)}
|
||||
optionally_typed_variable_tuple = _{ optionally_typed_variable ~ ("," ~ optionally_typed_variable)* }
|
||||
|
||||
expression_primitive = { value | variable }
|
||||
|
||||
/// Access
|
||||
@ -139,17 +143,19 @@ expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
|
||||
|
||||
/// Statements
|
||||
|
||||
statement_assign = { assignee ~ "=" ~ expression }
|
||||
statement_definition = { ty ~ variable ~ "=" ~ expression }
|
||||
statement_return = { "return" ~ expression_tuple }
|
||||
statement_for = { "for" ~ variable ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"}
|
||||
statement_multiple_assignment = { optionally_typed_variable_tuple ~ "=" ~ variable ~ "(" ~ expression_tuple ~ ")" }
|
||||
statement_definition = { ty ~ variable ~ "=" ~ expression }
|
||||
statement_assign = { assignee ~ "=" ~ expression }
|
||||
|
||||
statement = {
|
||||
(statement_return
|
||||
| (statement_for
|
||||
| statement_multiple_assignment
|
||||
| statement_definition
|
||||
| statement_assign
|
||||
) ~ NEWLINE
|
||||
) ~ LINE_END
|
||||
) ~ NEWLINE*
|
||||
}
|
||||
|
||||
@ -159,19 +165,23 @@ parameter = {variable ~ ":" ~ visibility? ~ ty}
|
||||
parameter_list = _{(parameter ~ ("," ~ parameter)*)?}
|
||||
|
||||
function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ "{" ~ NEWLINE* ~ statement* ~ "}"}
|
||||
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
|
||||
|
||||
/// Utilities
|
||||
|
||||
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
|
||||
WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* }
|
||||
LINE_END = _{";" ~ NEWLINE*}
|
||||
|
||||
/// Imports
|
||||
|
||||
import = { main_import | from_import }
|
||||
from_import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ variable ~ ("as" ~ variable)? ~ NEWLINE*}
|
||||
main_import = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ variable)? ~ NEWLINE+}
|
||||
import_source = @{(!"\"" ~ ANY)*}
|
||||
import_symbol = { variable ~ ("as" ~ variable)? }
|
||||
import_symbol_tuple = _{ import_symbol ~ ("," ~ NEWLINE* ~ import_symbol)* }
|
||||
|
||||
import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ ("*" | ("{" ~ NEWLINE* ~ import_symbol_tuple ~ NEWLINE* ~ "}")) ~ LINE_END}
|
||||
// import = { main_import | from_import }
|
||||
// main_import = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ variable)? ~ NEWLINE+}
|
||||
|
||||
/// Program File
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! Module containing structs and types that make up an Leo program.
|
||||
|
||||
extern crate from_pest;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate pest;
|
||||
extern crate pest_ast;
|
||||
#[macro_use] extern crate pest_derive;
|
||||
#[macro_use]
|
||||
extern crate pest_derive;
|
||||
|
||||
pub mod ast;
|
||||
|
||||
|
@ -86,7 +86,7 @@ pub enum Expression<F: Field + PrimeField> {
|
||||
StructMemberAccess(Box<Expression<F>>, Variable<F>), // (struct name, struct member name)
|
||||
|
||||
// Functions
|
||||
FunctionCall(Box<Expression<F>>, Vec<Expression<F>>),
|
||||
FunctionCall(Variable<F>, Vec<Expression<F>>),
|
||||
}
|
||||
|
||||
/// Definition assignee: v, arr[0..2], Point p.x
|
||||
@ -97,16 +97,7 @@ pub enum Assignee<F: Field + PrimeField> {
|
||||
StructMember(Box<Assignee<F>>, Variable<F>),
|
||||
}
|
||||
|
||||
/// Program statement that defines some action (or expression) to be carried out.
|
||||
#[derive(Clone)]
|
||||
pub enum Statement<F: Field + PrimeField> {
|
||||
// Declaration(Variable),
|
||||
Definition(Assignee<F>, Expression<F>),
|
||||
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
|
||||
Return(Vec<Expression<F>>),
|
||||
}
|
||||
|
||||
/// Explicit type used for defining struct members and function parameters
|
||||
/// Explicit type used for defining a variable or expression type
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Type<F: Field + PrimeField> {
|
||||
U32,
|
||||
@ -116,6 +107,17 @@ pub enum Type<F: Field + PrimeField> {
|
||||
Struct(Variable<F>),
|
||||
}
|
||||
|
||||
/// Program statement that defines some action (or expression) to be carried out.
|
||||
#[derive(Clone)]
|
||||
pub enum Statement<F: Field + PrimeField> {
|
||||
// Declaration(Variable),
|
||||
Return(Vec<Expression<F>>),
|
||||
Assign(Assignee<F>, Expression<F>),
|
||||
Definition(Type<F>, Assignee<F>, Expression<F>),
|
||||
MultipleDefinition(Vec<Assignee<F>>, Expression<F>),
|
||||
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StructMember<F: Field + PrimeField> {
|
||||
pub variable: Variable<F>,
|
||||
@ -165,12 +167,16 @@ impl<F: Field + PrimeField> Function<F> {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program<'ast, F: Field + PrimeField> {
|
||||
pub name: Variable<F>,
|
||||
pub imports: Vec<Import<'ast>>,
|
||||
pub imports: Vec<Import<'ast, F>>,
|
||||
pub structs: HashMap<Variable<F>, Struct<F>>,
|
||||
pub functions: HashMap<FunctionName, Function<F>>,
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> Program<'ast, F> {
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name.name.clone()
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: String) -> Self {
|
||||
self.name = Variable {
|
||||
name,
|
||||
|
@ -141,21 +141,37 @@ impl<F: Field + PrimeField> fmt::Display for Assignee<F> {
|
||||
impl<F: Field + PrimeField> fmt::Display for Statement<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Statement::Definition(ref variable, ref statement) => {
|
||||
write!(f, "{} = {}", variable, statement)
|
||||
Statement::Return(ref statements) => {
|
||||
write!(f, "return ")?;
|
||||
for (i, value) in statements.iter().enumerate() {
|
||||
write!(f, "{}", value)?;
|
||||
if i < statements.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
Statement::Assign(ref variable, ref statement) => {
|
||||
write!(f, "{} = {};", variable, statement)
|
||||
}
|
||||
Statement::Definition(ref ty, ref assignee, ref statement) => {
|
||||
write!(f, "{} {} = {};", ty, assignee, statement)
|
||||
}
|
||||
Statement::MultipleDefinition(ref assignees, ref function) => {
|
||||
for (i, id) in assignees.iter().enumerate() {
|
||||
write!(f, "{}", id)?;
|
||||
if i < assignees.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, " = {};", function)
|
||||
}
|
||||
Statement::For(ref var, ref start, ref stop, ref list) => {
|
||||
write!(f, "for {} in {}..{} do\n", var, start, stop)?;
|
||||
for l in list {
|
||||
write!(f, "\t\t{}\n", l)?;
|
||||
}
|
||||
write!(f, "\tendfor")
|
||||
}
|
||||
Statement::Return(ref statements) => {
|
||||
statements.iter().for_each(|statement| {
|
||||
write!(f, "return {}", statement).unwrap();
|
||||
});
|
||||
write!(f, "\n")
|
||||
write!(f, "\tendfor;")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,21 +180,37 @@ impl<F: Field + PrimeField> fmt::Display for Statement<F> {
|
||||
impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Statement::Definition(ref variable, ref statement) => {
|
||||
write!(f, "{} = {}", variable, statement)
|
||||
Statement::Return(ref statements) => {
|
||||
write!(f, "return ")?;
|
||||
for (i, value) in statements.iter().enumerate() {
|
||||
write!(f, "{}", value)?;
|
||||
if i < statements.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
Statement::Assign(ref variable, ref statement) => {
|
||||
write!(f, "{} = {};", variable, statement)
|
||||
}
|
||||
Statement::Definition(ref ty, ref assignee, ref statement) => {
|
||||
write!(f, "{} {} = {};", ty, assignee, statement)
|
||||
}
|
||||
Statement::MultipleDefinition(ref assignees, ref function) => {
|
||||
for (i, id) in assignees.iter().enumerate() {
|
||||
write!(f, "{}", id)?;
|
||||
if i < assignees.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, " = {}();", function)
|
||||
}
|
||||
Statement::For(ref var, ref start, ref stop, ref list) => {
|
||||
write!(f, "for {:?} in {:?}..{:?} do\n", var, start, stop)?;
|
||||
for l in list {
|
||||
write!(f, "\t\t{:?}\n", l)?;
|
||||
}
|
||||
write!(f, "\tendfor")
|
||||
}
|
||||
Statement::Return(ref statements) => {
|
||||
statements.iter().for_each(|statement| {
|
||||
write!(f, "return {}", statement).unwrap();
|
||||
});
|
||||
write!(f, "\n")
|
||||
write!(f, "\tendfor;")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Logic to convert from an abstract syntax tree (ast) representation to a typed aleo program.
|
||||
|
||||
use crate::ast;
|
||||
use crate::{types, Import, PathString};
|
||||
use crate::{types, Import, ImportSymbol};
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
use std::collections::HashMap;
|
||||
@ -192,43 +192,6 @@ impl<'ast, F: Field + PrimeField> From<ast::TernaryExpression<'ast>> for types::
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::Expression<F> {
|
||||
fn from(expression: ast::PostfixExpression<'ast>) -> Self {
|
||||
let variable = types::Expression::Variable(types::Variable::from(expression.variable));
|
||||
|
||||
// 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
|
||||
|
||||
// we start with the id, and we fold the array of accesses by wrapping the current value
|
||||
expression
|
||||
.accesses
|
||||
.into_iter()
|
||||
.fold(variable, |acc, access| match access {
|
||||
ast::Access::Call(function) => match acc {
|
||||
types::Expression::Variable(_) => types::Expression::FunctionCall(
|
||||
Box::new(acc),
|
||||
function
|
||||
.expressions
|
||||
.into_iter()
|
||||
.map(|expression| types::Expression::from(expression))
|
||||
.collect(),
|
||||
),
|
||||
expression => {
|
||||
unimplemented!("only function names are callable, found \"{}\"", expression)
|
||||
}
|
||||
},
|
||||
ast::Access::Member(struct_member) => types::Expression::StructMemberAccess(
|
||||
Box::new(acc),
|
||||
types::Variable::from(struct_member.variable),
|
||||
),
|
||||
ast::Access::Array(array) => types::Expression::ArrayAccess(
|
||||
Box::new(acc),
|
||||
Box::new(types::RangeOrExpression::from(array.expression)),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::ArrayInlineExpression<'ast>> for types::Expression<F> {
|
||||
fn from(array: ast::ArrayInlineExpression<'ast>) -> Self {
|
||||
types::Expression::Array(
|
||||
@ -251,6 +214,65 @@ impl<'ast, F: Field + PrimeField> From<ast::ArrayInitializerExpression<'ast>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::InlineStructMember<'ast>> for types::StructMember<F> {
|
||||
fn from(member: ast::InlineStructMember<'ast>) -> Self {
|
||||
types::StructMember {
|
||||
variable: types::Variable::from(member.variable),
|
||||
expression: types::Expression::from(member.expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::StructInlineExpression<'ast>> for types::Expression<F> {
|
||||
fn from(expression: ast::StructInlineExpression<'ast>) -> Self {
|
||||
let variable = types::Variable::from(expression.variable);
|
||||
let members = expression
|
||||
.members
|
||||
.into_iter()
|
||||
.map(|member| types::StructMember::from(member))
|
||||
.collect::<Vec<types::StructMember<F>>>();
|
||||
|
||||
types::Expression::Struct(variable, members)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::Expression<F> {
|
||||
fn from(expression: ast::PostfixExpression<'ast>) -> Self {
|
||||
let variable = types::Expression::Variable(types::Variable::from(expression.variable));
|
||||
|
||||
// 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
|
||||
|
||||
// we start with the id, and we fold the array of accesses by wrapping the current value
|
||||
expression
|
||||
.accesses
|
||||
.into_iter()
|
||||
.fold(variable, |acc, access| match access {
|
||||
ast::Access::Call(function) => match acc {
|
||||
types::Expression::Variable(variable) => types::Expression::FunctionCall(
|
||||
variable,
|
||||
function
|
||||
.expressions
|
||||
.into_iter()
|
||||
.map(|expression| types::Expression::from(expression))
|
||||
.collect(),
|
||||
),
|
||||
expression => {
|
||||
unimplemented!("only function names are callable, found \"{}\"", expression)
|
||||
}
|
||||
},
|
||||
ast::Access::Member(struct_member) => types::Expression::StructMemberAccess(
|
||||
Box::new(acc),
|
||||
types::Variable::from(struct_member.variable),
|
||||
),
|
||||
ast::Access::Array(array) => types::Expression::ArrayAccess(
|
||||
Box::new(acc),
|
||||
Box::new(types::RangeOrExpression::from(array.expression)),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Expression<F> {
|
||||
fn from(expression: ast::Expression<'ast>) -> Self {
|
||||
match expression {
|
||||
@ -261,18 +283,13 @@ impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Express
|
||||
ast::Expression::Ternary(expression) => types::Expression::from(expression),
|
||||
ast::Expression::ArrayInline(expression) => types::Expression::from(expression),
|
||||
ast::Expression::ArrayInitializer(expression) => types::Expression::from(expression),
|
||||
ast::Expression::StructInline(_expression) => {
|
||||
unimplemented!("unknown type for inline struct expression")
|
||||
}
|
||||
ast::Expression::StructInline(expression) => types::Expression::from(expression),
|
||||
ast::Expression::Postfix(expression) => types::Expression::from(expression),
|
||||
// _ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// pest ast -> typed types::Expression
|
||||
/// For defined types (ex: u32[4]) we manually construct the expression instead of implementing the From trait.
|
||||
/// This saves us from having to resolve things at a later point in time.
|
||||
impl<'ast, F: Field + PrimeField> types::Expression<F> {
|
||||
fn get_count(count: ast::Value<'ast>) -> usize {
|
||||
match count {
|
||||
@ -284,34 +301,6 @@ impl<'ast, F: Field + PrimeField> types::Expression<F> {
|
||||
size => unimplemented!("Array size should be an integer {}", size),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_struct(ty: ast::StructType<'ast>, expression: ast::Expression<'ast>) -> Self {
|
||||
let declaration_struct = ty.variable.value;
|
||||
match expression {
|
||||
ast::Expression::StructInline(inline_struct) => {
|
||||
if inline_struct.variable.value != declaration_struct {
|
||||
unimplemented!("Declared struct type must match inline struct type")
|
||||
}
|
||||
let variable = types::Variable::from(inline_struct.variable);
|
||||
let members = inline_struct
|
||||
.members
|
||||
.into_iter()
|
||||
.map(|member| types::StructMember::from(member))
|
||||
.collect::<Vec<types::StructMember<F>>>();
|
||||
|
||||
types::Expression::Struct(variable, members)
|
||||
}
|
||||
_ => unimplemented!("Struct declaration must be followed by inline struct"),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_type(ty: ast::Type<'ast>, expression: ast::Expression<'ast>) -> Self {
|
||||
match ty {
|
||||
ast::Type::Basic(_ty) => Self::from(expression),
|
||||
ast::Type::Array(_ty) => Self::from(expression),
|
||||
ast::Type::Struct(ty) => Self::from_struct(ty, expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// pest ast -> types::Assignee
|
||||
@ -345,24 +334,6 @@ impl<'ast, F: Field + PrimeField> From<ast::Assignee<'ast>> for types::Assignee<
|
||||
|
||||
/// pest ast -> types::Statement
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::AssignStatement<'ast>) -> Self {
|
||||
types::Statement::Definition(
|
||||
types::Assignee::from(statement.assignee),
|
||||
types::Expression::from(statement.expression),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
|
||||
types::Statement::Definition(
|
||||
types::Assignee::from(statement.variable),
|
||||
types::Expression::from_type(statement.ty, statement.expression),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::ReturnStatement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::ReturnStatement<'ast>) -> Self {
|
||||
types::Statement::Return(
|
||||
@ -399,13 +370,57 @@ impl<'ast, F: Field + PrimeField> From<ast::ForStatement<'ast>> for types::State
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::MultipleAssignmentStatement<'ast>>
|
||||
for types::Statement<F>
|
||||
{
|
||||
fn from(statement: ast::MultipleAssignmentStatement<'ast>) -> Self {
|
||||
let assignees = statement
|
||||
.assignees
|
||||
.into_iter()
|
||||
.map(|i| types::Assignee::Variable(types::Variable::from(i.id)))
|
||||
.collect();
|
||||
|
||||
types::Statement::MultipleDefinition(
|
||||
assignees,
|
||||
types::Expression::FunctionCall(
|
||||
types::Variable::from(statement.function_name),
|
||||
statement
|
||||
.arguments
|
||||
.into_iter()
|
||||
.map(|e| types::Expression::from(e))
|
||||
.collect(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::AssignStatement<'ast>) -> Self {
|
||||
types::Statement::Assign(
|
||||
types::Assignee::from(statement.assignee),
|
||||
types::Expression::from(statement.expression),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
|
||||
types::Statement::Definition(
|
||||
types::Type::from(statement.ty),
|
||||
types::Assignee::from(statement.variable),
|
||||
types::Expression::from(statement.expression),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::Statement<'ast>> for types::Statement<F> {
|
||||
fn from(statement: ast::Statement<'ast>) -> Self {
|
||||
match statement {
|
||||
ast::Statement::Return(statement) => types::Statement::from(statement),
|
||||
ast::Statement::Iteration(statement) => types::Statement::from(statement),
|
||||
ast::Statement::MultipleAssignment(statement) => types::Statement::from(statement),
|
||||
ast::Statement::Assign(statement) => types::Statement::from(statement),
|
||||
ast::Statement::Definition(statement) => types::Statement::from(statement),
|
||||
ast::Statement::Iteration(statement) => types::Statement::from(statement),
|
||||
ast::Statement::Return(statement) => types::Statement::from(statement),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -449,15 +464,6 @@ impl<'ast, F: Field + PrimeField> From<ast::Type<'ast>> for types::Type<F> {
|
||||
|
||||
/// pest ast -> types::Struct
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::InlineStructMember<'ast>> for types::StructMember<F> {
|
||||
fn from(member: ast::InlineStructMember<'ast>) -> Self {
|
||||
types::StructMember {
|
||||
variable: types::Variable::from(member.variable),
|
||||
expression: types::Expression::from(member.expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, F: Field + PrimeField> From<ast::StructField<'ast>> for types::StructField<F> {
|
||||
fn from(struct_field: ast::StructField<'ast>) -> Self {
|
||||
types::StructField {
|
||||
@ -485,7 +491,6 @@ impl<'ast, F: Field + PrimeField> From<ast::Struct<'ast>> for types::Struct<F> {
|
||||
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Parameter<F> {
|
||||
fn from(parameter: ast::Parameter<'ast>) -> Self {
|
||||
let ty = types::Type::from(parameter.ty);
|
||||
println!("type {}", ty);
|
||||
let variable = types::Variable::from(parameter.variable);
|
||||
|
||||
if parameter.visibility.is_some() {
|
||||
@ -546,22 +551,24 @@ impl<'ast, F: Field + PrimeField> From<ast::Function<'ast>> for types::Function<
|
||||
|
||||
/// pest ast -> Import
|
||||
|
||||
impl<'ast> From<ast::Variable<'ast>> for PathString<'ast> {
|
||||
fn from(import: ast::Variable<'ast>) -> Self {
|
||||
import.span.as_str()
|
||||
impl<'ast, F: Field + PrimeField> From<ast::ImportSymbol<'ast>> for ImportSymbol<F> {
|
||||
fn from(symbol: ast::ImportSymbol<'ast>) -> Self {
|
||||
ImportSymbol {
|
||||
symbol: types::Variable::from(symbol.value),
|
||||
alias: symbol.alias.map(|alias| types::Variable::from(alias)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> From<ast::Import<'ast>> for Import<'ast> {
|
||||
impl<'ast, F: Field + PrimeField> From<ast::Import<'ast>> for Import<'ast, F> {
|
||||
fn from(import: ast::Import<'ast>) -> Self {
|
||||
match import {
|
||||
ast::Import::Main(import) => Import::new(None, Path::new(import.source.span.as_str()))
|
||||
.alias(import.alias.map(|alias| PathString::from(alias))),
|
||||
ast::Import::From(import) => Import::new(
|
||||
Some(PathString::from(import.symbol)),
|
||||
Path::new(import.source.span.as_str()),
|
||||
)
|
||||
.alias(import.alias.map(|alias| PathString::from(alias))),
|
||||
Import {
|
||||
source: Path::new(import.source.span.as_str()),
|
||||
symbols: import
|
||||
.symbols
|
||||
.into_iter()
|
||||
.map(|symbol| ImportSymbol::from(symbol))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -575,7 +582,7 @@ impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast,
|
||||
.imports
|
||||
.into_iter()
|
||||
.map(|import| Import::from(import))
|
||||
.collect::<Vec<Import>>();
|
||||
.collect::<Vec<Import<'ast, F>>>();
|
||||
|
||||
let mut structs = HashMap::new();
|
||||
let mut functions = HashMap::new();
|
||||
|
Loading…
Reference in New Issue
Block a user