mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-26 11:45:00 +03:00
Merge pull request #198 from AleoHQ/feature/tuple
Implement tuple values and types
This commit is contained in:
commit
febc050a94
@ -7,6 +7,7 @@ use serde::Serialize;
|
||||
#[pest_ast(rule(Rule::access))]
|
||||
pub enum Access<'ast> {
|
||||
Array(ArrayAccess<'ast>),
|
||||
Tuple(TupleAccess<'ast>),
|
||||
Call(CallAccess<'ast>),
|
||||
Object(MemberAccess<'ast>),
|
||||
StaticObject(StaticMemberAccess<'ast>),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
access::{ArrayAccess, MemberAccess},
|
||||
access::{ArrayAccess, MemberAccess, TupleAccess},
|
||||
ast::Rule,
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@ use std::fmt;
|
||||
#[pest_ast(rule(Rule::access_assignee))]
|
||||
pub enum AssigneeAccess<'ast> {
|
||||
Array(ArrayAccess<'ast>),
|
||||
Tuple(TupleAccess<'ast>),
|
||||
Member(MemberAccess<'ast>),
|
||||
}
|
||||
|
||||
@ -18,6 +19,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::Tuple(ref tuple) => write!(f, ".{}", tuple.number),
|
||||
AssigneeAccess::Member(ref member) => write!(f, ".{}", member.identifier),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{ast::Rule, expressions::Expression, SpanDef};
|
||||
use crate::{ast::Rule, expressions::TupleExpression, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
@ -7,7 +7,7 @@ use serde::Serialize;
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::access_call))]
|
||||
pub struct CallAccess<'ast> {
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
pub expressions: TupleExpression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
|
@ -15,3 +15,6 @@ pub use member_access::*;
|
||||
|
||||
pub mod static_member_access;
|
||||
pub use static_member_access::*;
|
||||
|
||||
pub mod tuple_access;
|
||||
pub use tuple_access::*;
|
||||
|
14
ast/src/access/tuple_access.rs
Normal file
14
ast/src/access/tuple_access.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use crate::{ast::Rule, values::PositiveNumber, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::access_tuple))]
|
||||
pub struct TupleAccess<'ast> {
|
||||
pub number: PositiveNumber<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
@ -14,6 +14,7 @@ use crate::{
|
||||
values::Value,
|
||||
};
|
||||
|
||||
use crate::expressions::TupleExpression;
|
||||
use from_pest::{ConversionError, FromPest, Void};
|
||||
use pest::{
|
||||
error::Error,
|
||||
@ -64,6 +65,9 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
|
||||
let next = clone.into_inner().next().unwrap();
|
||||
match next.as_rule() {
|
||||
Rule::expression => Expression::from_pest(&mut pair.into_inner()).unwrap(), // Parenthesis case
|
||||
Rule::expression_tuple => {
|
||||
Expression::Tuple(TupleExpression::from_pest(&mut pair.into_inner()).unwrap())
|
||||
}
|
||||
Rule::expression_array_inline => {
|
||||
Expression::ArrayInline(ArrayInlineExpression::from_pest(&mut pair.into_inner()).unwrap())
|
||||
}
|
||||
|
@ -22,12 +22,6 @@ pub use range::*;
|
||||
pub mod range_or_expression;
|
||||
pub use range_or_expression::*;
|
||||
|
||||
pub mod return_;
|
||||
pub use return_::*;
|
||||
|
||||
pub mod return_tuple;
|
||||
pub use return_tuple::*;
|
||||
|
||||
pub mod spread;
|
||||
pub use spread::*;
|
||||
|
||||
@ -37,5 +31,8 @@ pub use spread_or_expression::*;
|
||||
pub mod static_;
|
||||
pub use static_::*;
|
||||
|
||||
pub mod variable;
|
||||
pub use variable::*;
|
||||
pub mod variables;
|
||||
pub use variables::*;
|
||||
|
||||
pub mod variable_name;
|
||||
pub use variable_name::*;
|
||||
|
@ -1,21 +0,0 @@
|
||||
use crate::{ast::Rule, common::ReturnTuple, expressions::Expression};
|
||||
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::return_))]
|
||||
pub enum Return<'ast> {
|
||||
Single(Expression<'ast>),
|
||||
Tuple(ReturnTuple<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Return<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Return::Single(ref expression) => write!(f, "{}", expression),
|
||||
Return::Tuple(ref expressions) => write!(f, "{}", expressions),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
use crate::{ast::Rule, expressions::Expression, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::return_tuple))]
|
||||
pub struct ReturnTuple<'ast> {
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for ReturnTuple<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for (i, expression) in self.expressions.iter().enumerate() {
|
||||
write!(f, "{}", expression)?;
|
||||
if i < self.expressions.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
ast::Rule,
|
||||
common::{Identifier, Mutable},
|
||||
types::Type,
|
||||
SpanDef,
|
||||
};
|
||||
|
||||
@ -11,28 +10,21 @@ use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::variable))]
|
||||
pub struct Variable<'ast> {
|
||||
#[pest_ast(rule(Rule::variable_name))]
|
||||
pub struct VariableName<'ast> {
|
||||
pub mutable: Option<Mutable>,
|
||||
pub identifier: Identifier<'ast>,
|
||||
pub _type: Option<Type<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Variable<'ast> {
|
||||
impl<'ast> fmt::Display for VariableName<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(ref _mutable) = self.mutable {
|
||||
write!(f, "mut ")?;
|
||||
}
|
||||
|
||||
write!(f, "{}", self.identifier)?;
|
||||
|
||||
if let Some(ref _type) = self._type {
|
||||
write!(f, ": {}", _type)?;
|
||||
}
|
||||
|
||||
write!(f, "")
|
||||
write!(f, "{}", self.identifier)
|
||||
}
|
||||
}
|
41
ast/src/common/variables.rs
Normal file
41
ast/src/common/variables.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use crate::{ast::Rule, common::VariableName, types::Type, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::variables))]
|
||||
pub struct Variables<'ast> {
|
||||
pub names: Vec<VariableName<'ast>>,
|
||||
pub type_: Option<Type<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Variables<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.names.len() == 1 {
|
||||
// mut a
|
||||
write!(f, "{}", self.names[0])?;
|
||||
} else {
|
||||
// (a, mut b)
|
||||
let names = self
|
||||
.names
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
|
||||
write!(f, "({})", names)?;
|
||||
}
|
||||
|
||||
if self.type_.is_some() {
|
||||
write!(f, ": {}", self.type_.as_ref().unwrap())?;
|
||||
}
|
||||
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ pub enum Expression<'ast> {
|
||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||
CircuitInline(CircuitInlineExpression<'ast>),
|
||||
Postfix(PostfixExpression<'ast>),
|
||||
Tuple(TupleExpression<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> Expression<'ast> {
|
||||
@ -57,6 +58,7 @@ impl<'ast> Expression<'ast> {
|
||||
Expression::ArrayInitializer(expression) => &expression.span,
|
||||
Expression::CircuitInline(expression) => &expression.span,
|
||||
Expression::Postfix(expression) => &expression.span,
|
||||
Expression::Tuple(expression) => &expression.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,10 +87,9 @@ 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.identifier)
|
||||
}
|
||||
Expression::Postfix(ref expression) => write!(f, "Postfix display not impl {}", expression.identifier),
|
||||
Expression::CircuitInline(ref expression) => write!(f, "{}", expression.span.as_str()),
|
||||
Expression::Postfix(ref expression) => write!(f, "{}", expression.span.as_str()),
|
||||
Expression::Tuple(ref expression) => write!(f, "{}", expression.span.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,3 +21,6 @@ pub use postfix_expression::*;
|
||||
|
||||
pub mod ternary_expression;
|
||||
pub use ternary_expression::*;
|
||||
|
||||
pub mod tuple_expression;
|
||||
pub use tuple_expression::*;
|
||||
|
14
ast/src/expressions/tuple_expression.rs
Normal file
14
ast/src/expressions/tuple_expression.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use crate::{ast::Rule, expressions::Expression, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::expression_tuple))]
|
||||
pub struct TupleExpression<'ast> {
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
@ -9,7 +9,7 @@ use serde::Serialize;
|
||||
pub struct Function<'ast> {
|
||||
pub identifier: Identifier<'ast>,
|
||||
pub parameters: Vec<Input<'ast>>,
|
||||
pub returns: Vec<Type<'ast>>,
|
||||
pub returns: Option<Type<'ast>>,
|
||||
pub statements: Vec<Statement<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
|
@ -57,17 +57,16 @@ spread = { "..." ~ expression }
|
||||
// Declared in common/spread_or_expression.rs
|
||||
spread_or_expression = { spread | expression }
|
||||
|
||||
// Declared in common/return_.rs
|
||||
return_ = { return_tuple | expression }
|
||||
|
||||
// Declared in common/return_tuple.rs
|
||||
return_tuple = {"(" ~ expression ~ ("," ~ expression)+ ~ ")"}
|
||||
|
||||
// Declared in common/static_.rs
|
||||
static_ = { "static " }
|
||||
|
||||
// Declared in common/variable.rs
|
||||
variable = { mutable? ~ identifier ~ (":" ~ type_)? }
|
||||
// Declared in common/variable_name.rs
|
||||
|
||||
variable_name = {mutable? ~ identifier}
|
||||
variable_name_tuple = _{"(" ~ variable_name ~ ("," ~ variable_name)+ ~ ")"}
|
||||
|
||||
// Declared in common/variables.rs
|
||||
variables = { ( variable_name_tuple | variable_name ) ~ (":" ~ type_ )? }
|
||||
|
||||
// Declared in common/declare.rs
|
||||
declare = { let_ | const_ }
|
||||
@ -124,7 +123,7 @@ operation_pow_assign = { "**=" }
|
||||
/// Types
|
||||
|
||||
// Declared in types/type_.rs
|
||||
type_ = { type_self | type_array | type_data | type_circuit }
|
||||
type_ = { type_self | type_tuple | type_array | type_data | type_circuit }
|
||||
|
||||
// Declared in types/integer_type.rs
|
||||
type_integer = {
|
||||
@ -192,7 +191,7 @@ type_circuit = { identifier }
|
||||
// Declared in types/array_type.rs
|
||||
type_array = { type_data ~ ("[" ~ number_positive ~ "]")+ }
|
||||
|
||||
type_list = _{ (type_ ~ ("," ~ type_)*)? }
|
||||
type_tuple = { "(" ~ type_ ~ ("," ~ type_)+ ~ ")" }
|
||||
|
||||
/// Values
|
||||
|
||||
@ -244,16 +243,19 @@ value_address = ${ type_address ~ "(" ~ address ~ ")" }
|
||||
/// Access
|
||||
|
||||
// Declared in access/access.rs
|
||||
access = { access_array | access_call | access_member | access_static_member}
|
||||
access = { access_array | access_tuple | access_call | access_member | access_static_member}
|
||||
|
||||
// Declared in access/array_access.rs
|
||||
access_array = !{ "[" ~ range_or_expression ~ "]" }
|
||||
|
||||
// Declared in access/tuple_access.rs
|
||||
access_tuple = ${ "." ~ number_positive }
|
||||
|
||||
// Declared in access/assignee_access.rs
|
||||
access_assignee = { access_array | access_member }
|
||||
access_assignee = { access_array | access_tuple | access_member }
|
||||
|
||||
// Declared in access/call_access.rs
|
||||
access_call = !{ "(" ~ expression_tuple ~ ")" }
|
||||
access_call = !{ expression_tuple }
|
||||
|
||||
// Declared in access/member_access.rs
|
||||
access_member = ${ "." ~ identifier }
|
||||
@ -285,22 +287,25 @@ expression_conditional = { "if " ~ expression ~ "? " ~ expression ~ ": " ~ expre
|
||||
/// Expressions
|
||||
|
||||
expression_term = {
|
||||
("(" ~ expression ~ ")")
|
||||
value
|
||||
| ("(" ~ expression ~ ")")
|
||||
| expression_tuple
|
||||
| expression_conditional
|
||||
| expression_array_initializer
|
||||
| expression_array_inline
|
||||
| expression_circuit_inline
|
||||
| expression_conditional
|
||||
| value
|
||||
| expression_unary // must be defined below value to avoid conflicts with negative values
|
||||
| expression_unary
|
||||
| expression_postfix
|
||||
| identifier
|
||||
}
|
||||
|
||||
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
|
||||
|
||||
// Declared in expressions/expression.rs
|
||||
expression = { expression_term ~ (operation_binary ~ expression_term)* }
|
||||
|
||||
// Declared in expressions/expression_tuple.rs
|
||||
expression_tuple = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
|
||||
|
||||
// Declared in expressions/array_initializer_expression.rs
|
||||
expression_array_initializer = { "[" ~ spread_or_expression ~ ";" ~ number_positive ~ "]" }
|
||||
|
||||
@ -325,7 +330,6 @@ statement = {
|
||||
(statement_return
|
||||
| statement_conditional
|
||||
| statement_for
|
||||
| statement_multiple_assignment
|
||||
| statement_macro
|
||||
| statement_definition
|
||||
| statement_assign
|
||||
@ -347,7 +351,7 @@ statement_conditional = {"if " ~ (expression | "(" ~ expression ~ ")") ~ "{" ~ N
|
||||
conditional_nested_or_end_statement = { statement_conditional | "{" ~ NEWLINE* ~ statement+ ~ "}"}
|
||||
|
||||
// Declared in statements/definition_statement.rs
|
||||
statement_definition = { declare ~ variable ~ "=" ~ expression ~ LINE_END}
|
||||
statement_definition = { declare ~ variables ~ "=" ~ expression ~ LINE_END}
|
||||
|
||||
// Declared in statements/expression_statement.rs
|
||||
statement_expression = { expression ~ LINE_END }
|
||||
@ -355,12 +359,8 @@ statement_expression = { expression ~ LINE_END }
|
||||
// Declared in statements/for_statement.rs
|
||||
statement_for = { "for " ~ identifier ~ "in " ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
|
||||
|
||||
// Declared in statements/multiple_assignment_statement.rs
|
||||
statement_multiple_assignment = { declare ~ "(" ~ variable_tuple ~ ")" ~ "=" ~ identifier ~ "(" ~ expression_tuple ~ ")" ~ LINE_END}
|
||||
variable_tuple = _{ variable ~ ("," ~ variable)* }
|
||||
|
||||
// Declared in statements/return_statement.rs
|
||||
statement_return = { "return " ~ return_}
|
||||
statement_return = { "return " ~ expression}
|
||||
|
||||
/// Functions
|
||||
|
||||
@ -368,7 +368,7 @@ statement_return = { "return " ~ return_}
|
||||
test_function = { "test " ~ function }
|
||||
|
||||
// Declared in functions/function.rs
|
||||
function = { "function " ~ identifier ~ "(" ~ NEWLINE* ~ input_list ~ NEWLINE* ~ ")" ~ ("->" ~ (type_ | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
|
||||
function = { "function " ~ identifier ~ input_tuple ~ ("->" ~ type_)? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
|
||||
|
||||
// Declared in functions/input/function_input.rs
|
||||
function_input = { mutable? ~ identifier ~ ":" ~ type_ }
|
||||
@ -381,7 +381,7 @@ input = {
|
||||
function_input
|
||||
| input_keyword
|
||||
}
|
||||
input_list = _{ (input ~ ("," ~ NEWLINE* ~ input)*)? }
|
||||
input_tuple = _{ "(" ~(input ~ ("," ~ NEWLINE* ~ input)*)? ~ ")"}
|
||||
|
||||
|
||||
/// Imports
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ast::Rule,
|
||||
common::{Declare, LineEnd, Variable},
|
||||
common::{Declare, LineEnd, Variables},
|
||||
expressions::Expression,
|
||||
SpanDef,
|
||||
};
|
||||
@ -14,8 +14,8 @@ use std::fmt;
|
||||
#[pest_ast(rule(Rule::statement_definition))]
|
||||
pub struct DefinitionStatement<'ast> {
|
||||
pub declare: Declare,
|
||||
pub variable: Variable<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
pub variables: Variables<'ast>,
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
pub line_end: LineEnd,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
@ -24,6 +24,13 @@ pub struct DefinitionStatement<'ast> {
|
||||
|
||||
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "let {} = {};", self.variable, self.expression)
|
||||
let expressions = self
|
||||
.expressions
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
|
||||
write!(f, "let {} = {};", self.variables, expressions)
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,6 @@ pub use expression_statement::*;
|
||||
pub mod for_statement;
|
||||
pub use for_statement::*;
|
||||
|
||||
pub mod multiple_assignment_statement;
|
||||
pub use multiple_assignment_statement::*;
|
||||
|
||||
pub mod return_statement;
|
||||
pub use return_statement::*;
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
use crate::{
|
||||
ast::Rule,
|
||||
common::{Declare, Identifier, LineEnd, Variable},
|
||||
expressions::Expression,
|
||||
SpanDef,
|
||||
};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::statement_multiple_assignment))]
|
||||
pub struct MultipleAssignmentStatement<'ast> {
|
||||
pub declare: Declare,
|
||||
pub variables: Vec<Variable<'ast>>,
|
||||
pub function_name: Identifier<'ast>,
|
||||
pub arguments: Vec<Expression<'ast>>,
|
||||
pub line_end: LineEnd,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for MultipleAssignmentStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for (i, id) in self.variables.iter().enumerate() {
|
||||
write!(f, "{}", id)?;
|
||||
if i < self.variables.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, " = {}", self.function_name)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::{ast::Rule, common::Return, SpanDef};
|
||||
use crate::{ast::Rule, expressions::Expression, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
@ -8,7 +8,7 @@ use std::fmt;
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::statement_return))]
|
||||
pub struct ReturnStatement<'ast> {
|
||||
pub return_: Return<'ast>,
|
||||
pub expression: Expression<'ast>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
@ -16,6 +16,6 @@ pub struct ReturnStatement<'ast> {
|
||||
|
||||
impl<'ast> fmt::Display for ReturnStatement<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.return_)
|
||||
write!(f, "return {}", self.expression)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ pub enum Statement<'ast> {
|
||||
Return(ReturnStatement<'ast>),
|
||||
Definition(DefinitionStatement<'ast>),
|
||||
Assign(AssignStatement<'ast>),
|
||||
MultipleAssignment(MultipleAssignmentStatement<'ast>),
|
||||
Conditional(ConditionalStatement<'ast>),
|
||||
Iteration(ForStatement<'ast>),
|
||||
Assert(MacroStatement<'ast>),
|
||||
@ -23,7 +22,6 @@ impl<'ast> fmt::Display for Statement<'ast> {
|
||||
Statement::Return(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Definition(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Assign(ref statement) => write!(f, "{}", statement),
|
||||
Statement::MultipleAssignment(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Conditional(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Iteration(ref statement) => write!(f, "{}", statement),
|
||||
Statement::Assert(ref statement) => write!(f, "{}", statement),
|
||||
|
@ -28,6 +28,9 @@ pub use self_type::*;
|
||||
pub mod signed_integer_type;
|
||||
pub use signed_integer_type::*;
|
||||
|
||||
pub mod tuple_type;
|
||||
pub use tuple_type::*;
|
||||
|
||||
pub mod type_;
|
||||
pub use type_::*;
|
||||
|
||||
|
14
ast/src/types/tuple_type.rs
Normal file
14
ast/src/types/tuple_type.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use crate::{ast::Rule, types::Type, SpanDef};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
|
||||
#[pest_ast(rule(Rule::type_tuple))]
|
||||
pub struct TupleType<'ast> {
|
||||
pub types: Vec<Type<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
#[serde(with = "SpanDef")]
|
||||
pub span: Span<'ast>,
|
||||
}
|
@ -9,6 +9,7 @@ use std::fmt;
|
||||
pub enum Type<'ast> {
|
||||
Basic(DataType),
|
||||
Array(ArrayType<'ast>),
|
||||
Tuple(TupleType<'ast>),
|
||||
Circuit(CircuitType<'ast>),
|
||||
SelfType(SelfType),
|
||||
}
|
||||
@ -18,6 +19,7 @@ impl<'ast> fmt::Display for Type<'ast> {
|
||||
match *self {
|
||||
Type::Basic(ref _type) => write!(f, "basic"),
|
||||
Type::Array(ref _type) => write!(f, "array"),
|
||||
Type::Tuple(ref _type) => write!(f, "tuple"),
|
||||
Type::Circuit(ref _type) => write!(f, "struct"),
|
||||
Type::SelfType(ref _type) => write!(f, "Self"),
|
||||
}
|
||||
|
@ -11,47 +11,45 @@
|
||||
}
|
||||
},
|
||||
"parameters": [],
|
||||
"returns": [],
|
||||
"returns": null,
|
||||
"statements": [
|
||||
{
|
||||
"Return": {
|
||||
"return_": {
|
||||
"Single": {
|
||||
"Binary": {
|
||||
"operation": "Add",
|
||||
"left": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"Positive": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 29,
|
||||
"end": 30
|
||||
}
|
||||
"expression": {
|
||||
"Binary": {
|
||||
"operation": "Add",
|
||||
"left": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"Positive": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 29,
|
||||
"end": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"Positive": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 33,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "1 + 1",
|
||||
"start": 29,
|
||||
"end": 34
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"Value": {
|
||||
"Implicit": {
|
||||
"Positive": {
|
||||
"value": "1",
|
||||
"span": {
|
||||
"input": "1",
|
||||
"start": 33,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"span": {
|
||||
"input": "1 + 1",
|
||||
"start": 29,
|
||||
"end": 34
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
use leo_typed::Variable;
|
||||
use leo_typed::Identifier;
|
||||
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
|
||||
@ -13,15 +13,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn store_definition(
|
||||
&mut self,
|
||||
function_scope: String,
|
||||
variable: Variable,
|
||||
mutable: bool,
|
||||
identifier: Identifier,
|
||||
mut value: ConstrainedValue<F, G>,
|
||||
) -> () {
|
||||
// Store with given mutability
|
||||
if variable.mutable {
|
||||
if mutable {
|
||||
value = ConstrainedValue::Mutable(Box::new(value));
|
||||
}
|
||||
|
||||
let variable_program_identifier = new_scope(function_scope, variable.identifier.name);
|
||||
let variable_program_identifier = new_scope(function_scope, identifier.name);
|
||||
|
||||
self.store(variable_program_identifier, value);
|
||||
}
|
||||
|
@ -70,14 +70,14 @@ impl ExpressionError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn unexpected_array(expected: String, actual: String, span: Span) -> Self {
|
||||
let message = format!("expected type `{}`, found array with elements `{}`", expected, actual);
|
||||
pub fn incompatible_types(operation: String, span: Span) -> Self {
|
||||
let message = format!("no implementation for `{}`", operation);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn incompatible_types(operation: String, span: Span) -> Self {
|
||||
let message = format!("no implementation for `{}`", operation);
|
||||
pub fn index_out_of_bounds(index: usize, span: Span) -> Self {
|
||||
let message = format!("cannot access index {} of tuple out of bounds", index);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
@ -159,4 +159,16 @@ impl ExpressionError {
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn unexpected_array(expected: String, actual: String, span: Span) -> Self {
|
||||
let message = format!("expected type `{}`, found array with elements `{}`", expected, actual);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn unexpected_tuple(expected: String, actual: String, span: Span) -> Self {
|
||||
let message = format!("expected type `{}`, found tuple with values `{}`", expected, actual);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,12 @@ impl FunctionError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn invalid_tuple(actual: String, span: Span) -> Self {
|
||||
let message = format!("Expected function input tuple, found `{}`", actual);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn return_arguments_length(expected: usize, actual: usize, span: Span) -> Self {
|
||||
let message = format!("function expected {} returns, found {} returns", expected, actual);
|
||||
|
||||
|
@ -19,12 +19,6 @@ impl OutputBytesError {
|
||||
OutputBytesError::Error(FormattedError::new_from_span(message, span))
|
||||
}
|
||||
|
||||
pub fn illegal_return(value: String, span: Span) -> Self {
|
||||
let message = format!("program return must be a return value, found `{}`", value);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn not_enough_registers(span: Span) -> Self {
|
||||
let message = format!("number of input registers must be greater than or equal to output registers");
|
||||
|
||||
|
@ -113,6 +113,12 @@ impl StatementError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn multiple_definition(value: String, span: Span) -> Self {
|
||||
let message = format!("cannot assign multiple variables to a single value: {}", value,);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn select_fail(first: String, second: String, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"Conditional select gadget failed to select between `{}` or `{}`",
|
||||
@ -122,6 +128,18 @@ impl StatementError {
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn tuple_assign_index(span: Span) -> Self {
|
||||
let message = format!("Cannot assign single index to tuple of values");
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn tuple_type(type_: String, span: Span) -> Self {
|
||||
let message = format!("Expected tuple type, found type `{}`", type_);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn unassigned(name: String, span: Span) -> Self {
|
||||
let message = format!("Expected assignment of return values for expression `{}`", name);
|
||||
|
||||
|
@ -14,7 +14,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
array: Box<Expression>,
|
||||
index: RangeOrExpression,
|
||||
span: Span,
|
||||
@ -23,7 +23,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*array,
|
||||
span.clone(),
|
||||
)? {
|
||||
|
@ -20,23 +20,22 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
mut expected_type: Option<Type>,
|
||||
array: Vec<Box<SpreadOrExpression>>,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
// Check explicit array type dimension if given
|
||||
let mut expected_types = expected_types.clone();
|
||||
let expected_dimensions = vec![];
|
||||
|
||||
if !expected_types.is_empty() {
|
||||
match expected_types[0] {
|
||||
Type::Array(ref _type, ref dimensions) => {
|
||||
expected_types = vec![expected_types[0].inner_dimension(dimensions)];
|
||||
if expected_type.is_some() {
|
||||
match expected_type.unwrap() {
|
||||
Type::Array(ref type_, ref dimensions) => {
|
||||
expected_type = Some(type_.inner_dimension(dimensions).clone());
|
||||
}
|
||||
ref _type => {
|
||||
return Err(ExpressionError::unexpected_array(
|
||||
expected_types[0].to_string(),
|
||||
_type.to_string(),
|
||||
format!("{:?}", array),
|
||||
span,
|
||||
));
|
||||
}
|
||||
@ -66,7 +65,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
expected_type.clone(),
|
||||
expression,
|
||||
)?);
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
index: Expression,
|
||||
span: Span,
|
||||
) -> Result<usize, ExpressionError> {
|
||||
let expected_types = vec![Type::IntegerType(IntegerType::U32)];
|
||||
let expected_type = Some(Type::IntegerType(IntegerType::U32));
|
||||
match self.enforce_operand(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
expected_type,
|
||||
index,
|
||||
span.clone(),
|
||||
)? {
|
||||
|
@ -14,7 +14,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
left: Expression,
|
||||
right: Expression,
|
||||
span: Span,
|
||||
@ -23,7 +23,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type.clone(),
|
||||
left,
|
||||
span.clone(),
|
||||
)?;
|
||||
@ -31,12 +31,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type.clone(),
|
||||
right,
|
||||
span.clone(),
|
||||
)?;
|
||||
|
||||
resolved_left.resolve_types(&mut resolved_right, expected_types, span)?;
|
||||
resolved_left.resolve_types(&mut resolved_right, expected_type, span)?;
|
||||
|
||||
Ok((resolved_left, resolved_right))
|
||||
}
|
||||
|
@ -17,14 +17,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
expression: Expression,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
let mut branch = self.enforce_expression(cs, file_scope, function_scope, expected_types, expression)?;
|
||||
let mut branch = self.enforce_expression(cs, file_scope, function_scope, expected_type.clone(), expression)?;
|
||||
|
||||
branch.get_inner_mut();
|
||||
branch.resolve_type(expected_types, span)?;
|
||||
branch.resolve_type(expected_type, span)?;
|
||||
|
||||
Ok(branch)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
circuit_identifier: Box<Expression>,
|
||||
circuit_member: Identifier,
|
||||
span: Span,
|
||||
@ -33,7 +33,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
let self_function_scope = new_scope(self_file_scope.clone(), identifier.name.to_string());
|
||||
|
||||
let member_value =
|
||||
self.evaluate_identifier(self_file_scope, self_function_scope, &vec![], circuit_member.clone())?;
|
||||
self.evaluate_identifier(self_file_scope, self_function_scope, None, circuit_member.clone())?;
|
||||
|
||||
return Ok(member_value);
|
||||
}
|
||||
@ -43,7 +43,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*circuit_identifier.clone(),
|
||||
span.clone(),
|
||||
)? {
|
||||
|
@ -51,7 +51,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![_type.clone()],
|
||||
Some(_type.clone()),
|
||||
field.expression,
|
||||
)?;
|
||||
|
||||
|
@ -14,7 +14,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
circuit_identifier: Box<Expression>,
|
||||
circuit_member: Identifier,
|
||||
span: Span,
|
||||
@ -24,7 +24,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*circuit_identifier.clone(),
|
||||
)? {
|
||||
ConstrainedValue::CircuitDefinition(circuit_definition) => circuit_definition,
|
||||
|
@ -15,7 +15,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
conditional: Expression,
|
||||
first: Expression,
|
||||
second: Expression,
|
||||
@ -25,7 +25,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![Type::Boolean],
|
||||
Some(Type::Boolean),
|
||||
conditional,
|
||||
)? {
|
||||
ConstrainedValue::Boolean(resolved) => resolved,
|
||||
@ -36,7 +36,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type.clone(),
|
||||
first,
|
||||
span.clone(),
|
||||
)?;
|
||||
@ -45,7 +45,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
second,
|
||||
span.clone(),
|
||||
)?;
|
||||
|
@ -25,13 +25,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
expression: Expression,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
match expression {
|
||||
// Variables
|
||||
Expression::Identifier(unresolved_variable) => {
|
||||
self.evaluate_identifier(file_scope, function_scope, expected_types, unresolved_variable)
|
||||
self.evaluate_identifier(file_scope, function_scope, expected_type, unresolved_variable)
|
||||
}
|
||||
|
||||
// Values
|
||||
@ -39,7 +39,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
Expression::Boolean(boolean, span) => Ok(ConstrainedValue::Boolean(new_bool_constant(boolean, span)?)),
|
||||
Expression::Field(field, span) => Ok(ConstrainedValue::Field(FieldType::constant(field, span)?)),
|
||||
Expression::Group(group_affine, span) => Ok(ConstrainedValue::Group(G::constant(group_affine, span)?)),
|
||||
Expression::Implicit(value, span) => Ok(enforce_number_implicit(expected_types, value, span)?),
|
||||
Expression::Implicit(value, span) => Ok(enforce_number_implicit(expected_type, value, span)?),
|
||||
Expression::Integer(type_, integer, span) => {
|
||||
Ok(ConstrainedValue::Integer(Integer::new_constant(&type_, integer, span)?))
|
||||
}
|
||||
@ -47,7 +47,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
// Binary operations
|
||||
Expression::Negate(expression, span) => {
|
||||
let resolved_value =
|
||||
self.enforce_expression(cs, file_scope, function_scope, expected_types, *expression)?;
|
||||
self.enforce_expression(cs, file_scope, function_scope, expected_type, *expression)?;
|
||||
|
||||
enforce_negate(cs, resolved_value, span)
|
||||
}
|
||||
@ -56,7 +56,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -69,7 +69,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -82,7 +82,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -95,7 +95,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -108,7 +108,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -119,7 +119,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
|
||||
// Boolean operations
|
||||
Expression::Not(expression, span) => Ok(evaluate_not(
|
||||
self.enforce_expression(cs, file_scope, function_scope, expected_types, *expression)?,
|
||||
self.enforce_expression(cs, file_scope, function_scope, expected_type, *expression)?,
|
||||
span,
|
||||
)?),
|
||||
Expression::Or(left, right, span) => {
|
||||
@ -127,7 +127,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -140,7 +140,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -153,7 +153,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -166,7 +166,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -179,7 +179,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -192,7 +192,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -205,7 +205,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
*left,
|
||||
*right,
|
||||
span.clone(),
|
||||
@ -219,7 +219,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
expected_types,
|
||||
expected_type,
|
||||
*conditional,
|
||||
*first,
|
||||
*second,
|
||||
@ -228,10 +228,18 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
|
||||
// Arrays
|
||||
Expression::Array(array, span) => {
|
||||
self.enforce_array(cs, file_scope, function_scope, expected_types, array, span)
|
||||
self.enforce_array(cs, file_scope, function_scope, expected_type, array, span)
|
||||
}
|
||||
Expression::ArrayAccess(array, index, span) => {
|
||||
self.enforce_array_access(cs, file_scope, function_scope, expected_types, array, *index, span)
|
||||
self.enforce_array_access(cs, file_scope, function_scope, expected_type, array, *index, span)
|
||||
}
|
||||
|
||||
// Tuples
|
||||
Expression::Tuple(tuple, span) => {
|
||||
self.enforce_tuple(cs, file_scope, function_scope, expected_type, tuple, span)
|
||||
}
|
||||
Expression::TupleAccess(tuple, index, span) => {
|
||||
self.enforce_tuple_access(cs, file_scope, function_scope, expected_type, tuple, index, span)
|
||||
}
|
||||
|
||||
// Circuits
|
||||
@ -242,7 +250,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
expected_types,
|
||||
expected_type,
|
||||
circuit_variable,
|
||||
circuit_member,
|
||||
span,
|
||||
@ -252,7 +260,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
expected_types,
|
||||
expected_type,
|
||||
circuit_identifier,
|
||||
circuit_member,
|
||||
span,
|
||||
@ -263,7 +271,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
expected_types,
|
||||
expected_type,
|
||||
function,
|
||||
arguments,
|
||||
span,
|
||||
|
@ -14,7 +14,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
function: Box<Expression>,
|
||||
arguments: Vec<Expression>,
|
||||
span: Span,
|
||||
@ -23,7 +23,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_types,
|
||||
expected_type,
|
||||
*function.clone(),
|
||||
)?;
|
||||
|
||||
@ -36,22 +36,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
span.start,
|
||||
);
|
||||
|
||||
match self.enforce_function(
|
||||
self.enforce_function(
|
||||
&mut cs.ns(|| name_unique),
|
||||
outer_scope,
|
||||
function_scope,
|
||||
function_call,
|
||||
arguments,
|
||||
) {
|
||||
Ok(ConstrainedValue::Return(return_values)) => {
|
||||
if return_values.len() == 1 {
|
||||
Ok(return_values[0].clone())
|
||||
} else {
|
||||
Ok(ConstrainedValue::Return(return_values))
|
||||
}
|
||||
}
|
||||
Ok(_) => Err(ExpressionError::function_no_return(function.to_string(), span)),
|
||||
Err(error) => Err(ExpressionError::from(Box::new(error))),
|
||||
}
|
||||
)
|
||||
.map_err(|error| ExpressionError::from(Box::new(error)))
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
&mut self,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
unresolved_identifier: Identifier,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
// Evaluate the identifier name in the current function scope
|
||||
@ -33,7 +33,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
} else if let Some(value) = self.get(&unresolved_identifier.name) {
|
||||
// Check imported file scope
|
||||
value.clone()
|
||||
} else if expected_types.contains(&Type::Address) {
|
||||
} else if expected_type.is_some() && expected_type.unwrap() == Type::Address {
|
||||
// If we expect an address type, try to return an address
|
||||
let address = Address::new(unresolved_identifier.name, unresolved_identifier.span)?;
|
||||
|
||||
@ -42,7 +42,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
return Err(ExpressionError::undefined_identifier(unresolved_identifier));
|
||||
};
|
||||
|
||||
result_value.resolve_type(expected_types, unresolved_identifier.span.clone())?;
|
||||
result_value.resolve_type(expected_type, unresolved_identifier.span.clone())?;
|
||||
|
||||
Ok(result_value)
|
||||
}
|
||||
|
@ -29,3 +29,6 @@ pub use self::logical::*;
|
||||
|
||||
pub mod relational;
|
||||
pub use self::relational::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
||||
|
40
compiler/src/expression/tuple/access.rs
Normal file
40
compiler/src/expression/tuple/access.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//! Enforces array access in a compiled Leo program.
|
||||
|
||||
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn enforce_tuple_access<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_type: Option<Type>,
|
||||
tuple: Box<Expression>,
|
||||
index: usize,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
let tuple = match self.enforce_operand(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_type,
|
||||
*tuple,
|
||||
span.clone(),
|
||||
)? {
|
||||
ConstrainedValue::Tuple(tuple) => tuple,
|
||||
value => return Err(ExpressionError::undefined_array(value.to_string(), span.clone())),
|
||||
};
|
||||
|
||||
if index > tuple.len() - 1 {
|
||||
return Err(ExpressionError::index_out_of_bounds(index, span));
|
||||
}
|
||||
|
||||
Ok(tuple[index].to_owned())
|
||||
}
|
||||
}
|
7
compiler/src/expression/tuple/mod.rs
Normal file
7
compiler/src/expression/tuple/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Methods to enforce tuple expressions in a compiled Leo program.
|
||||
|
||||
pub mod access;
|
||||
pub use self::access::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
53
compiler/src/expression/tuple/tuple.rs
Normal file
53
compiler/src/expression/tuple/tuple.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! Enforces an tuple expression in a compiled Leo program.
|
||||
|
||||
use crate::{errors::ExpressionError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
/// Enforce tuple expressions
|
||||
pub fn enforce_tuple<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expected_type: Option<Type>,
|
||||
tuple: Vec<Expression>,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
|
||||
// Check explicit tuple type dimension if given
|
||||
let mut expected_types = vec![];
|
||||
|
||||
if expected_type.is_some() {
|
||||
match expected_type.unwrap() {
|
||||
Type::Tuple(ref types) => {
|
||||
expected_types = types.clone();
|
||||
}
|
||||
ref type_ => {
|
||||
return Err(ExpressionError::unexpected_tuple(
|
||||
type_.to_string(),
|
||||
format!("{:?}", tuple),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
for (i, expression) in tuple.into_iter().enumerate() {
|
||||
let type_ = if expected_types.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(expected_types[i].clone())
|
||||
};
|
||||
|
||||
result.push(self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), type_, expression)?);
|
||||
}
|
||||
|
||||
Ok(ConstrainedValue::Tuple(result))
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use crate::{
|
||||
GroupType,
|
||||
};
|
||||
|
||||
use leo_typed::{Expression, Function, InputVariable, Span};
|
||||
use leo_typed::{Expression, Function, InputVariable, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -46,7 +46,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
scope.clone(),
|
||||
caller_scope.clone(),
|
||||
function_name.clone(),
|
||||
vec![],
|
||||
None,
|
||||
input_expression,
|
||||
)?;
|
||||
|
||||
@ -59,7 +59,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
scope.clone(),
|
||||
caller_scope.clone(),
|
||||
function_name.clone(),
|
||||
vec![input_model.type_.clone()],
|
||||
Some(input_model.type_.clone()),
|
||||
input_expression,
|
||||
)?;
|
||||
|
||||
@ -93,14 +93,20 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
|
||||
// Conditionally select a result based on returned indicators
|
||||
let mut return_values = ConstrainedValue::Return(vec![]);
|
||||
let mut return_values = ConstrainedValue::Tuple(vec![]);
|
||||
|
||||
Self::conditionally_select_result(cs, &mut return_values, results, function.span.clone())?;
|
||||
|
||||
if let ConstrainedValue::Return(ref returns) = return_values {
|
||||
if function.returns.len() != returns.len() {
|
||||
if let ConstrainedValue::Tuple(ref returns) = return_values {
|
||||
let return_types = match function.returns {
|
||||
Some(Type::Tuple(types)) => types.len(),
|
||||
Some(_) => 1usize,
|
||||
None => 0usize,
|
||||
};
|
||||
|
||||
if return_types != returns.len() {
|
||||
return Err(FunctionError::return_arguments_length(
|
||||
function.returns.len(),
|
||||
return_types,
|
||||
returns.len(),
|
||||
function.span.clone(),
|
||||
));
|
||||
|
@ -16,16 +16,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
scope: String,
|
||||
caller_scope: String,
|
||||
function_name: String,
|
||||
expected_types: Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
input: Expression,
|
||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||
// Evaluate the function input value as pass by value from the caller or
|
||||
// evaluate as an expression in the current function scope
|
||||
match input {
|
||||
Expression::Identifier(identifier) => {
|
||||
Ok(self.evaluate_identifier(caller_scope, function_name, &expected_types, identifier)?)
|
||||
Ok(self.evaluate_identifier(caller_scope, function_name, expected_type, identifier)?)
|
||||
}
|
||||
expression => Ok(self.enforce_expression(cs, scope, function_name, &expected_types, expression)?),
|
||||
expression => Ok(self.enforce_expression(cs, scope, function_name, expected_type, expression)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
input_option,
|
||||
span,
|
||||
)?)),
|
||||
Type::Array(_type, dimensions) => self.allocate_array(cs, name, *_type, dimensions, input_option, span),
|
||||
Type::Array(type_, dimensions) => self.allocate_array(cs, name, *type_, dimensions, input_option, span),
|
||||
Type::Tuple(types) => self.allocate_tuple(cs, name, types, input_option, span),
|
||||
_ => unimplemented!("main function input not implemented for type"),
|
||||
}
|
||||
}
|
||||
|
@ -14,3 +14,6 @@ pub use self::input_keyword::*;
|
||||
|
||||
pub mod input_section;
|
||||
pub use self::input_section::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
||||
|
56
compiler/src/function/input/tuple.rs
Normal file
56
compiler/src/function/input/tuple.rs
Normal file
@ -0,0 +1,56 @@
|
||||
//! Allocates an array as a main function input parameter in a compiled Leo program.
|
||||
|
||||
use crate::{
|
||||
errors::FunctionError,
|
||||
program::{new_scope, ConstrainedProgram},
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
|
||||
use leo_typed::{InputValue, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn allocate_tuple<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
name: String,
|
||||
types: Vec<Type>,
|
||||
input_value: Option<InputValue>,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, FunctionError> {
|
||||
let mut tuple_values = vec![];
|
||||
|
||||
match input_value {
|
||||
Some(InputValue::Tuple(values)) => {
|
||||
// Allocate each value in the tuple
|
||||
for (i, (value, type_)) in values.into_iter().zip(types.into_iter()).enumerate() {
|
||||
let value_name = new_scope(name.clone(), i.to_string());
|
||||
|
||||
tuple_values.push(self.allocate_main_function_input(
|
||||
cs,
|
||||
type_,
|
||||
value_name,
|
||||
Some(value),
|
||||
span.clone(),
|
||||
)?)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Allocate all tuple values as none
|
||||
for (i, type_) in types.into_iter().enumerate() {
|
||||
let value_name = new_scope(name.clone(), i.to_string());
|
||||
|
||||
tuple_values.push(self.allocate_main_function_input(cs, type_, value_name, None, span.clone())?);
|
||||
}
|
||||
}
|
||||
_ => return Err(FunctionError::invalid_tuple(input_value.unwrap().to_string(), span)),
|
||||
}
|
||||
|
||||
Ok(ConstrainedValue::Tuple(tuple_values))
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![],
|
||||
None,
|
||||
parameter.expression,
|
||||
)?;
|
||||
|
||||
|
@ -20,8 +20,8 @@ impl OutputBytes {
|
||||
span: Span,
|
||||
) -> Result<Self, OutputBytesError> {
|
||||
let return_values = match value {
|
||||
ConstrainedValue::Return(values) => values,
|
||||
value => return Err(OutputBytesError::illegal_return(value.to_string(), span)),
|
||||
ConstrainedValue::Tuple(values) => values,
|
||||
value => vec![value],
|
||||
};
|
||||
let register_hashmap = registers.values();
|
||||
|
||||
|
@ -33,7 +33,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
// Modify the single value of the array in place
|
||||
match self.get_mutable_assignee(name, span.clone())? {
|
||||
ConstrainedValue::Array(old) => {
|
||||
new_value.resolve_type(&vec![old[index].to_type(span.clone())?], span.clone())?;
|
||||
new_value.resolve_type(Some(old[index].to_type(span.clone())?), span.clone())?;
|
||||
|
||||
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
|
||||
let selected_value = ConstrainedValue::conditionally_select(
|
||||
|
@ -33,7 +33,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
|
||||
// Evaluate new value
|
||||
let mut new_value =
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), &vec![], expression)?;
|
||||
self.enforce_expression(cs, file_scope.clone(), function_scope.clone(), None, expression)?;
|
||||
|
||||
// Mutate the old value into the new value
|
||||
match assignee {
|
||||
@ -41,7 +41,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
||||
let old_value = self.get_mutable_assignee(variable_name.clone(), span.clone())?;
|
||||
|
||||
new_value.resolve_type(&vec![old_value.to_type(span.clone())?], span.clone())?;
|
||||
new_value.resolve_type(Some(old_value.to_type(span.clone())?), span.clone())?;
|
||||
|
||||
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
|
||||
let selected_value =
|
||||
@ -62,6 +62,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
new_value,
|
||||
span,
|
||||
),
|
||||
Assignee::Tuple(_tuple, index) => self.assign_tuple(cs, indicator, variable_name, index, new_value, span),
|
||||
Assignee::CircuitField(_assignee, object_name) => {
|
||||
self.mutute_circuit_field(cs, indicator, variable_name, object_name, new_value, span)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ pub fn resolve_assignee(scope: String, assignee: Assignee) -> String {
|
||||
match assignee {
|
||||
Assignee::Identifier(name) => new_scope(scope, name.to_string()),
|
||||
Assignee::Array(array, _index) => resolve_assignee(scope, *array),
|
||||
Assignee::Tuple(tuple, _index) => resolve_assignee(scope, *tuple),
|
||||
Assignee::CircuitField(circuit_name, _member) => resolve_assignee(scope, *circuit_name),
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
return Err(StatementError::immutable_circuit_function("static".into(), span));
|
||||
}
|
||||
_ => {
|
||||
new_value.resolve_type(&vec![object.1.to_type(span.clone())?], span.clone())?;
|
||||
new_value.resolve_type(Some(object.1.to_type(span.clone())?), span.clone())?;
|
||||
|
||||
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
|
||||
let selected_value = ConstrainedValue::conditionally_select(
|
||||
|
@ -11,3 +11,6 @@ pub use self::assignee::*;
|
||||
|
||||
pub mod circuit_field;
|
||||
pub use self::circuit_field::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use self::tuple::*;
|
||||
|
45
compiler/src/statement/assign/tuple.rs
Normal file
45
compiler/src/statement/assign/tuple.rs
Normal file
@ -0,0 +1,45 @@
|
||||
//! Enforces a tuple assignment statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::Span;
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::{
|
||||
r1cs::ConstraintSystem,
|
||||
utilities::{boolean::Boolean, select::CondSelectGadget},
|
||||
},
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn assign_tuple<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
indicator: Option<Boolean>,
|
||||
name: String,
|
||||
index: usize,
|
||||
mut new_value: ConstrainedValue<F, G>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let condition = indicator.unwrap_or(Boolean::Constant(true));
|
||||
|
||||
// Modify the single value of the tuple in place
|
||||
match self.get_mutable_assignee(name, span.clone())? {
|
||||
ConstrainedValue::Tuple(old) => {
|
||||
new_value.resolve_type(Some(old[index].to_type(span.clone())?), span.clone())?;
|
||||
|
||||
let name_unique = format!("select {} {}:{}", new_value, span.line, span.start);
|
||||
let selected_value =
|
||||
ConstrainedValue::conditionally_select(cs.ns(|| name_unique), &condition, &new_value, &old[index])
|
||||
.map_err(|_| {
|
||||
StatementError::select_fail(new_value.to_string(), old[index].to_string(), span)
|
||||
})?;
|
||||
|
||||
old[index] = selected_value;
|
||||
}
|
||||
_ => return Err(StatementError::tuple_assign_index(span)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope: String,
|
||||
indicator: Option<Boolean>,
|
||||
statements: Vec<Statement>,
|
||||
return_types: Vec<Type>,
|
||||
return_type: Option<Type>,
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let mut results = vec![];
|
||||
// Evaluate statements. Only allow a single return argument to be returned.
|
||||
@ -27,7 +27,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope.clone(),
|
||||
indicator.clone(),
|
||||
statement.clone(),
|
||||
return_types.clone(),
|
||||
return_type.clone(),
|
||||
)?;
|
||||
|
||||
results.append(&mut value);
|
||||
|
@ -27,7 +27,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope: String,
|
||||
indicator: Option<Boolean>,
|
||||
statement: ConditionalStatement,
|
||||
return_types: Vec<Type>,
|
||||
return_type: Option<Type>,
|
||||
span: Span,
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let statement_string = statement.to_string();
|
||||
@ -40,7 +40,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&vec![Type::Boolean],
|
||||
Some(Type::Boolean),
|
||||
statement.condition.clone(),
|
||||
)? {
|
||||
ConstrainedValue::Boolean(resolved) => resolved,
|
||||
@ -70,7 +70,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope.clone(),
|
||||
Some(branch_1_indicator),
|
||||
statement.statements,
|
||||
return_types.clone(),
|
||||
return_type.clone(),
|
||||
)?;
|
||||
|
||||
results.append(&mut branch_1_result);
|
||||
@ -98,7 +98,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope,
|
||||
Some(branch_2_indicator),
|
||||
*nested,
|
||||
return_types,
|
||||
return_type,
|
||||
span,
|
||||
)?,
|
||||
ConditionalNestedOrEndStatement::End(statements) => self.evaluate_branch(
|
||||
@ -107,7 +107,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope,
|
||||
Some(branch_2_indicator),
|
||||
statements,
|
||||
return_types,
|
||||
return_type,
|
||||
)?,
|
||||
},
|
||||
None => vec![],
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Enforces a definition statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, GroupType};
|
||||
use leo_typed::{Declare, Expression, Span, Variable};
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, ConstrainedValue, GroupType};
|
||||
use leo_typed::{Declare, Expression, Span, Type, VariableName, Variables};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
@ -9,39 +9,186 @@ use snarkos_models::{
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
fn enforce_single_definition<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
function_scope: String,
|
||||
is_constant: bool,
|
||||
variable_name: VariableName,
|
||||
mut value: ConstrainedValue<F, G>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
if is_constant && variable_name.mutable {
|
||||
return Err(StatementError::immutable_assign(variable_name.to_string(), span));
|
||||
} else {
|
||||
value.allocate_value(cs, span)?
|
||||
}
|
||||
|
||||
self.store_definition(function_scope, variable_name.mutable, variable_name.identifier, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enforce_expressions<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
type_: Option<Type>,
|
||||
expressions: Vec<Expression>,
|
||||
span: Span,
|
||||
) -> Result<Vec<ConstrainedValue<F, G>>, StatementError> {
|
||||
let types = match type_ {
|
||||
Some(Type::Tuple(types)) => types,
|
||||
Some(type_) => return Err(StatementError::tuple_type(type_.to_string(), span.clone())),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
let implicit_types = types.is_empty();
|
||||
let mut expected_types = vec![];
|
||||
|
||||
for i in 0..expressions.len() {
|
||||
let expected_type = if implicit_types { None } else { Some(types[i].clone()) };
|
||||
|
||||
expected_types.push(expected_type);
|
||||
}
|
||||
|
||||
let mut values = vec![];
|
||||
|
||||
for (expression, expected_type) in expressions.into_iter().zip(expected_types.into_iter()) {
|
||||
let value = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
expected_type,
|
||||
expression,
|
||||
)?;
|
||||
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
|
||||
fn enforce_tuple_definition<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
is_constant: bool,
|
||||
variables: Variables,
|
||||
expressions: Vec<Expression>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let values = self.enforce_expressions(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope.clone(),
|
||||
variables.type_.clone(),
|
||||
expressions,
|
||||
span.clone(),
|
||||
)?;
|
||||
|
||||
let tuple = ConstrainedValue::Tuple(values);
|
||||
let variable = variables.names[0].clone();
|
||||
|
||||
self.enforce_single_definition(cs, function_scope, is_constant, variable, tuple, span)
|
||||
}
|
||||
|
||||
fn enforce_multiple_definition<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
function_scope: String,
|
||||
is_constant: bool,
|
||||
variables: Variables,
|
||||
values: Vec<ConstrainedValue<F, G>>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
if values.len() != variables.names.len() {
|
||||
return Err(StatementError::invalid_number_of_definitions(
|
||||
values.len(),
|
||||
variables.names.len(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
||||
for (variable, value) in variables.names.into_iter().zip(values.into_iter()) {
|
||||
self.enforce_single_definition(cs, function_scope.clone(), is_constant, variable, value, span.clone())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enforce_definition_statement<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
declare: Declare,
|
||||
variable: Variable,
|
||||
expression: Expression,
|
||||
variables: Variables,
|
||||
expressions: Vec<Expression>,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let mut expected_types = vec![];
|
||||
if let Some(ref _type) = variable._type {
|
||||
expected_types.push(_type.clone());
|
||||
let num_variables = variables.names.len();
|
||||
let num_values = expressions.len();
|
||||
let is_constant = match declare {
|
||||
Declare::Let => false,
|
||||
Declare::Const => true,
|
||||
};
|
||||
|
||||
if num_variables == 1 && num_values == 1 {
|
||||
// Define a single variable with a single value
|
||||
|
||||
let variable = variables.names[0].clone();
|
||||
let expression = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
variables.type_,
|
||||
expressions[0].clone(),
|
||||
)?;
|
||||
|
||||
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, span)
|
||||
} else if num_variables == 1 && num_values > 1 {
|
||||
// Define a tuple (single variable with multiple values)
|
||||
|
||||
self.enforce_tuple_definition(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
is_constant,
|
||||
variables,
|
||||
expressions,
|
||||
span,
|
||||
)
|
||||
} else if num_variables > 1 && num_values == 1 {
|
||||
// Define multiple variables for an expression that returns multiple results (multiple definition)
|
||||
|
||||
let values = match self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
variables.type_.clone(),
|
||||
expressions[0].clone(),
|
||||
)? {
|
||||
// ConstrainedValue::Return(values) => values,
|
||||
ConstrainedValue::Tuple(values) => values,
|
||||
value => return Err(StatementError::multiple_definition(value.to_string(), span.clone())),
|
||||
};
|
||||
|
||||
self.enforce_multiple_definition(cs, function_scope, is_constant, variables, values, span)
|
||||
} else {
|
||||
// Define multiple variables for multiple expressions
|
||||
let values = self.enforce_expressions(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope.clone(),
|
||||
variables.type_.clone(),
|
||||
expressions,
|
||||
span.clone(),
|
||||
)?;
|
||||
|
||||
self.enforce_multiple_definition(cs, function_scope, is_constant, variables, values, span)
|
||||
}
|
||||
let mut value = self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
expression,
|
||||
)?;
|
||||
|
||||
match declare {
|
||||
Declare::Let => value.allocate_value(cs, span)?,
|
||||
Declare::Const => {
|
||||
if variable.mutable {
|
||||
return Err(StatementError::immutable_assign(variable.to_string(), span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.store_definition(function_scope, variable, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,3 @@
|
||||
|
||||
pub mod definition;
|
||||
pub use self::definition::*;
|
||||
|
||||
pub mod multiple;
|
||||
pub use self::multiple::*;
|
||||
|
@ -1,54 +0,0 @@
|
||||
//! Enforces a multiple definition statement in a compiled Leo program.
|
||||
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Variable};
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
pub fn enforce_multiple_definition_statement<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
variables: Vec<Variable>,
|
||||
function: Expression,
|
||||
span: Span,
|
||||
) -> Result<(), StatementError> {
|
||||
let mut expected_types = vec![];
|
||||
for variable in variables.iter() {
|
||||
if let Some(ref _type) = variable._type {
|
||||
expected_types.push(_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Expect return values from function
|
||||
let return_values = match self.enforce_expression(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
function,
|
||||
)? {
|
||||
ConstrainedValue::Return(values) => values,
|
||||
value => unimplemented!("multiple assignment only implemented for functions, got {}", value),
|
||||
};
|
||||
|
||||
if variables.len() != return_values.len() {
|
||||
return Err(StatementError::invalid_number_of_definitions(
|
||||
variables.len(),
|
||||
return_values.len(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
||||
for (variable, value) in variables.into_iter().zip(return_values.into_iter()) {
|
||||
self.store_definition(function_scope.clone(), variable, value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
start: Expression,
|
||||
stop: Expression,
|
||||
statements: Vec<Statement>,
|
||||
return_types: Vec<Type>,
|
||||
return_type: Option<Type>,
|
||||
span: Span,
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let mut results = vec![];
|
||||
@ -40,6 +40,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
for i in from..to {
|
||||
// Store index in current function scope.
|
||||
// For loop scope is not implemented.
|
||||
|
||||
let index_name = new_scope(function_scope.clone(), index.to_string());
|
||||
|
||||
self.store(
|
||||
@ -55,7 +56,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope.clone(),
|
||||
indicator,
|
||||
statements.clone(),
|
||||
return_types.clone(),
|
||||
return_type.clone(),
|
||||
)?;
|
||||
|
||||
results.append(&mut result);
|
||||
|
@ -1,11 +1,6 @@
|
||||
//! Enforces a return statement in a compiled Leo program.
|
||||
|
||||
use crate::{
|
||||
errors::{StatementError, ValueError},
|
||||
program::ConstrainedProgram,
|
||||
value::ConstrainedValue,
|
||||
GroupType,
|
||||
};
|
||||
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
|
||||
use leo_typed::{Expression, Span, Type};
|
||||
|
||||
use snarkos_models::{
|
||||
@ -13,25 +8,20 @@ use snarkos_models::{
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
};
|
||||
|
||||
fn check_return_types(expected: &Vec<Type>, actual: &Vec<Type>, span: Span) -> Result<(), StatementError> {
|
||||
expected
|
||||
.iter()
|
||||
.zip(actual.iter())
|
||||
.map(|(type_1, type_2)| {
|
||||
if type_1.ne(type_2) {
|
||||
// catch return Self type
|
||||
if type_1.is_self() && type_2.is_circuit() {
|
||||
Ok(())
|
||||
fn check_return_type(expected: Option<Type>, actual: Type, span: Span) -> Result<(), StatementError> {
|
||||
match expected {
|
||||
Some(expected) => {
|
||||
if expected.ne(&actual) {
|
||||
if expected.is_self() && actual.is_circuit() {
|
||||
return Ok(());
|
||||
} else {
|
||||
Err(StatementError::arguments_type(type_1, type_2, span.clone()))
|
||||
return Err(StatementError::arguments_type(&expected, &actual, span));
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<()>, StatementError>>()?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
@ -40,41 +30,23 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
cs: &mut CS,
|
||||
file_scope: String,
|
||||
function_scope: String,
|
||||
expressions: Vec<Expression>,
|
||||
return_types: Vec<Type>,
|
||||
expression: Expression,
|
||||
return_type: Option<Type>,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, StatementError> {
|
||||
// Make sure we return the correct number of values
|
||||
if return_types.len() != expressions.len() {
|
||||
return Err(StatementError::invalid_number_of_returns(
|
||||
return_types.len(),
|
||||
expressions.len(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
||||
let mut returns = vec![];
|
||||
for (expression, ty) in expressions.into_iter().zip(return_types.clone().into_iter()) {
|
||||
let expected_types = vec![ty.clone()];
|
||||
let result = self.enforce_operand(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
&expected_types,
|
||||
expression,
|
||||
span.clone(),
|
||||
)?;
|
||||
let result = self.enforce_operand(
|
||||
cs,
|
||||
file_scope.clone(),
|
||||
function_scope.clone(),
|
||||
return_type.clone(),
|
||||
expression,
|
||||
span.clone(),
|
||||
)?;
|
||||
|
||||
returns.push(result);
|
||||
}
|
||||
check_return_type(return_type, result.to_type(span.clone())?, span)?;
|
||||
|
||||
let actual_types = returns
|
||||
.iter()
|
||||
.map(|value| value.to_type(span.clone()))
|
||||
.collect::<Result<Vec<Type>, ValueError>>()?;
|
||||
|
||||
check_return_types(&return_types, &actual_types, span)?;
|
||||
|
||||
Ok(ConstrainedValue::Return(returns))
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
/// Enforce a program statement.
|
||||
/// Returns a Vector of (indicator, value) tuples.
|
||||
/// Each evaluated statement may execute of one or more statements that may return early.
|
||||
/// To indicate which of these return values to take,
|
||||
/// we conditionally select the value according the `indicator` bit that evaluates to true.
|
||||
/// To indicate which of these return values to take we conditionally select the value according
|
||||
/// to the `indicator` bit that evaluates to true.
|
||||
pub fn enforce_statement<CS: ConstraintSystem<F>>(
|
||||
&mut self,
|
||||
cs: &mut CS,
|
||||
@ -21,24 +21,29 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope: String,
|
||||
indicator: Option<Boolean>,
|
||||
statement: Statement,
|
||||
return_types: Vec<Type>,
|
||||
return_type: Option<Type>,
|
||||
) -> Result<Vec<(Option<Boolean>, ConstrainedValue<F, G>)>, StatementError> {
|
||||
let mut results = vec![];
|
||||
|
||||
match statement {
|
||||
Statement::Return(expressions, span) => {
|
||||
Statement::Return(expression, span) => {
|
||||
let return_value = (
|
||||
indicator,
|
||||
self.enforce_return_statement(cs, file_scope, function_scope, expressions, return_types, span)?,
|
||||
self.enforce_return_statement(cs, file_scope, function_scope, expression, return_type, span)?,
|
||||
);
|
||||
|
||||
results.push(return_value);
|
||||
}
|
||||
Statement::Definition(declare, variable, expression, span) => {
|
||||
self.enforce_definition_statement(cs, file_scope, function_scope, declare, variable, expression, span)?;
|
||||
}
|
||||
Statement::MultipleDefinition(variables, function, span) => {
|
||||
self.enforce_multiple_definition_statement(cs, file_scope, function_scope, variables, function, span)?;
|
||||
Statement::Definition(declare, variables, expressions, span) => {
|
||||
self.enforce_definition_statement(
|
||||
cs,
|
||||
file_scope,
|
||||
function_scope,
|
||||
declare,
|
||||
variables,
|
||||
expressions,
|
||||
span,
|
||||
)?;
|
||||
}
|
||||
Statement::Assign(variable, expression, span) => {
|
||||
self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression, span)?;
|
||||
@ -50,7 +55,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
function_scope,
|
||||
indicator,
|
||||
statement,
|
||||
return_types,
|
||||
return_type,
|
||||
span,
|
||||
)?;
|
||||
|
||||
@ -66,7 +71,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
start,
|
||||
stop,
|
||||
statements,
|
||||
return_types,
|
||||
return_type,
|
||||
span,
|
||||
)?;
|
||||
|
||||
@ -74,7 +79,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
Statement::AssertEq(left, right, span) => {
|
||||
let (resolved_left, resolved_right) =
|
||||
self.enforce_binary_expression(cs, file_scope, function_scope, &vec![], left, right, span.clone())?;
|
||||
self.enforce_binary_expression(cs, file_scope, function_scope, None, left, right, span.clone())?;
|
||||
|
||||
self.enforce_assert_eq_statement(cs, indicator, &resolved_left, &resolved_right, span)?;
|
||||
}
|
||||
@ -83,11 +88,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
Statement::Expression(expression, span) => {
|
||||
let expression_string = expression.to_string();
|
||||
let value = self.enforce_expression(cs, file_scope, function_scope, &vec![], expression)?;
|
||||
let value = self.enforce_expression(cs, file_scope, function_scope, None, expression)?;
|
||||
|
||||
// handle empty return value cases
|
||||
match &value {
|
||||
ConstrainedValue::Return(values) => {
|
||||
ConstrainedValue::Tuple(values) => {
|
||||
if !values.is_empty() {
|
||||
return Err(StatementError::unassigned(expression_string, span));
|
||||
}
|
||||
|
@ -209,17 +209,10 @@ impl<F: Field + PrimeField> PartialOrd for FieldType<F> {
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField> EvaluateEqGadget<F> for FieldType<F> {
|
||||
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, _cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||
match (self, other) {
|
||||
(FieldType::Constant(first), FieldType::Constant(second)) => Ok(Boolean::constant(first.eq(second))),
|
||||
(FieldType::Allocated(allocated), FieldType::Constant(constant))
|
||||
| (FieldType::Constant(constant), FieldType::Allocated(allocated)) => {
|
||||
let bool_option = allocated.value.map(|f| f.eq(constant));
|
||||
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
|
||||
bool_option.ok_or(SynthesisError::AssignmentMissing)
|
||||
})
|
||||
}
|
||||
(FieldType::Allocated(first), FieldType::Allocated(second)) => first.evaluate_equal(cs, second),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -237,35 +237,12 @@ impl PartialEq for EdwardsGroupType {
|
||||
impl Eq for EdwardsGroupType {}
|
||||
|
||||
impl EvaluateEqGadget<Fq> for EdwardsGroupType {
|
||||
fn evaluate_equal<CS: ConstraintSystem<Fq>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||
fn evaluate_equal<CS: ConstraintSystem<Fq>>(&self, _cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||
match (self, other) {
|
||||
(EdwardsGroupType::Constant(self_value), EdwardsGroupType::Constant(other_value)) => {
|
||||
Ok(Boolean::Constant(self_value == other_value))
|
||||
}
|
||||
|
||||
(EdwardsGroupType::Allocated(self_value), EdwardsGroupType::Allocated(other_value)) => {
|
||||
let bool_option =
|
||||
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(self_value)
|
||||
.and_then(|a| {
|
||||
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(
|
||||
other_value,
|
||||
)
|
||||
.map(|b| a.eq(&b))
|
||||
});
|
||||
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
|
||||
bool_option.ok_or(SynthesisError::AssignmentMissing)
|
||||
})
|
||||
}
|
||||
|
||||
(EdwardsGroupType::Constant(constant_value), EdwardsGroupType::Allocated(allocated_value))
|
||||
| (EdwardsGroupType::Allocated(allocated_value), EdwardsGroupType::Constant(constant_value)) => {
|
||||
let bool_option =
|
||||
<EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::get_value(allocated_value)
|
||||
.map(|a| a.eq(constant_value));
|
||||
Boolean::alloc(&mut cs.ns(|| "evaluate_equal"), || {
|
||||
bool_option.ok_or(SynthesisError::AssignmentMissing)
|
||||
})
|
||||
Ok(Boolean::constant(self_value.eq(other_value)))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,12 @@ use leo_typed::{Span, Type};
|
||||
use snarkos_models::curves::{Field, PrimeField};
|
||||
|
||||
pub fn enforce_number_implicit<F: Field + PrimeField, G: GroupType<F>>(
|
||||
expected_types: &Vec<Type>,
|
||||
expected_type: Option<Type>,
|
||||
value: String,
|
||||
span: Span,
|
||||
) -> Result<ConstrainedValue<F, G>, ValueError> {
|
||||
if expected_types.len() == 1 {
|
||||
return Ok(ConstrainedValue::from_type(value, &expected_types[0], span)?);
|
||||
match expected_type {
|
||||
Some(type_) => Ok(ConstrainedValue::from_type(value, &type_, span)?),
|
||||
None => Ok(ConstrainedValue::Unresolved(value)),
|
||||
}
|
||||
|
||||
Ok(ConstrainedValue::Unresolved(value))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Conversion of integer declarations to constraints in Leo.
|
||||
use crate::{errors::IntegerError, integer::macros::IntegerTrait};
|
||||
use crate::{errors::IntegerError, IntegerTrait};
|
||||
use leo_gadgets::{
|
||||
arithmetic::*,
|
||||
bits::comparator::{ComparatorGadget, EvaluateLtGadget},
|
||||
@ -17,7 +17,7 @@ use snarkos_models::{
|
||||
boolean::Boolean,
|
||||
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget},
|
||||
select::CondSelectGadget,
|
||||
uint::{UInt, UInt128, UInt16, UInt32, UInt64, UInt8},
|
||||
uint::*,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -489,7 +489,7 @@ impl<F: Field + PrimeField> ConditionalEqGadget<F> for Integer {
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
<UInt128 as ConditionalEqGadget<F>>::cost() // upper bound. change trait to increase accuracy
|
||||
unimplemented!() // cannot determine which integer we are enforcing
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,6 +517,6 @@ impl<F: Field + PrimeField> CondSelectGadget<F> for Integer {
|
||||
}
|
||||
|
||||
fn cost() -> usize {
|
||||
<UInt128 as CondSelectGadget<F>>::cost() // upper bound. change trait to increase accuracy
|
||||
unimplemented!() // cannot determine which integer we are enforcing
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +37,15 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: GroupType<F>> {
|
||||
// Arrays
|
||||
Array(Vec<ConstrainedValue<F, G>>),
|
||||
|
||||
// Tuples
|
||||
Tuple(Vec<ConstrainedValue<F, G>>),
|
||||
|
||||
// Circuits
|
||||
CircuitDefinition(Circuit),
|
||||
CircuitExpression(Identifier, Vec<ConstrainedCircuitMember<F, G>>),
|
||||
|
||||
// Functions
|
||||
Function(Option<Identifier>, Function), // (optional circuit identifier, function definition)
|
||||
Return(Vec<ConstrainedValue<F, G>>),
|
||||
|
||||
// Modifiers
|
||||
Mutable(Box<ConstrainedValue<F, G>>),
|
||||
@ -61,8 +63,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
ConstrainedValue::from_type(value, &other_type, span)
|
||||
}
|
||||
|
||||
pub(crate) fn from_type(value: String, _type: &Type, span: Span) -> Result<Self, ValueError> {
|
||||
match _type {
|
||||
pub(crate) fn from_type(value: String, type_: &Type, span: Span) -> Result<Self, ValueError> {
|
||||
match type_ {
|
||||
// Data types
|
||||
Type::Address => Ok(ConstrainedValue::Address(Address::new(value, span)?)),
|
||||
Type::Boolean => Ok(ConstrainedValue::Boolean(new_bool_constant(value, span)?)),
|
||||
@ -103,15 +105,25 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
|
||||
Type::Array(Box::new(array_type), vec![count])
|
||||
}
|
||||
ConstrainedValue::Tuple(tuple) => {
|
||||
let mut types = vec![];
|
||||
|
||||
for value in tuple {
|
||||
let type_ = value.to_type(span.clone())?;
|
||||
types.push(type_)
|
||||
}
|
||||
|
||||
Type::Tuple(types)
|
||||
}
|
||||
ConstrainedValue::CircuitExpression(id, _members) => Type::Circuit(id.clone()),
|
||||
value => return Err(ValueError::implicit(value.to_string(), span)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_type(&mut self, types: &Vec<Type>, span: Span) -> Result<(), ValueError> {
|
||||
pub(crate) fn resolve_type(&mut self, type_: Option<Type>, span: Span) -> Result<(), ValueError> {
|
||||
if let ConstrainedValue::Unresolved(ref string) = self {
|
||||
if !types.is_empty() {
|
||||
*self = ConstrainedValue::from_type(string.clone(), &types[0], span)?
|
||||
if type_.is_some() {
|
||||
*self = ConstrainedValue::from_type(string.clone(), &type_.unwrap(), span)?
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,16 +131,21 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
}
|
||||
|
||||
/// Expect both `self` and `other` to resolve to the same type
|
||||
pub(crate) fn resolve_types(&mut self, other: &mut Self, types: &Vec<Type>, span: Span) -> Result<(), ValueError> {
|
||||
if !types.is_empty() {
|
||||
self.resolve_type(types, span.clone())?;
|
||||
return other.resolve_type(types, span);
|
||||
pub(crate) fn resolve_types(
|
||||
&mut self,
|
||||
other: &mut Self,
|
||||
type_: Option<Type>,
|
||||
span: Span,
|
||||
) -> Result<(), ValueError> {
|
||||
if type_.is_some() {
|
||||
self.resolve_type(type_.clone(), span.clone())?;
|
||||
return other.resolve_type(type_, span);
|
||||
}
|
||||
|
||||
match (&self, &other) {
|
||||
(ConstrainedValue::Unresolved(_), ConstrainedValue::Unresolved(_)) => Ok(()),
|
||||
(ConstrainedValue::Unresolved(_), _) => self.resolve_type(&vec![other.to_type(span.clone())?], span),
|
||||
(_, ConstrainedValue::Unresolved(_)) => other.resolve_type(&vec![self.to_type(span.clone())?], span),
|
||||
(ConstrainedValue::Unresolved(_), _) => self.resolve_type(Some(other.to_type(span.clone())?), span),
|
||||
(_, ConstrainedValue::Unresolved(_)) => other.resolve_type(Some(self.to_type(span.clone())?), span),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@ -208,6 +225,17 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::Tuple(tuple) => {
|
||||
tuple
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.map(|(i, value)| {
|
||||
let unique_name = format!("allocate tuple member {} {}:{}", i, span.line, span.start);
|
||||
|
||||
value.allocate_value(cs.ns(|| unique_name), span.clone())
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::CircuitExpression(_id, members) => {
|
||||
members
|
||||
.iter_mut()
|
||||
@ -219,17 +247,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::Return(array) => {
|
||||
array
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.map(|(i, value)| {
|
||||
let unique_name = format!("allocate return member {} {}:{}", i, span.line, span.start);
|
||||
|
||||
value.allocate_value(cs.ns(|| unique_name), span.clone())
|
||||
})
|
||||
.collect::<Result<(), ValueError>>()?;
|
||||
}
|
||||
ConstrainedValue::Mutable(value) => {
|
||||
value.allocate_value(cs, span)?;
|
||||
}
|
||||
@ -280,6 +297,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<F
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ConstrainedValue::Tuple(ref tuple) => {
|
||||
let values = tuple.iter().map(|x| format!("{}", x)).collect::<Vec<_>>().join(", ");
|
||||
|
||||
write!(f, "({})", values)
|
||||
}
|
||||
ConstrainedValue::CircuitExpression(ref identifier, ref members) => {
|
||||
write!(f, "{} {{", identifier)?;
|
||||
for (i, member) in members.iter().enumerate() {
|
||||
@ -290,16 +312,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<F
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
ConstrainedValue::Return(ref values) => {
|
||||
write!(f, "Program output: [")?;
|
||||
for (i, value) in values.iter().enumerate() {
|
||||
write!(f, "{}", value)?;
|
||||
if i < values.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
ConstrainedValue::CircuitDefinition(ref circuit) => write!(f, "circuit {{ {} }}", circuit.circuit_name),
|
||||
ConstrainedValue::Function(ref _circuit_option, ref function) => {
|
||||
write!(f, "function {{ {}() }}", function.identifier)
|
||||
@ -347,6 +359,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConditionalEqGadget<F> for Constrai
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
(ConstrainedValue::Tuple(tuple_1), ConstrainedValue::Tuple(tuple_2)) => {
|
||||
for (i, (left, right)) in tuple_1.into_iter().zip(tuple_2.into_iter()).enumerate() {
|
||||
left.conditional_enforce_equal(cs.ns(|| format!("tuple index {}", i)), right, condition)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
(_, _) => return Err(SynthesisError::Unsatisfiable),
|
||||
}
|
||||
}
|
||||
@ -393,6 +411,20 @@ impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for Constrained
|
||||
|
||||
ConstrainedValue::Array(array)
|
||||
}
|
||||
(ConstrainedValue::Tuple(tuple_1), ConstrainedValue::Array(tuple_2)) => {
|
||||
let mut array = vec![];
|
||||
|
||||
for (i, (first, second)) in tuple_1.into_iter().zip(tuple_2.into_iter()).enumerate() {
|
||||
array.push(Self::conditionally_select(
|
||||
cs.ns(|| format!("tuple index {}", i)),
|
||||
cond,
|
||||
first,
|
||||
second,
|
||||
)?);
|
||||
}
|
||||
|
||||
ConstrainedValue::Tuple(array)
|
||||
}
|
||||
(ConstrainedValue::Function(identifier_1, function_1), ConstrainedValue::Function(_, _)) => {
|
||||
// This is a no-op. functions cannot hold circuit values
|
||||
// However, we must return a result here
|
||||
@ -415,20 +447,6 @@ impl<F: Field + PrimeField, G: GroupType<F>> CondSelectGadget<F> for Constrained
|
||||
|
||||
ConstrainedValue::CircuitExpression(identifier.clone(), members)
|
||||
}
|
||||
(ConstrainedValue::Return(returns_1), ConstrainedValue::Return(returns_2)) => {
|
||||
let mut returns = vec![];
|
||||
|
||||
for (i, (first, second)) in returns_1.into_iter().zip(returns_2.into_iter()).enumerate() {
|
||||
returns.push(Self::conditionally_select(
|
||||
cs.ns(|| format!("return[{}]", i)),
|
||||
cond,
|
||||
first,
|
||||
second,
|
||||
)?);
|
||||
}
|
||||
|
||||
ConstrainedValue::Return(returns)
|
||||
}
|
||||
(ConstrainedValue::Static(first), ConstrainedValue::Static(second)) => {
|
||||
let value = Self::conditionally_select(cs, cond, first, second)?;
|
||||
|
||||
|
@ -169,6 +169,7 @@ fn test_mul() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_eq() {
|
||||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
||||
|
2
compiler/tests/group/input/point.in
Normal file
2
compiler/tests/group/input/point.in
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
a: group = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
|
@ -44,6 +44,16 @@ fn test_point() {
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_input() {
|
||||
let program_bytes = include_bytes!("point_input.leo");
|
||||
let input_bytes = include_bytes!("input/point.in");
|
||||
|
||||
let program = parse_program_with_input(program_bytes, input_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input() {
|
||||
let program_bytes = include_bytes!("input.leo");
|
||||
@ -196,6 +206,7 @@ fn test_assert_eq_fail() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_eq() {
|
||||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
||||
|
3
compiler/tests/group/point_input.leo
Normal file
3
compiler/tests/group/point_input.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: group) {
|
||||
let b = a;
|
||||
}
|
@ -13,6 +13,7 @@ pub mod macros;
|
||||
pub mod mutability;
|
||||
pub mod statements;
|
||||
pub mod syntax;
|
||||
pub mod tuples;
|
||||
|
||||
use leo_compiler::{
|
||||
compiler::Compiler,
|
||||
|
@ -4,6 +4,7 @@ use leo_compiler::errors::{CompilerError, ExpressionError, FunctionError, Statem
|
||||
use leo_input::InputParserError;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_semicolon() {
|
||||
let bytes = include_bytes!("semicolon.leo");
|
||||
let error = parse_program(bytes).err().unwrap();
|
||||
@ -43,6 +44,7 @@ fn test_undefined() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn input_syntax_error() {
|
||||
let bytes = include_bytes!("input_semicolon.leo");
|
||||
let error = parse_input(bytes).err().unwrap();
|
||||
|
6
compiler/tests/tuples/access.leo
Normal file
6
compiler/tests/tuples/access.leo
Normal file
@ -0,0 +1,6 @@
|
||||
function main() {
|
||||
let a = (true, false);
|
||||
|
||||
assert_eq!(a.0, true);
|
||||
assert_eq!(a.1, false);
|
||||
}
|
3
compiler/tests/tuples/basic.leo
Normal file
3
compiler/tests/tuples/basic.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
let a = (true, false);
|
||||
}
|
10
compiler/tests/tuples/function.leo
Normal file
10
compiler/tests/tuples/function.leo
Normal file
@ -0,0 +1,10 @@
|
||||
function foo() -> (bool, bool) {
|
||||
return (true, false)
|
||||
}
|
||||
|
||||
function main() {
|
||||
let a = foo();
|
||||
|
||||
assert_eq!(a.0, true);
|
||||
assert_eq!(a.1, false);
|
||||
}
|
10
compiler/tests/tuples/function_multiple.leo
Normal file
10
compiler/tests/tuples/function_multiple.leo
Normal file
@ -0,0 +1,10 @@
|
||||
function foo() -> (bool, bool) {
|
||||
return (true, false)
|
||||
}
|
||||
|
||||
function main() {
|
||||
let (a, b) = foo();
|
||||
|
||||
assert_eq!(a, true);
|
||||
assert_eq!(b, false);
|
||||
}
|
10
compiler/tests/tuples/function_typed.leo
Normal file
10
compiler/tests/tuples/function_typed.leo
Normal file
@ -0,0 +1,10 @@
|
||||
function foo() -> (bool, bool) {
|
||||
return (true, false)
|
||||
}
|
||||
|
||||
function main() {
|
||||
let a: (bool, bool) = foo();
|
||||
|
||||
assert_eq!(a.0, true);
|
||||
assert_eq!(a.1, false);
|
||||
}
|
3
compiler/tests/tuples/input.leo
Normal file
3
compiler/tests/tuples/input.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: (bool, bool)) {
|
||||
|
||||
}
|
2
compiler/tests/tuples/inputs/input.in
Normal file
2
compiler/tests/tuples/inputs/input.in
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
a: (bool, bool) = (true, true);
|
106
compiler/tests/tuples/mod.rs
Normal file
106
compiler/tests/tuples/mod.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::{assert_satisfied, parse_program};
|
||||
|
||||
#[test]
|
||||
fn test_tuple_basic() {
|
||||
let program_bytes = include_bytes!("basic.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_access() {
|
||||
let program_bytes = include_bytes!("access.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_typed() {
|
||||
let program_bytes = include_bytes!("typed.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple() {
|
||||
let program_bytes = include_bytes!("multiple.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_typed() {
|
||||
let program_bytes = include_bytes!("multiple_typed.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function() {
|
||||
let program_bytes = include_bytes!("function.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_typed() {
|
||||
let program_bytes = include_bytes!("function_typed.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_multiple() {
|
||||
let progam_bytes = include_bytes!("function_multiple.leo");
|
||||
|
||||
let program = parse_program(progam_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested() {
|
||||
let program_bytes = include_bytes!("nested.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_access() {
|
||||
let program_bytes = include_bytes!("nested_access.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_typed() {
|
||||
let program_bytes = include_bytes!("nested_typed.leo");
|
||||
|
||||
let program = parse_program(program_bytes).unwrap();
|
||||
|
||||
assert_satisfied(program);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_input() {
|
||||
// let input_bytes = include_bytes!("inputs/input.in");
|
||||
// let program_bytes = include_bytes!("")
|
||||
// }
|
6
compiler/tests/tuples/multiple.leo
Normal file
6
compiler/tests/tuples/multiple.leo
Normal file
@ -0,0 +1,6 @@
|
||||
function main() {
|
||||
let (a, b) = (true, false);
|
||||
|
||||
assert_eq!(a, true);
|
||||
assert_eq!(b, false);
|
||||
}
|
6
compiler/tests/tuples/multiple_typed.leo
Normal file
6
compiler/tests/tuples/multiple_typed.leo
Normal file
@ -0,0 +1,6 @@
|
||||
function main() {
|
||||
let (a, b): (bool, bool) = (true, false);
|
||||
|
||||
assert_eq!(a, true);
|
||||
assert_eq!(b, false);
|
||||
}
|
4
compiler/tests/tuples/nested.leo
Normal file
4
compiler/tests/tuples/nested.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a = (true, false);
|
||||
let b = (true, a);
|
||||
}
|
8
compiler/tests/tuples/nested_access.leo
Normal file
8
compiler/tests/tuples/nested_access.leo
Normal file
@ -0,0 +1,8 @@
|
||||
function main() {
|
||||
let a = (true, false);
|
||||
let b = (true, a);
|
||||
|
||||
assert_eq!(b.0, true);
|
||||
assert_eq!(b.1.0, true);
|
||||
assert_eq!(b.1.1, false);
|
||||
}
|
4
compiler/tests/tuples/nested_typed.leo
Normal file
4
compiler/tests/tuples/nested_typed.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
let a = (true, false);
|
||||
let b: (bool, (bool, bool)) = (true, a);
|
||||
}
|
3
compiler/tests/tuples/typed.leo
Normal file
3
compiler/tests/tuples/typed.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
let a: (bool, bool) = (true, false);
|
||||
}
|
@ -114,6 +114,15 @@ impl InputParserError {
|
||||
Self::new_from_span(message, table.span)
|
||||
}
|
||||
|
||||
pub fn tuple_length(expected: usize, actual: usize, span: Span) -> Self {
|
||||
let message = format!(
|
||||
"expected a tuple with {} elements, found a tuple with {} elements",
|
||||
expected, actual
|
||||
);
|
||||
|
||||
Self::new_from_span(message, span)
|
||||
}
|
||||
|
||||
pub fn section(header: Header) -> Self {
|
||||
let message = format!(
|
||||
"the section header `{}` must have a double bracket visibility in a state `.state` file",
|
||||
|
@ -1,8 +1,4 @@
|
||||
use crate::{
|
||||
ast::Rule,
|
||||
expressions::*,
|
||||
values::{Address, Value},
|
||||
};
|
||||
use crate::{ast::Rule, expressions::*, values::Value};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
@ -11,19 +7,19 @@ use std::fmt;
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::expression))]
|
||||
pub enum Expression<'ast> {
|
||||
ArrayInline(ArrayInlineExpression<'ast>),
|
||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||
ArrayInline(ArrayInlineExpression<'ast>),
|
||||
Tuple(TupleExpression<'ast>),
|
||||
Value(Value<'ast>),
|
||||
ImplicitAddress(Address<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> Expression<'ast> {
|
||||
pub fn span(&self) -> &Span {
|
||||
match self {
|
||||
Expression::ArrayInline(expression) => &expression.span,
|
||||
Expression::ArrayInitializer(expression) => &expression.span,
|
||||
Expression::ArrayInline(expression) => &expression.span,
|
||||
Expression::Tuple(tuple) => &tuple.span,
|
||||
Expression::Value(value) => value.span(),
|
||||
Expression::ImplicitAddress(address) => &address.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,20 +27,30 @@ impl<'ast> Expression<'ast> {
|
||||
impl<'ast> fmt::Display for Expression<'ast> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Expression::ImplicitAddress(ref address) => write!(f, "{}", address),
|
||||
Expression::Value(ref expression) => write!(f, "{}", expression),
|
||||
Expression::ArrayInline(ref expression) => {
|
||||
for (i, value) in expression.expressions.iter().enumerate() {
|
||||
write!(f, "array [{}", value)?;
|
||||
if i < expression.expressions.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
Expression::ArrayInitializer(ref expression) => {
|
||||
write!(f, "array [{} ; {}]", expression.expression, expression.count)
|
||||
}
|
||||
Expression::ArrayInline(ref array) => {
|
||||
let values = array
|
||||
.expressions
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
write!(f, "array [{}]", values)
|
||||
}
|
||||
Expression::Tuple(ref tuple) => {
|
||||
let values = tuple
|
||||
.expressions
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
write!(f, "({})", values)
|
||||
}
|
||||
Expression::Value(ref expression) => write!(f, "{}", expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,3 +6,6 @@ pub use array_inline_expression::*;
|
||||
|
||||
pub mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
pub mod tuple_expression;
|
||||
pub use tuple_expression::*;
|
||||
|
12
input/src/expressions/tuple_expression.rs
Normal file
12
input/src/expressions/tuple_expression.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::{ast::Rule, expressions::Expression};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[pest_ast(rule(Rule::expression_tuple))]
|
||||
pub struct TupleExpression<'ast> {
|
||||
pub expressions: Vec<Expression<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
@ -8,8 +8,10 @@ protected_name = {
|
||||
| "const"
|
||||
| "else"
|
||||
| "false"
|
||||
| "function"
|
||||
| type_field
|
||||
| "for"
|
||||
| "function"
|
||||
| type_group
|
||||
| "if"
|
||||
| "import"
|
||||
| "in"
|
||||
@ -33,7 +35,7 @@ LINE_END = { ";" ~ NEWLINE* }
|
||||
/// Types
|
||||
|
||||
// Declared in types/type_.rs
|
||||
type_ = { type_array | type_data }
|
||||
type_ = { type_tuple | type_array | type_data }
|
||||
|
||||
// Declared in types/integer_type.rs
|
||||
type_integer = {
|
||||
@ -89,6 +91,8 @@ type_data = { type_field | type_group | type_boolean | type_address | type_integ
|
||||
// Declared in types/array_type.rs
|
||||
type_array = { type_data ~ ("[" ~ number_positive ~ "]")+ }
|
||||
|
||||
type_tuple = { "(" ~ type_ ~ ("," ~ (type_tuple | type_))+ ~ ")" }
|
||||
|
||||
/// Values
|
||||
|
||||
// Declared in values/value.rs
|
||||
@ -134,8 +138,11 @@ group_single_or_tuple = {value_number | group_tuple}
|
||||
// Declared in values/address.rs
|
||||
address = @{ "aleo" ~ ASCII_DIGIT ~ (LOWERCASE_LETTER | ASCII_DIGIT){58} }
|
||||
|
||||
// Declared in values/address_typed.rs
|
||||
address_typed = ${ type_address ~ "(" ~ address ~ ")" }
|
||||
|
||||
// Declared in values/address_value.rs
|
||||
value_address = ${ type_address ~ "(" ~ address ~ ")" }
|
||||
value_address = {address | address_typed}
|
||||
|
||||
/// Expressions
|
||||
|
||||
@ -148,11 +155,12 @@ inline_array_inner = _{ (expression ~ ("," ~ NEWLINE* ~ expression)*)? }
|
||||
|
||||
// Declared in expressions/expression.rs
|
||||
expression = {
|
||||
expression_array_inline
|
||||
value
|
||||
| expression_tuple
|
||||
| expression_array_inline
|
||||
| expression_array_initializer
|
||||
| value
|
||||
| address // address conflicts with identifier namespaces so we catch implicit address values as expressions here
|
||||
}
|
||||
expression_tuple = { "(" ~ expression ~ ("," ~ expression)+ ~")" }
|
||||
|
||||
/// Parameters
|
||||
|
||||
@ -185,7 +193,7 @@ header = { main | record | registers | state_leaf | state | identifier }
|
||||
/// Definitions
|
||||
|
||||
// Declared in definition/definition.rs
|
||||
definition = { parameter ~ "=" ~ NEWLINE* ~ expression ~ LINE_END }
|
||||
definition = { parameter ~ "=" ~ expression ~ LINE_END }
|
||||
|
||||
/// Table
|
||||
|
||||
|
@ -22,6 +22,9 @@ pub use integer_type::*;
|
||||
pub mod signed_integer_type;
|
||||
pub use signed_integer_type::*;
|
||||
|
||||
pub mod tuple_type;
|
||||
pub use tuple_type::*;
|
||||
|
||||
pub mod type_;
|
||||
pub use type_::*;
|
||||
|
||||
|
25
input/src/types/tuple_type.rs
Normal file
25
input/src/types/tuple_type.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::{ast::Rule, types::Type};
|
||||
|
||||
use pest::Span;
|
||||
use pest_ast::FromPest;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||
#[pest_ast(rule(Rule::type_tuple))]
|
||||
pub struct TupleType<'ast> {
|
||||
pub types_: Vec<Type<'ast>>,
|
||||
#[pest_ast(outer())]
|
||||
pub span: Span<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> std::fmt::Display for TupleType<'ast> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let tuple = self
|
||||
.types_
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
write!(f, "({})", tuple)
|
||||
}
|
||||
}
|
@ -3,11 +3,12 @@ use crate::{ast::Rule, types::*};
|
||||
use pest_ast::FromPest;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, FromPest, PartialEq)]
|
||||
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||
#[pest_ast(rule(Rule::type_))]
|
||||
pub enum Type<'ast> {
|
||||
Basic(DataType),
|
||||
Array(ArrayType<'ast>),
|
||||
Tuple(TupleType<'ast>),
|
||||
}
|
||||
|
||||
impl<'ast> fmt::Display for Type<'ast> {
|
||||
@ -15,6 +16,7 @@ impl<'ast> fmt::Display for Type<'ast> {
|
||||
match *self {
|
||||
Type::Basic(ref basic) => write!(f, "{}", basic),
|
||||
Type::Array(ref array) => write!(f, "{}", array),
|
||||
Type::Tuple(ref tuple) => write!(f, "{}", tuple),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user