Merge pull request #495 from AleoHQ/statement-breakout

Statement Breakout
This commit is contained in:
Collin Chin 2020-12-17 10:33:21 -05:00 committed by GitHub
commit 4c4338e38a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 1108 additions and 929 deletions

View File

@ -17,12 +17,6 @@
pub mod array_dimensions;
pub use array_dimensions::*;
pub mod assignee;
pub use assignee::*;
pub mod declare;
pub use declare::*;
pub mod identifier;
pub use identifier::*;
@ -46,9 +40,3 @@ pub use span::*;
pub mod spread_or_expression;
pub use spread_or_expression::*;
pub mod variables;
pub use variables::*;
pub mod variable_name;
pub use variable_name::*;

View File

@ -1,58 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Type, VariableName};
use leo_grammar::common::Variables as GrammarVariables;
use serde::{Deserialize, Serialize};
use std::fmt;
/// A variable that is assigned to a value in the constrained program
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Variables {
pub names: Vec<VariableName>,
pub type_: Option<Type>,
}
impl<'ast> From<GrammarVariables<'ast>> for Variables {
fn from(variables: GrammarVariables<'ast>) -> Self {
let names = variables.names.into_iter().map(VariableName::from).collect::<Vec<_>>();
let type_ = variables.type_.map(Type::from);
Self { names, type_ }
}
}
impl fmt::Display for Variables {
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| x.to_string()).collect::<Vec<_>>().join(",");
write!(f, "({})", names)?;
}
if self.type_.is_some() {
write!(f, ": {}", self.type_.as_ref().unwrap())?;
}
write!(f, "")
}
}

View File

@ -29,9 +29,6 @@ pub use self::circuits::*;
pub mod common;
pub use self::common::*;
pub mod console;
pub use self::console::*;
pub mod errors;
pub use self::errors::*;

View File

@ -14,15 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Identifier, PositiveNumber, RangeOrExpression, Span};
use leo_grammar::{access::AssigneeAccess as GrammarAssigneeAccess, common::Assignee as GrammarAssignee};
use crate::{Expression, Identifier, PositiveNumber, Span};
use leo_grammar::{
access::{ArrayAccess, AssigneeAccess as GrammarAssigneeAccess},
common::{Assignee as GrammarAssignee, Range, RangeOrExpression},
};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AssigneeAccess {
Array(RangeOrExpression),
ArrayRange(Option<Expression>, Option<Expression>),
ArrayIndex(Expression),
Tuple(PositiveNumber, Span),
Member(Identifier),
}
@ -30,7 +34,14 @@ pub enum AssigneeAccess {
impl<'ast> From<GrammarAssigneeAccess<'ast>> for AssigneeAccess {
fn from(access: GrammarAssigneeAccess<'ast>) -> Self {
match access {
GrammarAssigneeAccess::Array(array) => AssigneeAccess::Array(RangeOrExpression::from(array.expression)),
GrammarAssigneeAccess::Array(ArrayAccess {
expression: RangeOrExpression::Range(Range { from, to, .. }),
..
}) => AssigneeAccess::ArrayRange(from.map(Expression::from), to.map(Expression::from)),
GrammarAssigneeAccess::Array(ArrayAccess {
expression: RangeOrExpression::Expression(index),
..
}) => AssigneeAccess::ArrayIndex(Expression::from(index)),
GrammarAssigneeAccess::Tuple(tuple) => {
AssigneeAccess::Tuple(PositiveNumber::from(tuple.number), Span::from(tuple.span))
}
@ -74,7 +85,11 @@ impl fmt::Display for Assignee {
for access in &self.accesses {
match access {
AssigneeAccess::Array(expression) => write!(f, "[{}]", expression)?,
AssigneeAccess::ArrayRange(Some(left), Some(right)) => write!(f, "[{}..{}]", left, right)?,
AssigneeAccess::ArrayRange(None, Some(right)) => write!(f, "[..{}]", right)?,
AssigneeAccess::ArrayRange(Some(left), None) => write!(f, "[{}..]", left)?,
AssigneeAccess::ArrayRange(None, None) => write!(f, "[..]")?,
AssigneeAccess::ArrayIndex(index) => write!(f, "[{}]", index)?,
AssigneeAccess::Tuple(index, _span) => write!(f, ".{}", index)?,
AssigneeAccess::Member(member) => write!(f, ".{}", member)?,
}

View File

@ -0,0 +1,90 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, Node, Span};
pub use leo_grammar::operations::AssignOperation as GrammarAssignOperation;
use leo_grammar::statements::AssignStatement as GrammarAssignStatement;
use serde::{Deserialize, Serialize};
use std::fmt;
mod assignee;
pub use assignee::*;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum AssignOperation {
Assign,
Add,
Sub,
Mul,
Div,
Pow,
}
impl AsRef<str> for AssignOperation {
fn as_ref(&self) -> &'static str {
match self {
AssignOperation::Assign => "=",
AssignOperation::Add => "+=",
AssignOperation::Sub => "-=",
AssignOperation::Mul => "*=",
AssignOperation::Div => "/=",
AssignOperation::Pow => "**=",
}
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct AssignStatement {
pub operation: AssignOperation,
pub assignee: Assignee,
pub value: Expression,
pub span: Span,
}
impl fmt::Display for AssignStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {};", self.assignee, self.operation.as_ref(), self.value)
}
}
impl<'ast> From<GrammarAssignStatement<'ast>> for AssignStatement {
fn from(statement: GrammarAssignStatement<'ast>) -> Self {
AssignStatement {
operation: match statement.assign {
GrammarAssignOperation::Assign(_) => AssignOperation::Assign,
GrammarAssignOperation::AddAssign(_) => AssignOperation::Add,
GrammarAssignOperation::SubAssign(_) => AssignOperation::Sub,
GrammarAssignOperation::MulAssign(_) => AssignOperation::Mul,
GrammarAssignOperation::DivAssign(_) => AssignOperation::Div,
GrammarAssignOperation::PowAssign(_) => AssignOperation::Pow,
},
assignee: Assignee::from(statement.assignee),
value: Expression::from(statement.expression),
span: Span::from(statement.span),
}
}
}
impl Node for AssignStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,21 +14,23 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::Statement;
use crate::{Node, Span, Statement};
use leo_grammar::statements::Block as GrammarBlock;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct Block {
pub statements: Vec<Statement>,
pub span: Span,
}
impl<'ast> From<GrammarBlock<'ast>> for Block {
fn from(block: GrammarBlock<'ast>) -> Self {
Block {
statements: block.statements.into_iter().map(Statement::from).collect(),
span: Span::from(block.span),
}
}
}
@ -46,3 +48,13 @@ impl fmt::Display for Block {
write!(f, "}}")
}
}
impl Node for Block {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,17 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Block, ConditionalNestedOrEndStatement, Expression};
use leo_grammar::statements::ConditionalStatement as GrammarConditionalStatement;
use crate::{Block, Expression, Node, Span, Statement};
use leo_grammar::statements::{ConditionalNestedOrEndStatement, ConditionalStatement as GrammarConditionalStatement};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ConditionalStatement {
pub condition: Expression,
pub block: Block,
pub next: Option<ConditionalNestedOrEndStatement>,
pub next: Option<Box<Statement>>,
pub span: Span,
}
impl<'ast> From<GrammarConditionalStatement<'ast>> for ConditionalStatement {
@ -34,8 +35,14 @@ impl<'ast> From<GrammarConditionalStatement<'ast>> for ConditionalStatement {
block: Block::from(statement.block),
next: statement
.next
.map(|n_or_e| Some(ConditionalNestedOrEndStatement::from(n_or_e)))
.unwrap_or(None),
.map(|nested_statement| match nested_statement {
ConditionalNestedOrEndStatement::Nested(conditional_statement) => {
Statement::Conditional(ConditionalStatement::from(*conditional_statement))
}
ConditionalNestedOrEndStatement::End(block) => Statement::Block(Block::from(block)),
})
.map(Box::new),
span: Span::from(statement.span),
}
}
}
@ -49,3 +56,13 @@ impl fmt::Display for ConditionalStatement {
}
}
}
impl Node for ConditionalStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -1,49 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Block, ConditionalStatement};
use leo_grammar::statements::ConditionalNestedOrEndStatement as GrammarConditionalNestedOrEndStatement;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConditionalNestedOrEndStatement {
Nested(Box<ConditionalStatement>),
End(Block),
}
impl<'ast> From<GrammarConditionalNestedOrEndStatement<'ast>> for ConditionalNestedOrEndStatement {
fn from(statement: GrammarConditionalNestedOrEndStatement<'ast>) -> Self {
match statement {
GrammarConditionalNestedOrEndStatement::Nested(nested) => {
ConditionalNestedOrEndStatement::Nested(Box::new(ConditionalStatement::from(*nested)))
}
GrammarConditionalNestedOrEndStatement::End(block) => {
ConditionalNestedOrEndStatement::End(Block::from(block))
}
}
}
}
impl fmt::Display for ConditionalNestedOrEndStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConditionalNestedOrEndStatement::Nested(ref nested) => write!(f, "else {}", nested),
ConditionalNestedOrEndStatement::End(ref block) => write!(f, "else {}", block),
}
}
}

View File

