Merge pull request #198 from AleoHQ/feature/tuple

Implement tuple values and types
This commit is contained in:
Howard Wu 2020-08-14 21:00:37 -07:00 committed by GitHub
commit febc050a94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 1562 additions and 838 deletions

View File

@ -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>),

View File

@ -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),
}
}

View File

@ -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>,

View File

@ -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::*;

View 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>,
}

View File

@ -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())
}

View File

@ -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::*;

View File

@ -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),
}
}
}

View File

@ -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, "")
}
}

View File

@ -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)
}
}

View 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, "")
}
}

View File

@ -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()),
}
}
}

View File

@ -21,3 +21,6 @@ pub use postfix_expression::*;
pub mod ternary_expression;
pub use ternary_expression::*;
pub mod tuple_expression;
pub use tuple_expression::*;

View 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>,
}

View File

@ -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")]

View File

@ -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

View File

@ -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)
}
}

View File

@ -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::*;

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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),

View File

@ -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_::*;

View 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>,
}

View File

@ -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"),
}

View File

@ -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
}
}
},

View File

@ -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);
}

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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(),
)? {

View File

@ -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,
)?);
}

View File

@ -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(),
)? {

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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(),
)? {

View File

@ -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,
)?;

View File

@ -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,

View File

@ -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(),
)?;

View File

@ -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,

View File

@ -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)))
}
}

View File

@ -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)
}

View File

@ -29,3 +29,6 @@ pub use self::logical::*;
pub mod relational;
pub use self::relational::*;
pub mod tuple;
pub use self::tuple::*;

View 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())
}
}

View 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::*;

View 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))
}
}

View File

@ -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(),
));

View File

@ -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)?),
}
}
}

View File

@ -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"),
}
}

View File

@ -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::*;

View 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))
}
}

View File

@ -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,
)?;

View File

@ -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();

View File

@ -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(

View File

@ -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)
}

View File

@ -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),
}
}

View File

@ -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(

View File

@ -11,3 +11,6 @@ pub use self::assignee::*;
pub mod circuit_field;
pub use self::circuit_field::*;
pub mod tuple;
pub use self::tuple::*;

View 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(())
}
}

View File

@ -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);

View File

@ -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![],

View File

@ -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(())
}
}

View File

@ -2,6 +2,3 @@
pub mod definition;
pub use self::definition::*;
pub mod multiple;
pub use self::multiple::*;

View File

@ -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(())
}
}

View File

@ -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);

View File

@ -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)
}
}

View File

@ -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));
}

View File

@ -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!(),
}
}
}

View File

@ -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!(),
}
}
}

View File

@ -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))
}

View File

@ -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
}
}

View File

@ -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)?;

View File

@ -169,6 +169,7 @@ fn test_mul() {
}
#[test]
#[ignore]
fn test_eq() {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);

View File

@ -0,0 +1,2 @@
[main]
a: group = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;

View File

@ -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);

View File

@ -0,0 +1,3 @@
function main(a: group) {
let b = a;
}

View File

@ -13,6 +13,7 @@ pub mod macros;
pub mod mutability;
pub mod statements;
pub mod syntax;
pub mod tuples;
use leo_compiler::{
compiler::Compiler,

View File

@ -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();

View File

@ -0,0 +1,6 @@
function main() {
let a = (true, false);
assert_eq!(a.0, true);
assert_eq!(a.1, false);
}

View File

@ -0,0 +1,3 @@
function main() {
let a = (true, false);
}

View 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);
}

View 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);
}

View 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);
}

View File

@ -0,0 +1,3 @@
function main(a: (bool, bool)) {
}

View File

@ -0,0 +1,2 @@
[main]
a: (bool, bool) = (true, true);

View 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!("")
// }

View File

@ -0,0 +1,6 @@
function main() {
let (a, b) = (true, false);
assert_eq!(a, true);
assert_eq!(b, false);
}

View File

@ -0,0 +1,6 @@
function main() {
let (a, b): (bool, bool) = (true, false);
assert_eq!(a, true);
assert_eq!(b, false);
}

View File

@ -0,0 +1,4 @@
function main() {
let a = (true, false);
let b = (true, a);
}

View 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);
}

View File

@ -0,0 +1,4 @@
function main() {
let a = (true, false);
let b: (bool, (bool, bool)) = (true, a);
}

View File

@ -0,0 +1,3 @@
function main() {
let a: (bool, bool) = (true, false);
}

View File

@ -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",

View 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),
}
}
}

View File

@ -6,3 +6,6 @@ pub use array_inline_expression::*;
pub mod expression;
pub use expression::*;
pub mod tuple_expression;
pub use tuple_expression::*;

View 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>,
}

View File

@ -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

View File

@ -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_::*;

View 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)
}
}

View File

@ -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