@ -14,35 +14,45 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{ConsoleFunction, Span};
use crate::{ConsoleFunction, Node, Span};
use leo_grammar::console::ConsoleFunctionCall as GrammarConsoleFunctionCall;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConsoleFunctionCall {
pub struct ConsoleStatement {
pub function: ConsoleFunction,
pub span: Span,
}
impl<'ast> From<GrammarConsoleFunctionCall<'ast>> for ConsoleFunctionCall {
impl<'ast> From<GrammarConsoleFunctionCall<'ast>> for ConsoleStatement {
fn from(console: GrammarConsoleFunctionCall<'ast>) -> Self {
ConsoleFunctionCall {
ConsoleStatement {
function: ConsoleFunction::from(console.function),
span: Span::from(console.span),
}
}
}
impl fmt::Display for ConsoleFunctionCall {
impl fmt::Display for ConsoleStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "console.{};", self.function)
}
}
impl fmt::Debug for ConsoleFunctionCall {
impl fmt::Debug for ConsoleStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "console.{};", self.function)
}
}
impl Node for ConsoleStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,13 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::Span;
use crate::{Node, Span};
use leo_grammar::console::FormattedContainer as GrammarFormattedContainer;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct FormattedContainer {
pub span: Span,
}
@ -38,3 +38,13 @@ impl fmt::Display for FormattedContainer {
write!(f, "{{}}")
}
}
impl Node for FormattedContainer {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,17 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{FormattedContainer, FormattedParameter, Span};
use crate::{Expression, FormattedContainer, Node, Span};
use leo_grammar::console::FormattedString as GrammarFormattedString;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct FormattedString {
pub string: String,
pub containers: Vec<FormattedContainer>,
pub parameters: Vec<FormattedParameter>,
pub parameters: Vec<Expression>,
pub span: Span,
}
@ -33,7 +33,7 @@ impl<'ast> From<GrammarFormattedString<'ast>> for FormattedString {
let string = formatted.string;
let span = Span::from(formatted.span);
let containers = formatted.containers.into_iter().map(FormattedContainer::from).collect();
let parameters = formatted.parameters.into_iter().map(FormattedParameter::from).collect();
let parameters = formatted.parameters.into_iter().map(Expression::from).collect();
Self {
string,
@ -49,3 +49,13 @@ impl fmt::Display for FormattedString {
write!(f, "{}", self.string)
}
}
impl Node for FormattedString {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -17,14 +17,11 @@
pub mod console_function;
pub use console_function::*;
pub mod console_function_call;
pub use console_function_call::*;
pub mod console_statement;
pub use console_statement::*;
pub mod formatted_container;
pub use formatted_container::*;
pub mod formatted_parameter;
pub use formatted_parameter::*;
pub mod formatted_string;
pub use formatted_string::*;

View File

@ -0,0 +1,92 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, Node, Span, Type};
use serde::{Deserialize, Serialize};
use std::fmt;
mod variable_name;
pub use variable_name::*;
mod declare;
pub use declare::*;
use leo_grammar::statements::DefinitionStatement as GrammarDefinitionStatement;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct DefinitionStatement {
pub declaration_type: Declare,
pub variable_names: Vec<VariableName>,
pub type_: Option<Type>,
pub value: Expression,
pub span: Span,
}
impl fmt::Display for DefinitionStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ", self.declaration_type)?;
if self.variable_names.len() == 1 {
// mut a
write!(f, "{}", self.variable_names[0])?;
} else {
// (a, mut b)
let names = self
.variable_names
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "({})", names)?;
}
if self.type_.is_some() {
write!(f, ": {}", self.type_.as_ref().unwrap())?;
}
write!(f, " = {};", self.value)
}
}
impl<'ast> From<GrammarDefinitionStatement<'ast>> for DefinitionStatement {
fn from(statement: GrammarDefinitionStatement<'ast>) -> Self {
let variable_names = statement
.variables
.names
.into_iter()
.map(VariableName::from)
.collect::<Vec<_>>();
let type_ = statement.variables.type_.map(Type::from);
DefinitionStatement {
declaration_type: Declare::from(statement.declare),
variable_names,
type_,
value: Expression::from(statement.expression),
span: Span::from(statement.span),
}
}
}
impl Node for DefinitionStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::common::{Identifier, Span};
use crate::{Identifier, Node, Span};
use leo_grammar::common::VariableName as GrammarVariableName;
use serde::{Deserialize, Serialize};
@ -46,3 +46,13 @@ impl fmt::Display for VariableName {
write!(f, "{}", self.identifier)
}
}
impl Node for VariableName {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, Node, Span};
use leo_grammar::statements::ExpressionStatement as GrammarExpressionStatement;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ExpressionStatement {
pub expression: Expression,
pub span: Span,
}
impl fmt::Display for ExpressionStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{};", self.expression)
}
}
impl<'ast> From<GrammarExpressionStatement<'ast>> for ExpressionStatement {
fn from(statement: GrammarExpressionStatement<'ast>) -> Self {
// why do we have this span-setting logic?
let span = Span::from(statement.span);
let mut expression = Expression::from(statement.expression);
expression.set_span(span.clone());
ExpressionStatement { expression, span }
}
}
impl Node for ExpressionStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -0,0 +1,62 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Block, Expression, Identifier, Node, Span};
use leo_grammar::statements::ForStatement;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct IterationStatement {
pub variable: Identifier,
pub start: Expression,
pub stop: Expression,
pub block: Block,
pub span: Span,
}
impl fmt::Display for IterationStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"for {} in {}..{} {}",
self.variable, self.start, self.stop, self.block
)
}
}
impl<'ast> From<ForStatement<'ast>> for IterationStatement {
fn from(statement: ForStatement<'ast>) -> Self {
IterationStatement {
variable: Identifier::from(statement.index),
start: Expression::from(statement.start),
stop: Expression::from(statement.stop),
block: Block::from(statement.block),
span: Span::from(statement.span),
}
}
}
impl Node for IterationStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,14 +14,29 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
pub mod conditional_nested_or_end_statement;
pub use conditional_nested_or_end_statement::*;
pub mod conditional_statement;
pub use conditional_statement::*;
pub mod statement;
pub use statement::*;
pub mod conditional;
pub use conditional::*;
pub mod block;
pub use block::*;
pub mod return_statement;
pub use return_statement::*;
pub mod iteration;
pub use iteration::*;
pub mod expression;
pub use expression::*;
pub mod definition;
pub use definition::*;
pub mod console;
pub use console::*;
pub mod assign;
pub use assign::*;

View File

@ -14,29 +14,39 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, Span};
use leo_grammar::console::FormattedParameter as GrammarFormattedParameter;
use crate::{Expression, Node, Span};
use leo_grammar::statements::ReturnStatement as GrammarReturnStatement;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FormattedParameter {
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ReturnStatement {
pub expression: Expression,
pub span: Span,
}
impl<'ast> From<GrammarFormattedParameter<'ast>> for FormattedParameter {
fn from(parameter: GrammarFormattedParameter<'ast>) -> Self {
Self {
expression: Expression::from(parameter.expression),
span: Span::from(parameter.span),
impl fmt::Display for ReturnStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "return {}", self.expression)
}
}
impl<'ast> From<GrammarReturnStatement<'ast>> for ReturnStatement {
fn from(statement: GrammarReturnStatement<'ast>) -> Self {
ReturnStatement {
expression: Expression::from(statement.expression),
span: Span::from(statement.span),
}
}
}
impl fmt::Display for FormattedParameter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.expression)
impl Node for ReturnStatement {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,163 +14,83 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{
Assignee,
BinaryExpression,
BinaryOperation,
Block,
ConditionalStatement,
ConsoleFunctionCall,
Declare,
Expression,
Identifier,
Node,
Span,
Variables,
};
use crate::{ConditionalStatement, Node, Span};
use leo_grammar::{
console::ConsoleFunctionCall as GrammarConsoleFunctionCall,
operations::AssignOperation,
statements::{
AssignStatement,
DefinitionStatement,
ExpressionStatement,
ForStatement,
ReturnStatement,
Statement as GrammarStatement,
},
};
use leo_grammar::statements::Statement as GrammarStatement;
use super::*;
use serde::{Deserialize, Serialize};
use std::fmt;
/// Program statement that defines some action (or expression) to be carried out.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum Statement {
Return(Expression, Span),
Definition(Declare, Variables, Expression, Span),
Assign(Assignee, Expression, Span),
Conditional(ConditionalStatement, Span),
Iteration(Identifier, Box<(Expression, Expression)>, Block, Span),
Console(ConsoleFunctionCall),
Expression(Expression, Span),
}
impl<'ast> From<ReturnStatement<'ast>> for Statement {
fn from(statement: ReturnStatement<'ast>) -> Self {
Statement::Return(Expression::from(statement.expression), Span::from(statement.span))
}
}
impl<'ast> From<DefinitionStatement<'ast>> for Statement {
fn from(statement: DefinitionStatement<'ast>) -> Self {
let span = Span::from(statement.span);
Statement::Definition(
Declare::from(statement.declare),
Variables::from(statement.variables),
Expression::from(statement.expression),
span,
)
}
}
impl<'ast> From<AssignStatement<'ast>> for Statement {
fn from(statement: AssignStatement<'ast>) -> Self {
match statement.assign {
AssignOperation::Assign(ref _assign) => Statement::Assign(
Assignee::from(statement.assignee),
Expression::from(statement.expression),
Span::from(statement.span),
),
operation_assign => {
// convert assignee into postfix expression
let converted = Expression::from(statement.assignee.clone());
let operator = match operation_assign {
AssignOperation::AddAssign(ref _assign) => BinaryOperation::Add,
AssignOperation::SubAssign(ref _assign) => BinaryOperation::Sub,
AssignOperation::MulAssign(ref _assign) => BinaryOperation::Mul,
AssignOperation::DivAssign(ref _assign) => BinaryOperation::Div,
AssignOperation::PowAssign(ref _assign) => BinaryOperation::Pow,
AssignOperation::Assign(ref _assign) => unimplemented!("cannot assign twice to assign statement"),
};
Statement::Assign(
Assignee::from(statement.assignee),
Expression::Binary(BinaryExpression {
left: Box::new(converted),
right: Box::new(Expression::from(statement.expression)),
op: operator,
span: Span::from(statement.span.clone()),
}),
Span::from(statement.span),
)
}
}
}
}
impl<'ast> From<ForStatement<'ast>> for Statement {
fn from(statement: ForStatement<'ast>) -> Self {
Statement::Iteration(
Identifier::from(statement.index),
Box::new((Expression::from(statement.start), Expression::from(statement.stop))),
Block::from(statement.block),
Span::from(statement.span),
)
}
}
impl<'ast> From<GrammarConsoleFunctionCall<'ast>> for Statement {
fn from(function_call: GrammarConsoleFunctionCall<'ast>) -> Self {
Statement::Console(ConsoleFunctionCall::from(function_call))
}
}
impl<'ast> From<ExpressionStatement<'ast>> for Statement {
fn from(statement: ExpressionStatement<'ast>) -> Self {
let span = Span::from(statement.span);
let mut expression = Expression::from(statement.expression);
expression.set_span(span.clone());
Statement::Expression(expression, span)
}
Return(ReturnStatement),
Definition(DefinitionStatement),
Assign(AssignStatement),
Conditional(ConditionalStatement),
Iteration(IterationStatement),
Console(ConsoleStatement),
Expression(ExpressionStatement),
Block(Block),
}
impl<'ast> From<GrammarStatement<'ast>> for Statement {
fn from(statement: GrammarStatement<'ast>) -> Self {
match statement {
GrammarStatement::Return(statement) => Statement::from(statement),
GrammarStatement::Definition(statement) => Statement::from(statement),
GrammarStatement::Assign(statement) => Statement::from(statement),
GrammarStatement::Conditional(statement) => {
let span = Span::from(statement.span.clone());
Statement::Conditional(ConditionalStatement::from(statement), span)
}
GrammarStatement::Iteration(statement) => Statement::from(statement),
GrammarStatement::Console(console) => Statement::from(console),
GrammarStatement::Expression(statement) => Statement::from(statement),
GrammarStatement::Return(statement) => Statement::Return(ReturnStatement::from(statement)),
GrammarStatement::Definition(statement) => Statement::Definition(DefinitionStatement::from(statement)),
GrammarStatement::Assign(statement) => Statement::Assign(AssignStatement::from(statement)),
GrammarStatement::Conditional(statement) => Statement::Conditional(ConditionalStatement::from(statement)),
GrammarStatement::Iteration(statement) => Statement::Iteration(IterationStatement::from(statement)),
GrammarStatement::Console(statement) => Statement::Console(ConsoleStatement::from(statement)),
GrammarStatement::Expression(statement) => Statement::Expression(ExpressionStatement::from(statement)),
GrammarStatement::Block(statement) => Statement::Block(Block::from(statement)),
}
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Return(ref expression, ref _span) => write!(f, "return {}", expression),
Statement::Definition(ref declare, ref variable, ref expression, ref _span) => {
write!(f, "{} {} = {};", declare, variable, expression)
}
Statement::Assign(ref variable, ref statement, ref _span) => write!(f, "{} = {};", variable, statement),
Statement::Conditional(ref statement, ref _span) => write!(f, "{}", statement),
Statement::Iteration(ref var, ref start_stop, ref block, ref _span) => {
write!(f, "for {} in {}..{} {}", var, start_stop.0, start_stop.1, block)
}
Statement::Console(ref console) => write!(f, "{}", console),
Statement::Expression(ref expression, ref _span) => write!(f, "{};", expression),
match self {
Statement::Return(x) => x.fmt(f),
Statement::Definition(x) => x.fmt(f),
Statement::Assign(x) => x.fmt(f),
Statement::Conditional(x) => x.fmt(f),
Statement::Iteration(x) => x.fmt(f),
Statement::Console(x) => x.fmt(f),
Statement::Expression(x) => x.fmt(f),
Statement::Block(x) => x.fmt(f),
}
}
}
impl Node for Statement {
fn span(&self) -> &Span {
use Statement::*;
match &self {
Return(n) => n.span(),
Definition(n) => n.span(),
Assign(n) => n.span(),
Conditional(n) => n.span(),
Iteration(n) => n.span(),
Console(n) => n.span(),
Expression(n) => n.span(),
Block(n) => n.span(),
}
}
fn set_span(&mut self, span: Span) {
use Statement::*;
match self {
Return(n) => n.set_span(span),
Definition(n) => n.set_span(span),
Assign(n) => n.set_span(span),
Conditional(n) => n.set_span(span),
Iteration(n) => n.set_span(span),
Console(n) => n.set_span(span),
Expression(n) => n.set_span(span),
Block(n) => n.set_span(span),
}
}
}

View File

@ -11,8 +11,8 @@
"block" : {
"statements": [
{
"Return": [
{
"Return": {
"expression": {
"Binary": {
"left": {
"Value": {
@ -49,15 +49,21 @@
}
}
},
{
"span" : {
"text": " return 1 + 1",
"line": 2,
"start": 5,
"end": 17
}
]
}
}
]
],
"span" : {
"text": " return 1 + 1",
"line": 2,
"start": 5,
"end": 17
}
},
"span": {
"text": " function main() {",

View File

@ -17,7 +17,7 @@
//! Evaluates a macro in a compiled Leo program.
use crate::{errors::ConsoleError, program::ConstrainedProgram, statement::get_indicator_value, GroupType};
use leo_ast::{ConsoleFunction, ConsoleFunctionCall};
use leo_ast::{ConsoleFunction, ConsoleStatement};
use snarkos_models::{
curves::{Field, PrimeField},
@ -31,7 +31,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
file_scope: &str,
function_scope: &str,
indicator: &Boolean,
console: ConsoleFunctionCall,
console: ConsoleStatement,
) -> Result<(), ConsoleError> {
match console.function {
ConsoleFunction::Assert(expression) => {

View File

@ -52,8 +52,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let mut result = string.to_string();
for parameter in formatted.parameters.into_iter() {
let parameter_value =
self.enforce_expression(cs, file_scope, function_scope, None, parameter.expression)?;
let parameter_value = self.enforce_expression(cs, file_scope, function_scope, None, parameter)?;
result = result.replacen("{}", &parameter_value.to_string(), 1);
}

View File

@ -52,7 +52,7 @@ pub fn generate_constraints<F: Field + PrimeField, G: GroupType<F>, CS: Constrai
match main.clone() {
ConstrainedValue::Function(_circuit_identifier, function) => {
let result = resolved_program.enforce_main_function(cs, &program_name, function, input)?;
let result = resolved_program.enforce_main_function(cs, &program_name, *function, input)?;
Ok(result)
}
_ => Err(CompilerError::NoMainFunction),

View File

@ -56,7 +56,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let resolved_function_name = new_scope(program_name, &function_name.name);
self.store(
resolved_function_name,
ConstrainedValue::Function(None, function.clone()),
ConstrainedValue::Function(None, Box::new(function.clone())),
);
});

View File

@ -72,12 +72,36 @@ impl StatementError {
Self::new_from_span(message, span)
}
pub fn array_assign_interior_index(span: Span) -> Self {
let message = "Cannot assign single index to interior of array of values".to_string();
Self::new_from_span(message, span)
}
pub fn array_assign_range(span: Span) -> Self {
let message = "Cannot assign range of array values to single value".to_string();
Self::new_from_span(message, span)
}
pub fn array_assign_index_bounds(index: usize, length: usize, span: Span) -> Self {
let message = format!(
"Array assign index `{}` out of range for array of length `{}`",
index, length
);
Self::new_from_span(message, span)
}
pub fn array_assign_range_order(start: usize, stop: usize, length: usize, span: Span) -> Self {
let message = format!(
"Array assign range `{}`..`{}` out of range for array of length `{}`",
start, stop, length
);
Self::new_from_span(message, span)
}
pub fn conditional_boolean(actual: String, span: Span) -> Self {
let message = format!("If, else conditional must resolve to a boolean, found `{}`", actual);
@ -166,6 +190,15 @@ impl StatementError {
Self::new_from_span(message, span)
}
pub fn tuple_assign_index_bounds(index: usize, length: usize, span: Span) -> Self {
let message = format!(
"Tuple assign index `{}` out of range for tuple of length `{}`",
index, length
);
Self::new_from_span(message, span)
}
pub fn tuple_type(type_: String, span: Span) -> Self {
let message = format!("Expected tuple type, found type `{}`", type_);

View File

@ -82,7 +82,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
CircuitMember::CircuitFunction(function) => {
let identifier = function.identifier.clone();
let constrained_function_value =
ConstrainedValue::Function(Some(circuit_identifier.clone()), function);
ConstrainedValue::Function(Some(circuit_identifier.clone()), Box::new(function));
resolved_members.push(ConstrainedCircuitMember(identifier, constrained_function_value));
}

View File

@ -72,6 +72,9 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
};
Ok(ConstrainedValue::Function(Some(circuit.circuit_name), function))
Ok(ConstrainedValue::Function(
Some(circuit.circuit_name),
Box::new(function),
))
}
}

View File

@ -45,7 +45,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let name = new_scope(scope, &identifier.name);
let value = ConstrainedValue::Import(
program_name.to_owned(),
Box::new(ConstrainedValue::Function(None, function.clone())),
Box::new(ConstrainedValue::Function(None, Box::new(function.clone()))),
);
self.store(name, value);
@ -72,7 +72,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
match matched_function {
Some((_function_name, function)) => ConstrainedValue::Import(
program_name.to_owned(),
Box::new(ConstrainedValue::Function(None, function.clone())),
Box::new(ConstrainedValue::Function(None, Box::new(function.clone()))),
),
None => return Err(ImportError::unknown_symbol(symbol.to_owned(), program_name.to_owned())),
}

View File

@ -1,105 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Enforces an array assignment statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_ast::{RangeOrExpression, 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> {
#[allow(clippy::too_many_arguments)]
pub fn assign_array<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: &str,
function_scope: &str,
indicator: &Boolean,
name: &str,
range_or_expression: RangeOrExpression,
mut new_value: ConstrainedValue<F, G>,
span: &Span,
) -> Result<(), StatementError> {
// Resolve index so we know if we are assigning to a single value or a range of values
match range_or_expression {
RangeOrExpression::Expression(index) => {
let index = self.enforce_index(cs, file_scope, function_scope, index, span)?;
// Modify the single value of the array in place
match self.get_mutable_assignee(name, &span)? {
ConstrainedValue::Array(old) => {
new_value.resolve_type(Some(old[index].to_type(&span)?), &span)?;
let selected_value = ConstrainedValue::conditionally_select(
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
indicator,
&new_value,
&old[index],
)
.map_err(|_| {
StatementError::select_fail(new_value.to_string(), old[index].to_string(), span.to_owned())
})?;
old[index] = selected_value;
}
_ => return Err(StatementError::array_assign_index(span.to_owned())),
}
}
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => self.enforce_index(cs, file_scope, function_scope, integer, span)?,
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(self.enforce_index(cs, file_scope, function_scope, integer, span)?),
None => None,
};
// Modify the range of values of the array
let old_array = self.get_mutable_assignee(name, &span)?;
let new_array = match (old_array.clone(), new_value) {
(ConstrainedValue::Array(mut mutable), ConstrainedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(mutable.len());
mutable.splice(from_index..to_index, new.iter().cloned());
ConstrainedValue::Array(mutable)
}
_ => return Err(StatementError::array_assign_range(span.to_owned())),
};
let selected_array = ConstrainedValue::conditionally_select(
cs.ns(|| format!("select {} {}:{}", new_array, span.line, span.start)),
indicator,
&new_array,
old_array,
)
.map_err(|_| {
StatementError::select_fail(new_array.to_string(), old_array.to_string(), span.to_owned())
})?;
*old_array = selected_array;
}
}
Ok(())
}
}

View File

@ -17,14 +17,14 @@
//! Enforces an assign statement in a compiled Leo program.
use crate::{
assignee::resolve_assignee,
arithmetic::*,
errors::StatementError,
new_scope,
program::ConstrainedProgram,
value::ConstrainedValue,
GroupType,
};
use leo_ast::{Assignee, AssigneeAccess, Expression, Span};
use leo_ast::{AssignOperation, AssignStatement, AssigneeAccess, Span};
use snarkos_models::{
curves::{Field, PrimeField},
@ -44,71 +44,109 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
declared_circuit_reference: &str,
indicator: &Boolean,
mut_self: bool,
assignee: Assignee,
expression: Expression,
span: &Span,
statement: AssignStatement,
) -> Result<(), StatementError> {
// Get the name of the variable we are assigning to
let variable_name = resolve_assignee(function_scope.to_string(), assignee.clone());
let mut new_value = self.enforce_expression(cs, file_scope, function_scope, None, statement.value)?;
let mut resolved_assignee = self.resolve_assignee(
cs,
file_scope,
function_scope,
declared_circuit_reference,
mut_self,
statement.assignee.clone(),
)?;
// Evaluate new value
let mut new_value = self.enforce_expression(cs, file_scope, function_scope, None, expression)?;
if resolved_assignee.len() == 1 {
new_value.resolve_type(Some(resolved_assignee[0].to_type(&statement.span)?), &statement.span)?;
// Mutate the old value into the new value
if assignee.accesses.is_empty() {
let old_value = self.get_mutable_assignee(&variable_name, span)?;
let span = statement.span.clone();
new_value.resolve_type(Some(old_value.to_type(&span)?), span)?;
let selected_value = ConstrainedValue::conditionally_select(
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
Self::enforce_assign_operation(
cs,
indicator,
&new_value,
old_value,
)
.map_err(|_| StatementError::select_fail(new_value.to_string(), old_value.to_string(), span.to_owned()))?;
*old_value = selected_value;
Ok(())
format!("select {} {}:{}", new_value, &span.line, &span.start),
&statement.operation,
resolved_assignee[0],
new_value,
&span,
)?;
} else {
match assignee.accesses[0].clone() {
AssigneeAccess::Array(range_or_expression) => self.assign_array(
cs,
file_scope,
function_scope,
indicator,
&variable_name,
range_or_expression,
new_value,
span,
),
AssigneeAccess::Tuple(index, span) => {
self.assign_tuple(cs, indicator, &variable_name, index, new_value, &span)
}
AssigneeAccess::Member(identifier) => {
// Mutate a circuit variable using the self keyword.
if assignee.identifier.is_self() && mut_self {
let self_circuit_variable_name = new_scope(&assignee.identifier.name, &identifier.name);
let self_variable_name = new_scope(file_scope, &self_circuit_variable_name);
let value = self.mutate_circuit_variable(
match new_value {
ConstrainedValue::Array(new_values) => {
let span = statement.span.clone();
for (i, (old_ref, new_value)) in
resolved_assignee.into_iter().zip(new_values.into_iter()).enumerate()
{
Self::enforce_assign_operation(
cs,
indicator,
declared_circuit_reference,
identifier,
format!("select-splice {} {} {}:{}", i, new_value, &span.line, &span.start),
&statement.operation,
old_ref,
new_value,
span,
&span,
)?;
self.store(self_variable_name, value);
} else {
let _value =
self.mutate_circuit_variable(cs, indicator, &variable_name, identifier, new_value, span)?;
}
Ok(())
}
_ => return Err(StatementError::array_assign_range(statement.span)),
};
}
// self re-store logic -- structure is already checked by enforce_assign_operation
if statement.assignee.identifier.is_self() && mut_self {
if let Some(AssigneeAccess::Member(member_name)) = statement.assignee.accesses.get(0) {
let self_circuit_variable_name = new_scope(&statement.assignee.identifier.name, &member_name.name);
let self_variable_name = new_scope(file_scope, &self_circuit_variable_name);
// get circuit ref
let target = match self.get(declared_circuit_reference) {
Some(ConstrainedValue::Mutable(value)) => &**value,
_ => unimplemented!(),
};
// get freshly assigned member ref, and clone it
let source = match target {
ConstrainedValue::CircuitExpression(_circuit_name, members) => {
let matched_variable = members.iter().find(|member| &member.0 == member_name);
match matched_variable {
Some(member) => &member.1,
None => unimplemented!(),
}
}
_ => unimplemented!(),
}
.clone();
self.store(self_variable_name, source);
}
}
Ok(())
}
fn enforce_assign_operation<CS: ConstraintSystem<F>>(
cs: &mut CS,
condition: &Boolean,
scope: String,
operation: &AssignOperation,
target: &mut ConstrainedValue<F, G>,
mut new_value: ConstrainedValue<F, G>,
span: &Span,
) -> Result<(), StatementError> {
new_value.resolve_type(Some(target.to_type(span)?), span)?;
let new_value = match operation {
AssignOperation::Assign => new_value,
AssignOperation::Add => enforce_add(cs, target.clone(), new_value, span)?,
AssignOperation::Sub => enforce_sub(cs, target.clone(), new_value, span)?,
AssignOperation::Mul => enforce_mul(cs, target.clone(), new_value, span)?,
AssignOperation::Div => enforce_div(cs, target.clone(), new_value, span)?,
AssignOperation::Pow => enforce_pow(cs, target.clone(), new_value, span)?,
};
let selected_value = ConstrainedValue::conditionally_select(cs.ns(|| scope), condition, &new_value, target)
.map_err(|_| StatementError::select_fail(new_value.to_string(), target.to_string(), span.clone()))?;
*target = selected_value;
Ok(())
}
}

View File

@ -16,16 +16,223 @@
//! Resolves assignees in a compiled Leo program.
use crate::{errors::StatementError, new_scope, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_ast::{Assignee, Span};
use crate::{
errors::StatementError,
new_scope,
parse_index,
program::ConstrainedProgram,
value::ConstrainedValue,
GroupType,
};
use leo_ast::{Assignee, AssigneeAccess, Identifier, PositiveNumber, Span};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
pub fn resolve_assignee(scope: String, assignee: Assignee) -> String {
new_scope(&scope, &assignee.identifier().to_string())
enum ResolvedAssigneeAccess {
ArrayRange(Option<usize>, Option<usize>),
ArrayIndex(usize),
Tuple(PositiveNumber, Span),
Member(Identifier),
}
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn resolve_assignee<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
file_scope: &str,
function_scope: &str,
declared_circuit_reference: &str,
mut_self: bool,
assignee: Assignee,
) -> Result<Vec<&mut ConstrainedValue<F, G>>, StatementError> {
let value_ref = if assignee.identifier.is_self() {
if !mut_self {
return Err(StatementError::immutable_assign("self".to_string(), assignee.span));
}
declared_circuit_reference.to_string()
} else {
new_scope(&function_scope, &assignee.identifier().to_string())
};
let span = assignee.span.clone();
let identifier_string = assignee.identifier.to_string();
let resolved_accesses = assignee
.accesses
.into_iter()
.map(|access| match access {
AssigneeAccess::ArrayRange(start, stop) => {
let start_index = start
.map(|start| self.enforce_index(cs, file_scope, function_scope, start, &span))
.transpose()?;
let stop_index = stop
.map(|stop| self.enforce_index(cs, file_scope, function_scope, stop, &span))
.transpose()?;
Ok(ResolvedAssigneeAccess::ArrayRange(start_index, stop_index))
}
AssigneeAccess::ArrayIndex(index) => {
let index = self.enforce_index(cs, file_scope, function_scope, index, &span)?;
Ok(ResolvedAssigneeAccess::ArrayIndex(index))
}
AssigneeAccess::Tuple(index, span) => Ok(ResolvedAssigneeAccess::Tuple(index, span)),
AssigneeAccess::Member(identifier) => Ok(ResolvedAssigneeAccess::Member(identifier)),
})
.collect::<Result<Vec<_>, crate::errors::ExpressionError>>()?;
let mut result = vec![match self.get_mut(&value_ref) {
Some(value) => match value {
ConstrainedValue::Mutable(mutable) => &mut **mutable,
_ => return Err(StatementError::immutable_assign(identifier_string, span)),
},
None => return Err(StatementError::undefined_variable(identifier_string, span)),
}];
for access in resolved_accesses {
result = Self::resolve_assignee_access(access, &span, result)?;
}
Ok(result)
}
fn check_range_index(start_index: usize, stop_index: usize, len: usize, span: &Span) -> Result<(), StatementError> {
if stop_index < start_index {
Err(StatementError::array_assign_range_order(
start_index,
stop_index,
len,
span.clone(),
))
} else if start_index > len {
Err(StatementError::array_assign_index_bounds(
start_index,
len,
span.clone(),
))
} else if stop_index > len {
Err(StatementError::array_assign_index_bounds(stop_index, len, span.clone()))
} else {
Ok(())
}
}
// discards unnecessary mutable wrappers
fn unwrap_mutable(input: &mut ConstrainedValue<F, G>) -> &mut ConstrainedValue<F, G> {
match input {
ConstrainedValue::Mutable(x) => &mut **x,
x => x,
}
}
fn resolve_assignee_access<'a>(
access: ResolvedAssigneeAccess,
span: &Span,
mut value: Vec<&'a mut ConstrainedValue<F, G>>,
) -> Result<Vec<&'a mut ConstrainedValue<F, G>>, StatementError> {
match access {
ResolvedAssigneeAccess::ArrayIndex(index) => {
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(span.clone()));
}
match Self::unwrap_mutable(value.remove(0)) {
ConstrainedValue::Array(old) => {
if index > old.len() {
Err(StatementError::array_assign_index_bounds(
index,
old.len(),
span.clone(),
))
} else {
Ok(vec![old.get_mut(index).unwrap()])
}
}
_ => Err(StatementError::array_assign_index(span.clone())),
}
}
ResolvedAssigneeAccess::ArrayRange(start_index, stop_index) => {
let start_index = start_index.unwrap_or(0);
if value.len() == 1 {
// not a range of a range
match Self::unwrap_mutable(value.remove(0)) {
ConstrainedValue::Array(old) => {
let stop_index = stop_index.unwrap_or(old.len());
Self::check_range_index(start_index, stop_index, old.len(), &span)?;
Ok(old[start_index..stop_index].iter_mut().collect())
}
_ => Err(StatementError::array_assign_index(span.clone())),
}
} else {
// range of a range
let stop_index = stop_index.unwrap_or(value.len());
Self::check_range_index(start_index, stop_index, value.len(), &span)?;
Ok(value.drain(start_index..stop_index).map(Self::unwrap_mutable).collect())
}
}
ResolvedAssigneeAccess::Tuple(index, span) => {
let index = parse_index(&index, &span)?;
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(span));
}
match Self::unwrap_mutable(value.remove(0)) {
ConstrainedValue::Tuple(old) => {
if index > old.len() {
Err(StatementError::tuple_assign_index_bounds(index, old.len(), span))
} else {
Ok(vec![&mut old[index]])
}
}
_ => Err(StatementError::tuple_assign_index(span)),
}
}
ResolvedAssigneeAccess::Member(name) => {
if value.len() != 1 {
return Err(StatementError::array_assign_interior_index(span.clone()));
}
match Self::unwrap_mutable(value.remove(0)) {
ConstrainedValue::CircuitExpression(_variable, members) => {
// Modify the circuit variable in place
let matched_variable = members.iter_mut().find(|member| member.0 == name);
match matched_variable {
Some(member) => match &mut member.1 {
ConstrainedValue::Function(_circuit_identifier, function) => {
// Throw an error if we try to mutate a circuit function
Err(StatementError::immutable_circuit_function(
function.identifier.to_string(),
span.to_owned(),
))
}
ConstrainedValue::Static(_circuit_function) => {
// Throw an error if we try to mutate a static circuit function
Err(StatementError::immutable_circuit_function(
"static".into(),
span.to_owned(),
))
}
value => Ok(vec![value]),
},
None => {
// Throw an error if the circuit variable does not exist in the circuit
Err(StatementError::undefined_circuit_variable(
name.to_string(),
span.to_owned(),
))
}
}
}
// Throw an error if the circuit definition does not exist in the file
_ => Err(StatementError::undefined_circuit(name.to_string(), span.to_owned())),
}
}
}
}
pub fn get_mutable_assignee(
&mut self,
name: &str,

View File

@ -1,105 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Enforces a circuit variable assignment statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_ast::{Identifier, 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 mutate_circuit_variable<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,
indicator: &Boolean,
circuit_name: &str,
variable_name: Identifier,
mut new_value: ConstrainedValue<F, G>,
span: &Span,
) -> Result<ConstrainedValue<F, G>, StatementError> {
// Get the mutable circuit by name
match self.get_mutable_assignee(circuit_name, span)? {
ConstrainedValue::CircuitExpression(_variable, members) => {
// Modify the circuit variable in place
let matched_variable = members.iter_mut().find(|member| member.0 == variable_name);
match matched_variable {
Some(member) => match &member.1 {
ConstrainedValue::Function(_circuit_identifier, function) => {
// Throw an error if we try to mutate a circuit function
Err(StatementError::immutable_circuit_function(
function.identifier.to_string(),
span.to_owned(),
))
}
ConstrainedValue::Static(_circuit_function) => {
// Throw an error if we try to mutate a static circuit function
Err(StatementError::immutable_circuit_function(
"static".into(),
span.to_owned(),
))
}
value => {
// Check that the new value type == old value type
new_value.resolve_type(Some(value.to_type(span)?), span)?;
// Conditionally select the value if this branch is executed.
let mut selected_value = ConstrainedValue::conditionally_select(
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
indicator,
&new_value,
&member.1,
)
.map_err(|_| {
StatementError::select_fail(
new_value.to_string(),
member.1.to_string(),
span.to_owned(),
)
})?;
// Make sure the new value is still mutable
selected_value = ConstrainedValue::Mutable(Box::new(selected_value));
member.1 = selected_value.to_owned();
Ok(selected_value)
}
},
None => {
// Throw an error if the circuit variable does not exist in the circuit
Err(StatementError::undefined_circuit_variable(
variable_name.to_string(),
span.to_owned(),
))
}
}
}
// Throw an error if the circuit definition does not exist in the file
_ => Err(StatementError::undefined_circuit(
variable_name.to_string(),
span.to_owned(),
)),
}
}
}

View File

@ -16,17 +16,8 @@
//! Methods to enforce constraints on assign statements in a compiled Leo program.
pub mod array;
pub use self::array::*;
pub mod assign;
pub use self::assign::*;
pub mod assignee;
pub use self::assignee::*;
pub mod circuit_variable;
pub use self::circuit_variable::*;
pub mod tuple;
pub use self::tuple::*;

View File

@ -1,65 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! Enforces a tuple assignment statement in a compiled Leo program.
use crate::{errors::StatementError, parse_index, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_ast::{PositiveNumber, 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: &Boolean,
name: &str,
index: PositiveNumber,
mut new_value: ConstrainedValue<F, G>,
span: &Span,
) -> Result<(), StatementError> {
// Parse the index.
let index_usize = parse_index(&index, &span)?;
// Modify the single value of the tuple in place
match self.get_mutable_assignee(name, &span)? {
ConstrainedValue::Tuple(old) => {
new_value.resolve_type(Some(old[index_usize].to_type(&span)?), &span)?;
let selected_value = ConstrainedValue::conditionally_select(
cs.ns(|| format!("select {} {}:{}", new_value, span.line, span.start)),
indicator,
&new_value,
&old[index_usize],
)
.map_err(|_| {
StatementError::select_fail(new_value.to_string(), old[index_usize].to_string(), span.to_owned())
})?;
old[index_usize] = selected_value;
}
_ => return Err(StatementError::tuple_assign_index(span.to_owned())),
}
Ok(())
}
}

View File

@ -42,7 +42,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let mut results = Vec::with_capacity(block.statements.len());
// Evaluate statements. Only allow a single return argument to be returned.
for statement in block.statements.into_iter() {
let mut value = self.enforce_statement(
let value = self.enforce_statement(
cs,
file_scope,
function_scope,
@ -53,7 +53,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
mut_self,
)?;
results.append(&mut value);
results.extend(value);
}
Ok(results)

View File

@ -24,7 +24,7 @@ use crate::{
IndicatorAndConstrainedValue,
StatementResult,
};
use leo_ast::{ConditionalNestedOrEndStatement, ConditionalStatement, Span, Type};
use leo_ast::{ConditionalStatement, Type};
use snarkos_models::{
curves::{Field, PrimeField},
@ -50,11 +50,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
file_scope: &str,
function_scope: &str,
indicator: &Boolean,
statement: ConditionalStatement,
return_type: Option<Type>,
declared_circuit_reference: &str,
mut_self: bool,
span: &Span,
statement: ConditionalStatement,
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
let statement_string = statement.to_string();
@ -70,7 +69,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
statement.condition.clone(),
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(StatementError::conditional_boolean(value.to_string(), span.to_owned())),
value => {
return Err(StatementError::conditional_boolean(
value.to_string(),
statement.span.clone(),
));
}
};
// If outer_indicator && inner_indicator, then select branch 1
@ -81,11 +85,16 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
outer_indicator_string, inner_indicator_string
);
let branch_1_indicator = Boolean::and(
&mut cs.ns(|| format!("branch 1 {} {}:{}", statement_string, span.line, span.start)),
&mut cs.ns(|| {
format!(
"branch 1 {} {}:{}",
statement_string, &statement.span.line, &statement.span.start
)
}),
outer_indicator,
&inner_indicator,
)
.map_err(|_| StatementError::indicator_calculation(branch_1_name, span.to_owned()))?;
.map_err(|_| StatementError::indicator_calculation(branch_1_name, statement.span.clone()))?;
let mut results = vec![];
@ -110,38 +119,26 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
"branch indicator 2 {} && {}",
outer_indicator_string, inner_indicator_string
);
let span = statement.span.clone();
let branch_2_indicator = Boolean::and(
&mut cs.ns(|| format!("branch 2 {} {}:{}", statement_string, span.line, span.start)),
&mut cs.ns(|| format!("branch 2 {} {}:{}", statement_string, &span.line, &span.start)),
&outer_indicator,
&inner_indicator,
)
.map_err(|_| StatementError::indicator_calculation(branch_2_name, span.to_owned()))?;
.map_err(|_| StatementError::indicator_calculation(branch_2_name, span.clone()))?;
// Evaluate branch 2
let mut branch_2_result = match statement.next {
Some(next) => match next {
ConditionalNestedOrEndStatement::Nested(nested) => self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
&branch_2_indicator,
*nested,
return_type,
declared_circuit_reference,
mut_self,
span,
)?,
ConditionalNestedOrEndStatement::End(block) => self.evaluate_block(
cs,
file_scope,
function_scope,
&branch_2_indicator,
block,
return_type,
declared_circuit_reference,
mut_self,
)?,
},
Some(next) => self.enforce_statement(
cs,
file_scope,
function_scope,
&branch_2_indicator,
*next,
return_type,
declared_circuit_reference,
mut_self,
)?,
None => vec![],
};

View File

@ -17,7 +17,7 @@
//! Enforces a definition statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, ConstrainedValue, GroupType};
use leo_ast::{Declare, Expression, Span, VariableName, Variables};
use leo_ast::{Declare, DefinitionStatement, Span, VariableName};
use snarkos_models::{
curves::{Field, PrimeField},
@ -53,19 +53,19 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
function_scope: &str,
is_constant: bool,
variables: Variables,
variable_names: Vec<VariableName>,
values: Vec<ConstrainedValue<F, G>>,
span: &Span,
) -> Result<(), StatementError> {
if values.len() != variables.names.len() {
if values.len() != variable_names.len() {
return Err(StatementError::invalid_number_of_definitions(
values.len(),
variables.names.len(),
variable_names.len(),
span.to_owned(),
));
}
for (variable, value) in variables.names.into_iter().zip(values.into_iter()) {
for (variable, value) in variable_names.into_iter().zip(values.into_iter()) {
self.enforce_single_definition(cs, function_scope, is_constant, variable, value, span)?;
}
@ -78,33 +78,37 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: &str,
function_scope: &str,
declare: Declare,
variables: Variables,
expression: Expression,
span: &Span,
statement: DefinitionStatement,
) -> Result<(), StatementError> {
let num_variables = variables.names.len();
let is_constant = match declare {
let num_variables = statement.variable_names.len();
let is_constant = match statement.declaration_type {
Declare::Let => false,
Declare::Const => true,
};
let expression =
self.enforce_expression(cs, file_scope, function_scope, variables.type_.clone(), expression)?;
self.enforce_expression(cs, file_scope, function_scope, statement.type_.clone(), statement.value)?;
if num_variables == 1 {
// Define a single variable with a single value
let variable = variables.names[0].clone();
let variable = statement.variable_names[0].clone();
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, span)
self.enforce_single_definition(cs, function_scope, is_constant, variable, expression, &statement.span)
} else {
// Define multiple variables for an expression that returns multiple results (multiple definition)
let values = match expression {
// ConstrainedValue::Return(values) => values,
ConstrainedValue::Tuple(values) => values,
value => return Err(StatementError::multiple_definition(value.to_string(), span.to_owned())),
value => return Err(StatementError::multiple_definition(value.to_string(), statement.span)),
};
self.enforce_multiple_definition(cs, function_scope, is_constant, variables, values, span)
self.enforce_multiple_definition(
cs,
function_scope,
is_constant,
statement.variable_names,
values,
&statement.span,
)
}
}
}

View File

@ -25,7 +25,7 @@ use crate::{
Integer,
StatementResult,
};
use leo_ast::{Block, Expression, Identifier, Span, Type};
use leo_ast::{IterationStatement, Type};
use snarkos_models::{
curves::{Field, PrimeField},
@ -43,25 +43,22 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
file_scope: &str,
function_scope: &str,
indicator: &Boolean,
index: Identifier,
start: Expression,
stop: Expression,
block: Block,
return_type: Option<Type>,
declared_circuit_reference: &str,
mut_self: bool,
span: &Span,
statement: IterationStatement,
) -> StatementResult<Vec<IndicatorAndConstrainedValue<F, G>>> {
let mut results = vec![];
let from = self.enforce_index(cs, file_scope, function_scope, start, span)?;
let to = self.enforce_index(cs, file_scope, function_scope, stop, span)?;
let from = self.enforce_index(cs, file_scope, function_scope, statement.start, &statement.span)?;
let to = self.enforce_index(cs, file_scope, function_scope, statement.stop, &statement.span)?;
let span = statement.span.clone();
for i in from..to {
// Store index in current function scope.
// For loop scope is not implemented.
let index_name = new_scope(function_scope, &index.name);
let index_name = new_scope(function_scope, &statement.variable.name);
self.store(
index_name,
@ -69,18 +66,18 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
);
// Evaluate statements and possibly return early
let mut result = self.evaluate_block(
&mut cs.ns(|| format!("for loop iteration {} {}:{}", i, span.line, span.start)),
let result = self.evaluate_block(
&mut cs.ns(|| format!("for loop iteration {} {}:{}", i, &span.line, &span.start)),
file_scope,
function_scope,
indicator,
block.clone(),
statement.block.clone(),
return_type.clone(),
declared_circuit_reference,
mut_self,
)?;
results.append(&mut result);
results.extend(result);
}
Ok(results)

View File

@ -17,7 +17,7 @@
//! Enforces a return statement in a compiled Leo program.
use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType};
use leo_ast::{Expression, Span, Type};
use leo_ast::{ReturnStatement, Span, Type};
use snarkos_models::{
curves::{Field, PrimeField},
@ -43,15 +43,21 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: &str,
function_scope: &str,
expression: Expression,
return_type: Option<Type>,
span: &Span,
statement: ReturnStatement,
) -> Result<ConstrainedValue<F, G>, StatementError> {
let result = self.enforce_operand(cs, file_scope, function_scope, return_type.clone(), expression, span)?;
let result = self.enforce_operand(
cs,
file_scope,
function_scope,
return_type.clone(),
statement.expression,
&statement.span,
)?;
// Make sure we return the correct type.
if let Some(expected) = return_type {
check_return_type(&expected, &result.to_type(span)?, span)?;
check_return_type(&expected, &result.to_type(&statement.span)?, &statement.span)?;
}
Ok(result)

View File

@ -50,26 +50,18 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
let mut results = vec![];
match statement {
Statement::Return(expression, span) => {
Statement::Return(statement) => {
let return_value = (
indicator.to_owned(),
self.enforce_return_statement(cs, file_scope, function_scope, expression, return_type, &span)?,
*indicator,
self.enforce_return_statement(cs, file_scope, function_scope, return_type, statement)?,
);
results.push(return_value);
}
Statement::Definition(declare, variables, expressions, span) => {
self.enforce_definition_statement(
cs,
file_scope,
function_scope,
declare,
variables,
expressions,
&span,
)?;
Statement::Definition(statement) => {
self.enforce_definition_statement(cs, file_scope, function_scope, statement)?;
}
Statement::Assign(variable, expression, span) => {
Statement::Assign(statement) => {
self.enforce_assign_statement(
cs,
file_scope,
@ -77,60 +69,67 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
declared_circuit_reference,
indicator,
mut_self,
variable,
expression,
&span,
statement,
)?;
}
Statement::Conditional(statement, span) => {
let mut result = self.enforce_conditional_statement(
Statement::Conditional(statement) => {
let result = self.enforce_conditional_statement(
cs,
file_scope,
function_scope,
indicator,
return_type,
declared_circuit_reference,
mut_self,
statement,
)?;
results.extend(result);
}
Statement::Iteration(statement) => {
let result = self.enforce_iteration_statement(
cs,
file_scope,
function_scope,
indicator,
return_type,
declared_circuit_reference,
mut_self,
statement,
)?;
results.extend(result);
}
Statement::Console(statement) => {
self.evaluate_console_function_call(cs, file_scope, function_scope, indicator, statement)?;
}
Statement::Expression(statement) => {
let expression_string = statement.expression.to_string();
let value = self.enforce_expression(cs, file_scope, function_scope, None, statement.expression)?;
// handle empty return value cases
match &value {
ConstrainedValue::Tuple(values) => {
if !values.is_empty() {
results.push((*indicator, value));
}
}
_ => return Err(StatementError::unassigned(expression_string, statement.span)),
}
}
Statement::Block(statement) => {
let span = statement.span.clone();
let result = self.evaluate_block(
&mut cs.ns(|| format!("block {}:{}", &span.line, &span.start)),
file_scope,
function_scope,
indicator,
statement,
return_type,
declared_circuit_reference,
mut_self,
&span,
)?;
results.append(&mut result);
}
Statement::Iteration(index, start_stop, block, span) => {
let mut result = self.enforce_iteration_statement(
cs,
file_scope,
function_scope,
indicator,
index,
start_stop.0,
start_stop.1,
block,
return_type,
declared_circuit_reference,
mut_self,
&span,
)?;
results.append(&mut result);
}
Statement::Console(console) => {
self.evaluate_console_function_call(cs, file_scope, function_scope, indicator, console)?;
}
Statement::Expression(expression, span) => {
let expression_string = expression.to_string();
let value = self.enforce_expression(cs, file_scope, function_scope, None, expression)?;
// Handle empty return value cases.
match &value {
ConstrainedValue::Tuple(values) => {
if !values.is_empty() {
results.push((*indicator, value));
}
}
_ => return Err(StatementError::unassigned(expression_string, span)),
}
results.extend(result);
}
};

View File

@ -62,7 +62,7 @@ pub enum ConstrainedValue<F: Field + PrimeField, G: GroupType<F>> {
CircuitExpression(Identifier, Vec<ConstrainedCircuitMember<F, G>>),
// Functions
Function(Option<Identifier>, Function), // (optional circuit identifier, function definition)
Function(Option<Identifier>, Box<Function>), // (optional circuit identifier, function definition)
// Modifiers
Mutable(Box<ConstrainedValue<F, G>>),
@ -214,7 +214,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
}
}
Ok((outer_scope, function))
Ok((outer_scope, *function))
}
ConstrainedValue::Import(import_scope, function) => function.extract_function(&import_scope, span),
value => Err(ExpressionError::undefined_function(value.to_string(), span.to_owned())),

View File

@ -0,0 +1,9 @@
// Adding the `mut` keyword makes an array variable mutable.
function main() {
let mut a = [1u32, 2u32, 3u32];
a[0u32..2u32] = [4u32, 5u32];
console.assert(a[0] == 4u32);
console.assert(a[1] == 5u32);
console.assert(a[2] == 3u32);
}

View File

@ -0,0 +1,8 @@
// Adding the `mut` keyword makes an array variable mutable.
function main() {
let mut a = [(1u32, 2u32)];
a[0u32].1 = 3u32;
console.assert(a[0u32].0 == 1u32);
console.assert(a[0u32].1 == 3u32);
}

View File

@ -73,6 +73,22 @@ fn test_array_mut() {
assert_satisfied(program);
}
#[test]
fn test_array_tuple_mut() {
let bytes = include_str!("array_tuple_mut.leo");
let program = parse_program(bytes).unwrap();
assert_satisfied(program);
}
#[test]
fn test_array_splice_mut() {
let bytes = include_str!("array_splice_mut.leo");
let program = parse_program(bytes).unwrap();
assert_satisfied(program);
}
#[test]
fn test_circuit() {
let program_string = include_str!("circuit.leo");

View File

@ -0,0 +1,9 @@
function main() {
let mut x = 4u32;
{
x = 5u32;
}
console.assert(x == 5u32);
}

View File

@ -64,3 +64,11 @@ fn test_num_returns_fail() {
expect_type_inference_error(error);
}
#[test]
fn test_block() {
let bytes = include_str!("block.leo");
let program = parse_program(bytes).unwrap();
assert_satisfied(program);
}

View File

@ -29,6 +29,7 @@ use leo_ast::{
Identifier,
IntegerType,
PositiveNumber,
ReturnStatement,
Span,
Statement,
Type,
@ -110,8 +111,8 @@ impl CoreCircuit for Blake2sCircuit {
}]),
)),
block: Block {
statements: vec![Statement::Return(
Expression::Call(CallExpression {
statements: vec![Statement::Return(ReturnStatement {
expression: Expression::Call(CallExpression {
function: Box::new(Expression::Identifier(Identifier::new_with_span(&Self::name(), &span))),
arguments: vec![
Expression::Identifier(Identifier::new_with_span("seed", &span)),
@ -119,8 +120,9 @@ impl CoreCircuit for Blake2sCircuit {
],
span: span.clone(),
}),
span.clone(),
)],
span: span.clone(),
})],
span: span.clone(),
},
span,
})],

View File

@ -1,37 +0,0 @@
// Copyright (C) 2019-2020 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
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::formatted_parameter))]
pub struct FormattedParameter<'ast> {
pub expression: Expression<'ast>,
#[pest_ast(outer())]
#[serde(with = "SpanDef")]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for FormattedParameter<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.expression)
}
}

View File

@ -16,7 +16,8 @@
use crate::{
ast::{span_into_string, Rule},
console::{FormattedContainer, FormattedParameter},
console::FormattedContainer,
expressions::Expression,
SpanDef,
};
@ -31,7 +32,7 @@ pub struct FormattedString<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub string: String,
pub containers: Vec<FormattedContainer<'ast>>,
pub parameters: Vec<FormattedParameter<'ast>>,
pub parameters: Vec<Expression<'ast>>,
#[pest_ast(outer())]
#[serde(with = "SpanDef")]
pub span: Span<'ast>,

View File

@ -38,8 +38,5 @@ pub use console_log::*;
pub mod formatted_container;
pub use formatted_container::*;
pub mod formatted_parameter;
pub use formatted_parameter::*;
pub mod formatted_string;
pub use formatted_string::*;

View File

@ -390,6 +390,7 @@ statement = {
| statement_definition
| statement_assign
| statement_expression
| block
) ~ NEWLINE*
}
@ -504,15 +505,12 @@ formatted_string = {
"\""
~ (!"\"" ~ (formatted_container | ANY))*
~ "\""
~ ("," ~ formatted_parameter)*
~ ("," ~ expression)*
}
// Declared in console/formatted_container.rs
formatted_container = { "{" ~ "}"}
// Declared in console/formatted_parameter.rs
formatted_parameter = { expression }
/// Annotations
// Declared in annotations/annotation.rs

View File

@ -30,6 +30,7 @@ pub enum Statement<'ast> {
Iteration(ForStatement<'ast>),
Console(ConsoleFunctionCall<'ast>),
Expression(ExpressionStatement<'ast>),
Block(Block<'ast>),
}
impl<'ast> fmt::Display for Statement<'ast> {
@ -42,6 +43,7 @@ impl<'ast> fmt::Display for Statement<'ast> {
Statement::Iteration(ref statement) => write!(f, "{}", statement),
Statement::Console(ref statement) => write!(f, "{}", statement),
Statement::Expression(ref statement) => write!(f, "{}", statement.expression),
Statement::Block(ref block) => write!(f, "{}", block),
}
}
}

View File

@ -17,24 +17,19 @@
use crate::{FrameError, Scope, TypeAssertion};
use leo_ast::{
expression::*,
statements::*,
ArrayDimensions,
Assignee,
AssigneeAccess,
Block,
CircuitVariableDefinition,
ConditionalNestedOrEndStatement,
ConditionalStatement,
Declare,
Expression,
Function,
Identifier,
IntegerType,
PositiveNumber,
RangeOrExpression,
Span,
SpreadOrExpression,
Statement,
Variables,
};
use leo_symbol_table::{CircuitType, FunctionType, SymbolTable, Type, TypeVariable};
@ -244,24 +239,21 @@ impl Frame {
///
fn parse_statement(&mut self, statement: &Statement) -> Result<(), FrameError> {
match statement {
Statement::Return(expression, span) => self.parse_return(expression, span),
Statement::Definition(declare, variables, expression, span) => {
self.parse_definition(declare, variables, expression, span)
}
Statement::Assign(assignee, expression, span) => self.parse_assign(assignee, expression, span),
Statement::Conditional(conditional, span) => self.parse_statement_conditional(conditional, span),
Statement::Iteration(identifier, from_to, block, span) => {
self.parse_iteration(identifier, from_to, block, span)
}
Statement::Expression(expression, span) => self.parse_statement_expression(expression, span),
Statement::Return(statement) => self.parse_return(statement),
Statement::Definition(statement) => self.parse_definition(statement),
Statement::Assign(statement) => self.parse_assign(statement),
Statement::Conditional(statement) => self.parse_statement_conditional(statement),
Statement::Iteration(statement) => self.parse_iteration(statement),
Statement::Expression(statement) => self.parse_statement_expression(statement),
Statement::Console(_console_call) => Ok(()), // Console function calls do not generate type assertions.
Statement::Block(statement) => self.parse_block(statement),
}
}
///
/// Collects `TypeAssertion` predicates from a return statement.
///
fn parse_return(&mut self, expression: &Expression, span: &Span) -> Result<(), FrameError> {
fn parse_return(&mut self, statement: &ReturnStatement) -> Result<(), FrameError> {
// Get the function output type.
let output_type = &self.function_type.output.type_;
@ -269,10 +261,10 @@ impl Frame {
let left = output_type.clone();
// Create the right hand side from the statement return expression.
let right = self.parse_expression(expression)?;
let right = self.parse_expression(&statement.expression)?;
// Create a new type assertion for the statement return.
self.assert_equal(left, right, span);
self.assert_equal(left, right, &statement.span);
Ok(())
}
@ -280,58 +272,52 @@ impl Frame {
///
/// Collects `Type Assertion` predicates from a definition statement.
///
fn parse_definition(
&mut self,
_declare: &Declare,
variables: &Variables,
expression: &Expression,
span: &Span,
) -> Result<(), FrameError> {
fn parse_definition(&mut self, statement: &DefinitionStatement) -> Result<(), FrameError> {
// Parse the definition expression.
let actual_type = self.parse_expression(expression)?;
let actual_type = self.parse_expression(&statement.value)?;
// Check if an explicit type is given.
if let Some(type_) = variables.type_.clone() {
if let Some(type_) = statement.type_.clone() {
// Check the expected type.
let expected_type = match self.self_type {
Some(ref circuit_type) => Type::new_from_circuit(
&self.user_defined_types,
type_,
circuit_type.identifier.clone(),
span.clone(),
statement.span.clone(),
)
.unwrap(),
None => Type::new(&self.user_defined_types, type_, span.clone()).unwrap(),
None => Type::new(&self.user_defined_types, type_, statement.span.clone()).unwrap(),
};
// Assert that the expected type is equal to the actual type.
self.assert_equal(expected_type, actual_type.clone(), span)
self.assert_equal(expected_type, actual_type.clone(), &statement.span)
}
// Check for multiple defined variables.
if variables.names.len() == 1 {
if statement.variable_names.len() == 1 {
// Insert variable into symbol table
let variable = variables.names[0].clone();
self.insert_variable(variable.identifier.name, actual_type, span)?;
let variable = statement.variable_names[0].clone();
self.insert_variable(variable.identifier.name, actual_type, &statement.span)?;
} else {
// Expect a tuple type.
let types = match actual_type {
Type::Tuple(types) => types,
_ => return Err(FrameError::not_enough_values(span)),
_ => return Err(FrameError::not_enough_values(&statement.span)),
};
// Check number of variables == number of types.
if types.len() != variables.names.len() {
if types.len() != statement.variable_names.len() {
return Err(FrameError::invalid_number_of_values(
types.len(),
variables.names.len(),
span,
statement.variable_names.len(),
&statement.span,
));
}
// Insert variables into symbol table
for (variable, type_) in variables.names.iter().zip(types) {
self.insert_variable(variable.identifier.name.clone(), type_, span)?;
for (variable, type_) in statement.variable_names.iter().zip(types) {
self.insert_variable(variable.identifier.name.clone(), type_, &statement.span)?;
}
}
@ -341,15 +327,15 @@ impl Frame {
///
/// Asserts that the assignee's type is equal to the `Expression` type.
///
fn parse_assign(&mut self, assignee: &Assignee, expression: &Expression, span: &Span) -> Result<(), FrameError> {
fn parse_assign(&mut self, statement: &AssignStatement) -> Result<(), FrameError> {
// Parse assignee type.
let assignee_type = self.parse_assignee(assignee, span)?;
let assignee_type = self.parse_assignee(&statement.assignee, &statement.span)?;
// Parse expression type.
let expression_type = self.parse_expression(expression)?;
let expression_type = self.parse_expression(&statement.value)?;
// Assert that the assignee_type == expression_type.
self.assert_equal(assignee_type, expression_type, span);
self.assert_equal(assignee_type, expression_type, &statement.span);
Ok(())
}
@ -374,10 +360,8 @@ impl Frame {
// Iteratively evaluate assignee access types.
for access in &assignee.accesses {
let access_type = match access {
AssigneeAccess::Array(RangeOrExpression::Expression(index)) => {
self.parse_array_access(type_, index, span)
}
AssigneeAccess::Array(RangeOrExpression::Range(left, right)) => {
AssigneeAccess::ArrayIndex(index) => self.parse_array_access(type_, index, span),
AssigneeAccess::ArrayRange(left, right) => {
self.parse_array_range_access(type_, left.as_ref(), right.as_ref(), span)
}
AssigneeAccess::Tuple(index, _) => self.parse_tuple_access(type_, &index, span),
@ -393,7 +377,7 @@ impl Frame {
///
/// Collects `TypeAssertion` predicates from a block of statements.
///
fn parse_block(&mut self, block: &Block, _span: &Span) -> Result<(), FrameError> {
fn parse_block(&mut self, block: &Block) -> Result<(), FrameError> {
// Push new scope.
let scope = Scope::new(self.scopes.last().cloned());
self.push_scope(scope);
@ -414,80 +398,56 @@ impl Frame {
///
/// Creates a new scope for each code block in the conditional.
///
fn parse_statement_conditional(
&mut self,
conditional: &ConditionalStatement,
span: &Span,
) -> Result<(), FrameError> {
fn parse_statement_conditional(&mut self, conditional: &ConditionalStatement) -> Result<(), FrameError> {
// Parse the condition expression.
let condition = self.parse_expression(&conditional.condition)?;
// Assert that the condition is a boolean type.
let boolean_type = Type::Boolean;
self.assert_equal(boolean_type, condition, span);
self.assert_equal(boolean_type, condition, &conditional.span);
// Parse conditional statements.
self.parse_block(&conditional.block, span)?;
self.parse_block(&conditional.block)?;
// Parse conditional or end.
if let Some(cond_or_end) = &conditional.next {
self.parse_conditional_nested_or_end(cond_or_end, span)?;
self.parse_statement(cond_or_end)?;
}
Ok(())
}
///
/// Collects `TypeAssertion` predicates from a conditional statement.
///
fn parse_conditional_nested_or_end(
&mut self,
cond_or_end: &ConditionalNestedOrEndStatement,
span: &Span,
) -> Result<(), FrameError> {
match cond_or_end {
ConditionalNestedOrEndStatement::Nested(nested) => self.parse_statement_conditional(nested, span),
ConditionalNestedOrEndStatement::End(statements) => self.parse_block(statements, span),
}
}
///
/// Collects `TypeAssertion` predicates from an iteration statement.
///
fn parse_iteration(
&mut self,
identifier: &Identifier,
from_to: &(Expression, Expression),
statements: &Block,
span: &Span,
) -> Result<(), FrameError> {
fn parse_iteration(&mut self, statement: &IterationStatement) -> Result<(), FrameError> {
// Insert variable into symbol table with u32 type.
let u32_type = Type::IntegerType(IntegerType::U32);
let _expect_none = self.insert_variable(identifier.name.to_owned(), u32_type.clone(), span);
let _expect_none = self.insert_variable(statement.variable.name.to_owned(), u32_type.clone(), &statement.span);
// Parse `from` and `to` expressions.
let from_type = self.parse_expression(&from_to.0)?;
let to_type = self.parse_expression(&from_to.1)?;
let from_type = self.parse_expression(&statement.start)?;
let to_type = self.parse_expression(&statement.stop)?;
// Assert `from` and `to` types are a u32 or implicit.
self.assert_equal(u32_type.clone(), from_type, span);
self.assert_equal(u32_type, to_type, span);
self.assert_equal(u32_type.clone(), from_type, &statement.span);
self.assert_equal(u32_type, to_type, &statement.span);
// Parse block of statements.
self.parse_block(statements, span)
self.parse_block(&statement.block)
}
///
/// Asserts that the statement `UnresolvedExpression` returns an empty tuple.
///
fn parse_statement_expression(&mut self, expression: &Expression, span: &Span) -> Result<(), FrameError> {
fn parse_statement_expression(&mut self, statement: &ExpressionStatement) -> Result<(), FrameError> {
// Create empty tuple type.
let expected_type = Type::Tuple(Vec::new());
// Parse the actual type of the expression.
let actual_type = self.parse_expression(expression)?;
let actual_type = self.parse_expression(&statement.expression)?;
self.assert_equal(expected_type, actual_type, span);
self.assert_equal(expected_type, actual_type, &statement.span);
Ok(())
}