Merge pull request #793 from AleoHQ/feature/canonicalization-before-asg

Feature/canonicalization before asg
This commit is contained in:
Collin Chin 2021-03-25 16:41:53 -07:00 committed by GitHub
commit 3e3a63d97b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2744 additions and 90 deletions

72
Cargo.lock generated
View File

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "adler"
version = "1.0.2"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "bedc89c5c7b5550ffb9372eb5c5ffc7f9f705cc3f4a128bd4669b9745f555093"
[[package]]
name = "adler32"
@ -188,7 +188,7 @@ dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
"generic-array 0.12.3",
]
[[package]]
@ -577,7 +577,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
"generic-array 0.12.3",
]
[[package]]
@ -846,9 +846,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.12.4"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
"typenum",
]
@ -908,9 +908,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "h2"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78"
checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5"
dependencies = [
"bytes",
"fnv",
@ -923,6 +923,7 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
"tracing-futures",
]
[[package]]
@ -1162,9 +1163,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.48"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78"
checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
dependencies = [
"wasm-bindgen",
]
@ -1217,6 +1218,7 @@ dependencies = [
name = "leo-ast"
version = "1.2.3"
dependencies = [
"anyhow",
"criterion",
"indexmap",
"leo-input",
@ -1875,9 +1877,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
version = "0.2.6"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
[[package]]
name = "pin-utils"
@ -2382,9 +2384,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.64"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
checksum = "43535db9747a4ba938c0ce0a98cc631a46ebf943c9e1d604e091df6007620bf6"
dependencies = [
"itoa",
"ryu",
@ -2935,6 +2937,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project",
"tracing",
]
[[package]]
name = "tracing-log"
version = "0.1.2"
@ -3124,9 +3136,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.71"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7"
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [
"cfg-if 1.0.0",
"serde",
@ -3136,9 +3148,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.71"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8"
checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
dependencies = [
"bumpalo",
"lazy_static",
@ -3151,9 +3163,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.21"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab"
checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@ -3163,9 +3175,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.71"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b"
checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
dependencies = [
"quote 1.0.9",
"wasm-bindgen-macro-support",
@ -3173,9 +3185,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.71"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.9",
@ -3186,15 +3198,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.71"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1"
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
[[package]]
name = "web-sys"
version = "0.3.48"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b"
checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -3273,9 +3285,9 @@ dependencies = [
[[package]]
name = "zip"
version = "0.5.11"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8264fcea9b7a036a4a5103d7153e988dbc2ebbafb34f68a3c2d404b6b82d74b6"
checksum = "5a8977234acab718eb2820494b2f96cbb16004c19dddf88b7445b27381450997"
dependencies = [
"byteorder",
"bzip2",

View File

@ -22,6 +22,9 @@
//! A new [`Asg`] type can be created from an [`Ast`].
//! Converting to an [`Asg`] provides greater type safety by canonicalizing and checking program types.
#![allow(clippy::from_over_into)]
#![allow(clippy::result_unit_err)]
#[macro_use]
extern crate thiserror;

View File

@ -68,35 +68,50 @@ impl<'a> Circuit<'a> {
let mut members = circuit.members.borrow_mut();
for member in value.members.iter() {
match member {
leo_ast::CircuitMember::CircuitVariable(name, type_) => {
if members.contains_key(&name.name) {
return Err(AsgConvertError::redefined_circuit_member(
&value.circuit_name.name,
&name.name,
&name.span,
));
}
members.insert(
name.name.clone(),
CircuitMember::Variable(new_scope.resolve_ast_type(type_)?),
);
if let leo_ast::CircuitMember::CircuitVariable(name, type_) = member {
if members.contains_key(&name.name) {
return Err(AsgConvertError::redefined_circuit_member(
&value.circuit_name.name,
&name.name,
&name.span,
));
}
leo_ast::CircuitMember::CircuitFunction(function) => {
if members.contains_key(&function.identifier.name) {
return Err(AsgConvertError::redefined_circuit_member(
&value.circuit_name.name,
&function.identifier.name,
&function.identifier.span,
));
}
let asg_function = Function::init(new_scope, function)?;
asg_function.circuit.replace(Some(circuit));
if asg_function.is_test() {
return Err(AsgConvertError::circuit_test_function(&function.identifier.span));
}
members.insert(function.identifier.name.clone(), CircuitMember::Function(asg_function));
members.insert(
name.name.clone(),
CircuitMember::Variable(new_scope.resolve_ast_type(type_)?),
);
}
}
Ok(circuit)
}
pub(super) fn init_member(
scope: &'a Scope<'a>,
value: &leo_ast::Circuit,
) -> Result<&'a Circuit<'a>, AsgConvertError> {
let new_scope = scope.make_subscope();
let circuits = scope.circuits.borrow();
let circuit = circuits.get(&value.circuit_name.name).unwrap();
new_scope.circuit_self.replace(Some(circuit));
let mut members = circuit.members.borrow_mut();
for member in value.members.iter() {
if let leo_ast::CircuitMember::CircuitFunction(function) = member {
if members.contains_key(&function.identifier.name) {
return Err(AsgConvertError::redefined_circuit_member(
&value.circuit_name.name,
&function.identifier.name,
&function.identifier.span,
));
}
let asg_function = Function::init(new_scope, function)?;
asg_function.circuit.replace(Some(circuit));
if asg_function.is_test() {
return Err(AsgConvertError::circuit_test_function(&function.identifier.span));
}
members.insert(function.identifier.name.clone(), CircuitMember::Function(asg_function));
}
}

View File

@ -243,6 +243,14 @@ impl<'a> Program<'a> {
scope.circuits.borrow_mut().insert(name.name.clone(), asg_circuit);
}
// Second pass for circuit members.
for (name, circuit) in program.circuits.iter() {
assert_eq!(name.name, circuit.circuit_name.name);
let asg_circuit = Circuit::init_member(scope, circuit)?;
scope.circuits.borrow_mut().insert(name.name.clone(), asg_circuit);
}
for (name, function) in program.functions.iter() {
assert_eq!(name.name, function.identifier.name);
let function = Function::init(scope, function)?;

View File

@ -35,6 +35,9 @@ features = ["derive", "rc"]
[dependencies.serde_json]
version = "1.0"
[dependencies.anyhow]
version = "1.0"
[dependencies.thiserror]
version = "1.0"

View File

@ -19,6 +19,10 @@
//! This module contains the [`Ast`] type, a wrapper around the [`Program`] type.
//! The [`Ast`] type is intended to be parsed and modified by different passes
//! of the Leo compiler. The Leo compiler can generate a set of R1CS constraints from any [`Ast`].
#[macro_use]
extern crate thiserror;
pub mod annotation;
pub use self::annotation::*;
@ -49,6 +53,9 @@ pub use self::input::*;
pub mod program;
pub use self::program::*;
pub mod reducer;
pub use self::reducer::*;
pub mod statements;
pub use self::statements::*;
@ -75,6 +82,12 @@ impl Ast {
Self { ast: program }
}
/// Mutates the program ast by preforming canonicalization on it.
pub fn canonicalize(&mut self) -> Result<(), CanonicalizeError> {
self.ast = ReconstructingDirector::new(Canonicalizer::default()).reduce_program(self.as_repr())?;
Ok(())
}
/// Returns a reference to the inner program AST representation.
pub fn as_repr(&self) -> &Program {
&self.ast

View File

@ -0,0 +1,554 @@
// Copyright (C) 2019-2021 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::*;
/// Replace Self when it is in a enclosing circuit type.
/// Error when Self is outside an enclosing circuit type.
/// Tuple array types and expressions expand to nested arrays.
/// Tuple array types and expressions error if a size of 0 is given.anyhow
/// Compound operators become simple assignments.
/// Functions missing output type return a empty tuple.
pub struct Canonicalizer {
// If we are in a circuit keep track of the circuit name.
circuit_name: Option<Identifier>,
}
impl Canonicalizer {
pub fn default() -> Self {
Self { circuit_name: None }
}
fn is_self_type(&mut self, type_option: Option<&Type>) -> bool {
matches!(type_option, Some(Type::SelfType))
}
fn canonicalize_expression(&mut self, expression: &Expression) -> Expression {
match expression {
Expression::Unary(unary) => {
let inner = Box::new(self.canonicalize_expression(&unary.inner));
return Expression::Unary(UnaryExpression {
inner,
op: unary.op.clone(),
span: unary.span.clone(),
});
}
Expression::Binary(binary) => {
let left = Box::new(self.canonicalize_expression(&binary.left));
let right = Box::new(self.canonicalize_expression(&binary.right));
return Expression::Binary(BinaryExpression {
left,
right,
op: binary.op.clone(),
span: binary.span.clone(),
});
}
Expression::Ternary(ternary) => {
let condition = Box::new(self.canonicalize_expression(&ternary.condition));
let if_true = Box::new(self.canonicalize_expression(&ternary.if_true));
let if_false = Box::new(self.canonicalize_expression(&ternary.if_false));
return Expression::Ternary(TernaryExpression {
condition,
if_true,
if_false,
span: ternary.span.clone(),
});
}
Expression::Cast(cast) => {
let inner = Box::new(self.canonicalize_expression(&cast.inner));
let mut target_type = cast.target_type.clone();
if matches!(target_type, Type::SelfType) {
target_type = Type::Circuit(self.circuit_name.as_ref().unwrap().clone());
}
return Expression::Cast(CastExpression {
inner,
target_type,
span: cast.span.clone(),
});
}
Expression::ArrayInline(array_inline) => {
let elements = array_inline
.elements
.iter()
.map(|element| match element {
SpreadOrExpression::Expression(expression) => {
SpreadOrExpression::Expression(self.canonicalize_expression(expression))
}
SpreadOrExpression::Spread(expression) => {
SpreadOrExpression::Spread(self.canonicalize_expression(expression))
}
})
.collect();
return Expression::ArrayInline(ArrayInlineExpression {
elements,
span: array_inline.span.clone(),
});
}
Expression::ArrayInit(array_init) => {
let element = Box::new(self.canonicalize_expression(&array_init.element));
return Expression::ArrayInit(ArrayInitExpression {
dimensions: array_init.dimensions.clone(),
element,
span: array_init.span.clone(),
});
}
Expression::ArrayAccess(array_access) => {
let array = Box::new(self.canonicalize_expression(&array_access.array));
let index = Box::new(self.canonicalize_expression(&array_access.index));
return Expression::ArrayAccess(ArrayAccessExpression {
array,
index,
span: array_access.span.clone(),
});
}
Expression::ArrayRangeAccess(array_range_access) => {
let array = Box::new(self.canonicalize_expression(&array_range_access.array));
let left = array_range_access
.left
.as_ref()
.map(|left| Box::new(self.canonicalize_expression(left)));
let right = array_range_access
.right
.as_ref()
.map(|right| Box::new(self.canonicalize_expression(right)));
return Expression::ArrayRangeAccess(ArrayRangeAccessExpression {
array,
left,
right,
span: array_range_access.span.clone(),
});
}
Expression::TupleInit(tuple_init) => {
let elements = tuple_init
.elements
.iter()
.map(|element| self.canonicalize_expression(element))
.collect();
return Expression::TupleInit(TupleInitExpression {
elements,
span: tuple_init.span.clone(),
});
}
Expression::TupleAccess(tuple_access) => {
let tuple = Box::new(self.canonicalize_expression(&tuple_access.tuple));
return Expression::TupleAccess(TupleAccessExpression {
tuple,
index: tuple_access.index.clone(),
span: tuple_access.span.clone(),
});
}
Expression::CircuitInit(circuit_init) => {
let mut name = circuit_init.name.clone();
if name.name == *"Self" {
name = self.circuit_name.as_ref().unwrap().clone();
}
return Expression::CircuitInit(CircuitInitExpression {
name,
members: circuit_init.members.clone(),
span: circuit_init.span.clone(),
});
}
Expression::CircuitMemberAccess(circuit_member_access) => {
return Expression::CircuitMemberAccess(CircuitMemberAccessExpression {
circuit: Box::new(self.canonicalize_expression(&circuit_member_access.circuit)),
name: circuit_member_access.name.clone(),
span: circuit_member_access.span.clone(),
});
}
Expression::CircuitStaticFunctionAccess(circuit_static_func_access) => {
return Expression::CircuitStaticFunctionAccess(CircuitStaticFunctionAccessExpression {
circuit: Box::new(self.canonicalize_expression(&circuit_static_func_access.circuit)),
name: circuit_static_func_access.name.clone(),
span: circuit_static_func_access.span.clone(),
});
}
Expression::Call(call) => {
return Expression::Call(CallExpression {
function: Box::new(self.canonicalize_expression(&call.function)),
arguments: call.arguments.clone(),
span: call.span.clone(),
});
}
_ => {}
}
expression.clone()
}
fn canonicalize_assignee_access(&mut self, access: &AssigneeAccess) -> AssigneeAccess {
match access {
AssigneeAccess::ArrayRange(left, right) => {
let left = left.as_ref().map(|left| self.canonicalize_expression(left));
let right = right.as_ref().map(|right| self.canonicalize_expression(right));
AssigneeAccess::ArrayRange(left, right)
}
AssigneeAccess::ArrayIndex(index) => AssigneeAccess::ArrayIndex(self.canonicalize_expression(&index)),
_ => access.clone(),
}
}
fn canonicalize_assignee(&mut self, assignee: &Assignee) -> Assignee {
let accesses = assignee
.accesses
.iter()
.map(|access| self.canonicalize_assignee_access(access))
.collect();
Assignee {
identifier: assignee.identifier.clone(),
accesses,
span: assignee.span.clone(),
}
}
fn canonicalize_block(&mut self, block: &Block) -> Block {
let statements = block
.statements
.iter()
.map(|block_statement| self.canonicalize_statement(&block_statement))
.collect();
Block {
statements,
span: block.span.clone(),
}
}
fn canonicalize_statement(&mut self, statement: &Statement) -> Statement {
match statement {
Statement::Return(return_statement) => {
let expression = self.canonicalize_expression(&return_statement.expression);
Statement::Return(ReturnStatement {
expression,
span: return_statement.span.clone(),
})
}
Statement::Definition(definition) => {
let value = self.canonicalize_expression(&definition.value);
let mut type_ = definition.type_.clone();
if self.is_self_type(type_.as_ref()) {
type_ = Some(Type::Circuit(self.circuit_name.as_ref().unwrap().clone()));
}
Statement::Definition(DefinitionStatement {
declaration_type: definition.declaration_type.clone(),
variable_names: definition.variable_names.clone(),
type_,
value,
span: definition.span.clone(),
})
}
Statement::Assign(assign) => {
let assignee = self.canonicalize_assignee(&assign.assignee);
let value = self.canonicalize_expression(&assign.value);
Statement::Assign(AssignStatement {
assignee,
value,
operation: assign.operation.clone(),
span: assign.span.clone(),
})
}
Statement::Conditional(conditional) => {
let condition = self.canonicalize_expression(&conditional.condition);
let block = self.canonicalize_block(&conditional.block);
let next = conditional
.next
.as_ref()
.map(|condition| Box::new(self.canonicalize_statement(condition)));
Statement::Conditional(ConditionalStatement {
condition,
block,
next,
span: conditional.span.clone(),
})
}
Statement::Iteration(iteration) => {
let start = self.canonicalize_expression(&iteration.start);
let stop = self.canonicalize_expression(&iteration.stop);
let block = self.canonicalize_block(&iteration.block);
Statement::Iteration(IterationStatement {
variable: iteration.variable.clone(),
start,
stop,
block,
span: iteration.span.clone(),
})
}
Statement::Console(console_function_call) => {
let function = match &console_function_call.function {
ConsoleFunction::Assert(expression) => {
ConsoleFunction::Assert(self.canonicalize_expression(expression))
}
ConsoleFunction::Debug(format) | ConsoleFunction::Error(format) | ConsoleFunction::Log(format) => {
let parameters = format
.parameters
.iter()
.map(|parameter| self.canonicalize_expression(parameter))
.collect();
let formatted = FormattedString {
parts: format.parts.clone(),
parameters,
span: format.span.clone(),
};
match &console_function_call.function {
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted),
ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted),
ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted),
_ => unimplemented!(), // impossible
}
}
};
Statement::Console(ConsoleStatement {
function,
span: console_function_call.span.clone(),
})
}
Statement::Expression(expression) => Statement::Expression(ExpressionStatement {
expression: self.canonicalize_expression(&expression.expression),
span: expression.span.clone(),
}),
Statement::Block(block) => Statement::Block(self.canonicalize_block(block)),
}
}
fn canonicalize_circuit_member(&mut self, circuit_member: &CircuitMember) -> CircuitMember {
match circuit_member {
CircuitMember::CircuitVariable(_, _) => {}
CircuitMember::CircuitFunction(function) => {
let input = function.input.clone();
let mut output = function.output.clone();
let block = self.canonicalize_block(&function.block);
if self.is_self_type(output.as_ref()) {
output = Some(Type::Circuit(self.circuit_name.as_ref().unwrap().clone()));
}
return CircuitMember::CircuitFunction(Function {
annotations: function.annotations.clone(),
identifier: function.identifier.clone(),
input,
output,
block,
span: function.span.clone(),
});
}
}
circuit_member.clone()
}
}
impl ReconstructingReducer for Canonicalizer {
fn reduce_type(
&mut self,
_type_: &Type,
new: Type,
in_circuit: bool,
span: &Span,
) -> Result<Type, CanonicalizeError> {
match new {
Type::Array(type_, mut dimensions) => {
if dimensions.is_zero() {
return Err(CanonicalizeError::invalid_array_dimension_size(span));
}
let mut next = Type::Array(type_, ArrayDimensions(vec![dimensions.remove_last().unwrap()]));
let mut array = next.clone();
loop {
if dimensions.is_empty() {
break;
}
array = Type::Array(Box::new(next), ArrayDimensions(vec![dimensions.remove_last().unwrap()]));
next = array.clone();
}
Ok(array)
}
Type::SelfType if !in_circuit => Err(CanonicalizeError::big_self_outside_of_circuit(span)),
_ => Ok(new.clone()),
}
}
fn reduce_array_init(
&mut self,
array_init: &ArrayInitExpression,
element: Expression,
_in_circuit: bool,
) -> Result<ArrayInitExpression, CanonicalizeError> {
if array_init.dimensions.is_zero() {
return Err(CanonicalizeError::invalid_array_dimension_size(&array_init.span));
}
let element = Box::new(element);
if array_init.dimensions.0.len() == 1 {
return Ok(ArrayInitExpression {
element,
dimensions: array_init.dimensions.clone(),
span: array_init.span.clone(),
});
}
let mut dimensions = array_init.dimensions.clone();
let mut next = Expression::ArrayInit(ArrayInitExpression {
element,
dimensions: ArrayDimensions(vec![dimensions.remove_last().unwrap()]),
span: array_init.span.clone(),
});
let mut outer_element = Box::new(next.clone());
for (index, dimension) in dimensions.0.iter().rev().enumerate() {
if index == dimensions.0.len() - 1 {
break;
}
next = Expression::ArrayInit(ArrayInitExpression {
element: outer_element,
dimensions: ArrayDimensions(vec![dimension.clone()]),
span: array_init.span.clone(),
});
outer_element = Box::new(next.clone());
}
Ok(ArrayInitExpression {
element: outer_element,
dimensions: ArrayDimensions(vec![dimensions.remove_first().unwrap()]),
span: array_init.span.clone(),
})
}
fn reduce_assign(
&mut self,
assign: &AssignStatement,
assignee: Assignee,
value: Expression,
_in_circuit: bool,
) -> Result<AssignStatement, CanonicalizeError> {
match value {
Expression::Value(value_expr) if assign.operation != AssignOperation::Assign => {
let left = Box::new(Expression::Identifier(assignee.identifier.clone()));
let right = Box::new(Expression::Value(value_expr));
let op = match assign.operation {
AssignOperation::Assign => unimplemented!(), // Imposible
AssignOperation::Add => BinaryOperation::Add,
AssignOperation::Sub => BinaryOperation::Sub,
AssignOperation::Mul => BinaryOperation::Mul,
AssignOperation::Div => BinaryOperation::Div,
AssignOperation::Pow => BinaryOperation::Pow,
AssignOperation::Or => BinaryOperation::Or,
AssignOperation::And => BinaryOperation::And,
AssignOperation::BitOr => BinaryOperation::BitOr,
AssignOperation::BitAnd => BinaryOperation::BitAnd,
AssignOperation::BitXor => BinaryOperation::BitXor,
AssignOperation::Shr => BinaryOperation::Shr,
AssignOperation::ShrSigned => BinaryOperation::ShrSigned,
AssignOperation::Shl => BinaryOperation::Shl,
AssignOperation::Mod => BinaryOperation::Mod,
};
let new_value = Expression::Binary(BinaryExpression {
left,
right,
op,
span: assign.span.clone(),
});
Ok(AssignStatement {
operation: AssignOperation::Assign,
assignee,
value: new_value,
span: assign.span.clone(),
})
}
_ => Ok(assign.clone()),
}
}
fn reduce_function(
&mut self,
function: &Function,
identifier: Identifier,
annotations: Vec<Annotation>,
input: Vec<FunctionInput>,
output: Option<Type>,
block: Block,
_in_circuit: bool,
) -> Result<Function, CanonicalizeError> {
let new_output = match output {
None => Some(Type::Tuple(vec![])),
_ => output,
};
Ok(Function {
identifier,
annotations,
input,
output: new_output,
block,
span: function.span.clone(),
})
}
fn reduce_circuit(
&mut self,
_circuit: &Circuit,
circuit_name: Identifier,
members: Vec<CircuitMember>,
) -> Result<Circuit, CanonicalizeError> {
self.circuit_name = Some(circuit_name.clone());
let circ = Circuit {
circuit_name,
members: members
.iter()
.map(|member| self.canonicalize_circuit_member(member))
.collect(),
};
self.circuit_name = None;
Ok(circ)
}
}

41
ast/src/reducer/errors.rs Normal file
View File

@ -0,0 +1,41 @@
// Copyright (C) 2019-2021 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::{FormattedError, Span};
#[derive(Debug, Error)]
pub enum CanonicalizeError {
#[error("{}", _0)]
Error(#[from] FormattedError),
}
impl CanonicalizeError {
fn new_from_span(message: String, span: &Span) -> Self {
CanonicalizeError::Error(FormattedError::new_from_span(message, span))
}
pub fn big_self_outside_of_circuit(span: &Span) -> Self {
let message = "cannot call keyword `Self` outside of a circuit function".to_string();
Self::new_from_span(message, span)
}
pub fn invalid_array_dimension_size(span: &Span) -> Self {
let message = "recieved dimension size of 0, expected it to be 1 or larger.".to_string();
Self::new_from_span(message, span)
}
}

27
ast/src/reducer/mod.rs Normal file
View File

@ -0,0 +1,27 @@
// Copyright (C) 2019-2021 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/>.
mod canonicalization;
pub use canonicalization::*;
mod errors;
pub use errors::*;
mod reconstructing_reducer;
pub use reconstructing_reducer::*;
mod reconstructing_director;
pub use reconstructing_director::*;

View File

@ -0,0 +1,599 @@
// Copyright (C) 2019-2021 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/>.
//! This module contains the reducer which iterates through ast nodes - converting them into
//! asg nodes and saving relevant information.
use crate::*;
use indexmap::IndexMap;
pub struct ReconstructingDirector<R: ReconstructingReducer> {
reducer: R,
in_circuit: bool,
}
impl<R: ReconstructingReducer> ReconstructingDirector<R> {
pub fn new(reducer: R) -> Self {
Self {
reducer,
in_circuit: false,
}
}
pub fn reduce_type(&mut self, type_: &Type, span: &Span) -> Result<Type, CanonicalizeError> {
let new = match type_ {
Type::Array(type_, dimensions) => Type::Array(Box::new(self.reduce_type(type_, span)?), dimensions.clone()),
Type::Tuple(types) => {
let mut reduced_types = vec![];
for type_ in types.iter() {
reduced_types.push(self.reduce_type(type_, span)?);
}
Type::Tuple(reduced_types)
}
Type::Circuit(identifier) => Type::Circuit(self.reduce_identifier(identifier)?),
_ => type_.clone(),
};
self.reducer.reduce_type(type_, new, self.in_circuit, span)
}
// Expressions
pub fn reduce_expression(&mut self, expression: &Expression) -> Result<Expression, CanonicalizeError> {
let new = match expression {
Expression::Identifier(identifier) => Expression::Identifier(self.reduce_identifier(&identifier)?),
Expression::Value(value) => Expression::Value(self.reduce_value(&value)?),
Expression::Binary(binary) => Expression::Binary(self.reduce_binary(&binary)?),
Expression::Unary(unary) => Expression::Unary(self.reduce_unary(&unary)?),
Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(&ternary)?),
Expression::Cast(cast) => Expression::Cast(self.reduce_cast(&cast)?),
Expression::ArrayInline(array_inline) => Expression::ArrayInline(self.reduce_array_inline(&array_inline)?),
Expression::ArrayInit(array_init) => Expression::ArrayInit(self.reduce_array_init(&array_init)?),
Expression::ArrayAccess(array_access) => Expression::ArrayAccess(self.reduce_array_access(&array_access)?),
Expression::ArrayRangeAccess(array_range_access) => {
Expression::ArrayRangeAccess(self.reduce_array_range_access(&array_range_access)?)
}
Expression::TupleInit(tuple_init) => Expression::TupleInit(self.reduce_tuple_init(&tuple_init)?),
Expression::TupleAccess(tuple_access) => Expression::TupleAccess(self.reduce_tuple_access(&tuple_access)?),
Expression::CircuitInit(circuit_init) => Expression::CircuitInit(self.reduce_circuit_init(&circuit_init)?),
Expression::CircuitMemberAccess(circuit_member_access) => {
Expression::CircuitMemberAccess(self.reduce_circuit_member_access(&circuit_member_access)?)
}
Expression::CircuitStaticFunctionAccess(circuit_static_fn_access) => {
Expression::CircuitStaticFunctionAccess(
self.reduce_circuit_static_fn_access(&circuit_static_fn_access)?,
)
}
Expression::Call(call) => Expression::Call(self.reduce_call(&call)?),
};
self.reducer.reduce_expression(expression, new, self.in_circuit)
}
pub fn reduce_identifier(&mut self, identifier: &Identifier) -> Result<Identifier, CanonicalizeError> {
self.reducer.reduce_identifier(identifier)
}
pub fn reduce_group_tuple(&mut self, group_tuple: &GroupTuple) -> Result<GroupTuple, CanonicalizeError> {
self.reducer.reduce_group_tuple(group_tuple)
}
pub fn reduce_group_value(&mut self, group_value: &GroupValue) -> Result<GroupValue, CanonicalizeError> {
let new = match group_value {
GroupValue::Tuple(group_tuple) => GroupValue::Tuple(self.reduce_group_tuple(&group_tuple)?),
_ => group_value.clone(),
};
self.reducer.reduce_group_value(group_value, new)
}
pub fn reduce_value(&mut self, value: &ValueExpression) -> Result<ValueExpression, CanonicalizeError> {
let new = match value {
ValueExpression::Group(group_value) => {
ValueExpression::Group(Box::new(self.reduce_group_value(&group_value)?))
}
_ => value.clone(),
};
self.reducer.reduce_value(value, new)
}
pub fn reduce_binary(&mut self, binary: &BinaryExpression) -> Result<BinaryExpression, CanonicalizeError> {
let left = self.reduce_expression(&binary.left)?;
let right = self.reduce_expression(&binary.right)?;
self.reducer
.reduce_binary(binary, left, right, binary.op.clone(), self.in_circuit)
}
pub fn reduce_unary(&mut self, unary: &UnaryExpression) -> Result<UnaryExpression, CanonicalizeError> {
let inner = self.reduce_expression(&unary.inner)?;
self.reducer
.reduce_unary(unary, inner, unary.op.clone(), self.in_circuit)
}
pub fn reduce_ternary(&mut self, ternary: &TernaryExpression) -> Result<TernaryExpression, CanonicalizeError> {
let condition = self.reduce_expression(&ternary.condition)?;
let if_true = self.reduce_expression(&ternary.if_true)?;
let if_false = self.reduce_expression(&ternary.if_false)?;
self.reducer
.reduce_ternary(ternary, condition, if_true, if_false, self.in_circuit)
}
pub fn reduce_cast(&mut self, cast: &CastExpression) -> Result<CastExpression, CanonicalizeError> {
let inner = self.reduce_expression(&cast.inner)?;
let target_type = self.reduce_type(&cast.target_type, &cast.span)?;
self.reducer.reduce_cast(cast, inner, target_type, self.in_circuit)
}
pub fn reduce_array_inline(
&mut self,
array_inline: &ArrayInlineExpression,
) -> Result<ArrayInlineExpression, CanonicalizeError> {
let mut elements = vec![];
for element in array_inline.elements.iter() {
let reduced_element = match element {
SpreadOrExpression::Expression(expression) => {
SpreadOrExpression::Expression(self.reduce_expression(expression)?)
}
SpreadOrExpression::Spread(expression) => {
SpreadOrExpression::Spread(self.reduce_expression(expression)?)
}
};
elements.push(reduced_element);
}
self.reducer
.reduce_array_inline(array_inline, elements, self.in_circuit)
}
pub fn reduce_array_init(
&mut self,
array_init: &ArrayInitExpression,
) -> Result<ArrayInitExpression, CanonicalizeError> {
let element = self.reduce_expression(&array_init.element)?;
self.reducer.reduce_array_init(array_init, element, self.in_circuit)
}
pub fn reduce_array_access(
&mut self,
array_access: &ArrayAccessExpression,
) -> Result<ArrayAccessExpression, CanonicalizeError> {
let array = self.reduce_expression(&array_access.array)?;
let index = self.reduce_expression(&array_access.index)?;
self.reducer
.reduce_array_access(array_access, array, index, self.in_circuit)
}
pub fn reduce_array_range_access(
&mut self,
array_range_access: &ArrayRangeAccessExpression,
) -> Result<ArrayRangeAccessExpression, CanonicalizeError> {
let array = self.reduce_expression(&array_range_access.array)?;
let left = array_range_access
.left
.as_ref()
.map(|left| self.reduce_expression(left))
.transpose()?;
let right = array_range_access
.right
.as_ref()
.map(|right| self.reduce_expression(right))
.transpose()?;
self.reducer
.reduce_array_range_access(array_range_access, array, left, right, self.in_circuit)
}
pub fn reduce_tuple_init(
&mut self,
tuple_init: &TupleInitExpression,
) -> Result<TupleInitExpression, CanonicalizeError> {
let mut elements = vec![];
for element in tuple_init.elements.iter() {
elements.push(self.reduce_expression(element)?);
}
self.reducer.reduce_tuple_init(tuple_init, elements, self.in_circuit)
}
pub fn reduce_tuple_access(
&mut self,
tuple_access: &TupleAccessExpression,
) -> Result<TupleAccessExpression, CanonicalizeError> {
let tuple = self.reduce_expression(&tuple_access.tuple)?;
self.reducer.reduce_tuple_access(tuple_access, tuple, self.in_circuit)
}
pub fn reduce_circuit_implied_variable_definition(
&mut self,
variable: &CircuitImpliedVariableDefinition,
) -> Result<CircuitImpliedVariableDefinition, CanonicalizeError> {
let identifier = self.reduce_identifier(&variable.identifier)?;
let expression = variable
.expression
.as_ref()
.map(|expr| self.reduce_expression(expr))
.transpose()?;
self.reducer
.reduce_circuit_implied_variable_definition(variable, identifier, expression, self.in_circuit)
}
pub fn reduce_circuit_init(
&mut self,
circuit_init: &CircuitInitExpression,
) -> Result<CircuitInitExpression, CanonicalizeError> {
let name = self.reduce_identifier(&circuit_init.name)?;
let mut members = vec![];
for member in circuit_init.members.iter() {
members.push(self.reduce_circuit_implied_variable_definition(member)?);
}
self.reducer
.reduce_circuit_init(circuit_init, name, members, self.in_circuit)
}
pub fn reduce_circuit_member_access(
&mut self,
circuit_member_access: &CircuitMemberAccessExpression,
) -> Result<CircuitMemberAccessExpression, CanonicalizeError> {
let circuit = self.reduce_expression(&circuit_member_access.circuit)?;
let name = self.reduce_identifier(&circuit_member_access.name)?;
self.reducer
.reduce_circuit_member_access(circuit_member_access, circuit, name, self.in_circuit)
}
pub fn reduce_circuit_static_fn_access(
&mut self,
circuit_static_fn_access: &CircuitStaticFunctionAccessExpression,
) -> Result<CircuitStaticFunctionAccessExpression, CanonicalizeError> {
let circuit = self.reduce_expression(&circuit_static_fn_access.circuit)?;
let name = self.reduce_identifier(&circuit_static_fn_access.name)?;
self.reducer
.reduce_circuit_static_fn_access(circuit_static_fn_access, circuit, name, self.in_circuit)
}
pub fn reduce_call(&mut self, call: &CallExpression) -> Result<CallExpression, CanonicalizeError> {
let function = self.reduce_expression(&call.function)?;
let mut arguments = vec![];
for argument in call.arguments.iter() {
arguments.push(self.reduce_expression(argument)?);
}
self.reducer.reduce_call(call, function, arguments, self.in_circuit)
}
// Statements
pub fn reduce_statement(&mut self, statement: &Statement) -> Result<Statement, CanonicalizeError> {
let new = match statement {
Statement::Return(return_statement) => Statement::Return(self.reduce_return(&return_statement)?),
Statement::Definition(definition) => Statement::Definition(self.reduce_definition(&definition)?),
Statement::Assign(assign) => Statement::Assign(self.reduce_assign(&assign)?),
Statement::Conditional(conditional) => Statement::Conditional(self.reduce_conditional(&conditional)?),
Statement::Iteration(iteration) => Statement::Iteration(self.reduce_iteration(&iteration)?),
Statement::Console(console) => Statement::Console(self.reduce_console(&console)?),
Statement::Expression(expression) => Statement::Expression(self.reduce_expression_statement(&expression)?),
Statement::Block(block) => Statement::Block(self.reduce_block(&block)?),
};
self.reducer.reduce_statement(statement, new, self.in_circuit)
}
pub fn reduce_return(&mut self, return_statement: &ReturnStatement) -> Result<ReturnStatement, CanonicalizeError> {
let expression = self.reduce_expression(&return_statement.expression)?;
self.reducer
.reduce_return(return_statement, expression, self.in_circuit)
}
pub fn reduce_variable_name(&mut self, variable_name: &VariableName) -> Result<VariableName, CanonicalizeError> {
let identifier = self.reduce_identifier(&variable_name.identifier)?;
self.reducer.reduce_variable_name(variable_name, identifier)
}
pub fn reduce_definition(
&mut self,
definition: &DefinitionStatement,
) -> Result<DefinitionStatement, CanonicalizeError> {
let mut variable_names = vec![];
for variable_name in definition.variable_names.iter() {
variable_names.push(self.reduce_variable_name(variable_name)?);
}
let type_ = definition
.type_
.as_ref()
.map(|type_| self.reduce_type(type_, &definition.span))
.transpose()?;
let value = self.reduce_expression(&definition.value)?;
self.reducer
.reduce_definition(definition, variable_names, type_, value, self.in_circuit)
}
pub fn reduce_assignee_access(&mut self, access: &AssigneeAccess) -> Result<AssigneeAccess, CanonicalizeError> {
let new = match access {
AssigneeAccess::ArrayRange(left, right) => {
let left = left.as_ref().map(|left| self.reduce_expression(left)).transpose()?;
let right = right.as_ref().map(|right| self.reduce_expression(right)).transpose()?;
AssigneeAccess::ArrayRange(left, right)
}
AssigneeAccess::ArrayIndex(index) => AssigneeAccess::ArrayIndex(self.reduce_expression(&index)?),
AssigneeAccess::Member(identifier) => AssigneeAccess::Member(self.reduce_identifier(&identifier)?),
_ => access.clone(),
};
self.reducer.reduce_assignee_access(access, new, self.in_circuit)
}
pub fn reduce_assignee(&mut self, assignee: &Assignee) -> Result<Assignee, CanonicalizeError> {
let identifier = self.reduce_identifier(&assignee.identifier)?;
let mut accesses = vec![];
for access in assignee.accesses.iter() {
accesses.push(self.reduce_assignee_access(access)?);
}
self.reducer
.reduce_assignee(assignee, identifier, accesses, self.in_circuit)
}
pub fn reduce_assign(&mut self, assign: &AssignStatement) -> Result<AssignStatement, CanonicalizeError> {
let assignee = self.reduce_assignee(&assign.assignee)?;
let value = self.reduce_expression(&assign.value)?;
self.reducer.reduce_assign(assign, assignee, value, self.in_circuit)
}
pub fn reduce_conditional(
&mut self,
conditional: &ConditionalStatement,
) -> Result<ConditionalStatement, CanonicalizeError> {
let condition = self.reduce_expression(&conditional.condition)?;
let block = self.reduce_block(&conditional.block)?;
let next = conditional
.next
.as_ref()
.map(|condition| self.reduce_statement(condition))
.transpose()?;
self.reducer
.reduce_conditional(conditional, condition, block, next, self.in_circuit)
}
pub fn reduce_iteration(
&mut self,
iteration: &IterationStatement,
) -> Result<IterationStatement, CanonicalizeError> {
let variable = self.reduce_identifier(&iteration.variable)?;
let start = self.reduce_expression(&iteration.start)?;
let stop = self.reduce_expression(&iteration.stop)?;
let block = self.reduce_block(&iteration.block)?;
self.reducer
.reduce_iteration(iteration, variable, start, stop, block, self.in_circuit)
}
pub fn reduce_console(
&mut self,
console_function_call: &ConsoleStatement,
) -> Result<ConsoleStatement, CanonicalizeError> {
let function = match &console_function_call.function {
ConsoleFunction::Assert(expression) => ConsoleFunction::Assert(self.reduce_expression(expression)?),
ConsoleFunction::Debug(format) | ConsoleFunction::Error(format) | ConsoleFunction::Log(format) => {
let mut parameters = vec![];
for parameter in format.parameters.iter() {
parameters.push(self.reduce_expression(parameter)?);
}
let formatted = FormattedString {
parts: format.parts.clone(),
parameters,
span: format.span.clone(),
};
match &console_function_call.function {
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted),
ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted),
ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted),
_ => unimplemented!(), // impossible
}
}
};
self.reducer
.reduce_console(console_function_call, function, self.in_circuit)
}
pub fn reduce_expression_statement(
&mut self,
expression: &ExpressionStatement,
) -> Result<ExpressionStatement, CanonicalizeError> {
let inner_expression = self.reduce_expression(&expression.expression)?;
self.reducer
.reduce_expression_statement(expression, inner_expression, self.in_circuit)
}
pub fn reduce_block(&mut self, block: &Block) -> Result<Block, CanonicalizeError> {
let mut statements = vec![];
for statement in block.statements.iter() {
statements.push(self.reduce_statement(statement)?);
}
self.reducer.reduce_block(block, statements, self.in_circuit)
}
// Program
pub fn reduce_program(&mut self, program: &Program) -> Result<Program, CanonicalizeError> {
let mut inputs = vec![];
for input in program.expected_input.iter() {
inputs.push(self.reduce_function_input(input)?);
}
let mut imports = vec![];
for import in program.imports.iter() {
imports.push(self.reduce_import(import)?);
}
let mut circuits = IndexMap::new();
for (identifier, circuit) in program.circuits.iter() {
circuits.insert(self.reduce_identifier(identifier)?, self.reduce_circuit(circuit)?);
}
let mut functions = IndexMap::new();
for (identifier, function) in program.functions.iter() {
functions.insert(self.reduce_identifier(identifier)?, self.reduce_function(function)?);
}
self.reducer
.reduce_program(program, inputs, imports, circuits, functions)
}
pub fn reduce_function_input_variable(
&mut self,
variable: &FunctionInputVariable,
) -> Result<FunctionInputVariable, CanonicalizeError> {
let identifier = self.reduce_identifier(&variable.identifier)?;
let type_ = self.reduce_type(&variable.type_, &variable.span)?;
self.reducer
.reduce_function_input_variable(variable, identifier, type_, self.in_circuit)
}
pub fn reduce_function_input(&mut self, input: &FunctionInput) -> Result<FunctionInput, CanonicalizeError> {
let new = match input {
FunctionInput::Variable(function_input_variable) => {
FunctionInput::Variable(self.reduce_function_input_variable(function_input_variable)?)
}
_ => input.clone(),
};
self.reducer.reduce_function_input(input, new, self.in_circuit)
}
pub fn reduce_package_or_packages(
&mut self,
package_or_packages: &PackageOrPackages,
) -> Result<PackageOrPackages, CanonicalizeError> {
let new = match package_or_packages {
PackageOrPackages::Package(package) => PackageOrPackages::Package(Package {
name: self.reduce_identifier(&package.name)?,
access: package.access.clone(),
span: package.span.clone(),
}),
PackageOrPackages::Packages(packages) => PackageOrPackages::Packages(Packages {
name: self.reduce_identifier(&packages.name)?,
accesses: packages.accesses.clone(),
span: packages.span.clone(),
}),
};
self.reducer.reduce_package_or_packages(package_or_packages, new)
}
pub fn reduce_import(&mut self, import: &ImportStatement) -> Result<ImportStatement, CanonicalizeError> {
let package_or_packages = self.reduce_package_or_packages(&import.package_or_packages)?;
self.reducer.reduce_import(import, package_or_packages)
}
pub fn reduce_circuit_member(
&mut self,
circuit_member: &CircuitMember,
) -> Result<CircuitMember, CanonicalizeError> {
self.in_circuit = !self.in_circuit;
let new = match circuit_member {
CircuitMember::CircuitVariable(identifier, type_) => CircuitMember::CircuitVariable(
self.reduce_identifier(&identifier)?,
self.reduce_type(&type_, &identifier.span)?,
),
CircuitMember::CircuitFunction(function) => {
CircuitMember::CircuitFunction(self.reduce_function(&function)?)
}
};
self.in_circuit = !self.in_circuit;
self.reducer.reduce_circuit_member(circuit_member, new)
}
pub fn reduce_circuit(&mut self, circuit: &Circuit) -> Result<Circuit, CanonicalizeError> {
let circuit_name = self.reduce_identifier(&circuit.circuit_name)?;
let mut members = vec![];
for member in circuit.members.iter() {
members.push(self.reduce_circuit_member(member)?);
}
self.reducer.reduce_circuit(circuit, circuit_name, members)
}
fn reduce_annotation(&mut self, annotation: &Annotation) -> Result<Annotation, CanonicalizeError> {
let name = self.reduce_identifier(&annotation.name)?;
self.reducer.reduce_annotation(annotation, name)
}
pub fn reduce_function(&mut self, function: &Function) -> Result<Function, CanonicalizeError> {
let identifier = self.reduce_identifier(&function.identifier)?;
let mut annotations = vec![];
for annotation in function.annotations.iter() {
annotations.push(self.reduce_annotation(annotation)?);
}
let mut inputs = vec![];
for input in function.input.iter() {
inputs.push(self.reduce_function_input(input)?);
}
let output = function
.output
.as_ref()
.map(|type_| self.reduce_type(type_, &function.span))
.transpose()?;
let block = self.reduce_block(&function.block)?;
self.reducer.reduce_function(
function,
identifier,
annotations,
inputs,
output,
block,
self.in_circuit,
)
}
}

View File

@ -0,0 +1,550 @@
// Copyright (C) 2019-2021 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::*;
use indexmap::IndexMap;
// Needed to fix clippy bug.
#[allow(clippy::redundant_closure)]
pub trait ReconstructingReducer {
fn reduce_type(
&mut self,
_type_: &Type,
new: Type,
_in_circuit: bool,
_span: &Span,
) -> Result<Type, CanonicalizeError> {
Ok(new)
}
// Expressions
fn reduce_expression(
&mut self,
_expression: &Expression,
new: Expression,
_in_circuit: bool,
) -> Result<Expression, CanonicalizeError> {
Ok(new)
}
fn reduce_identifier(&mut self, identifier: &Identifier) -> Result<Identifier, CanonicalizeError> {
Ok(Identifier {
name: identifier.name.clone(),
span: identifier.span.clone(),
})
}
fn reduce_group_tuple(&mut self, group_tuple: &GroupTuple) -> Result<GroupTuple, CanonicalizeError> {
Ok(GroupTuple {
x: group_tuple.x.clone(),
y: group_tuple.y.clone(),
span: group_tuple.span.clone(),
})
}
fn reduce_group_value(
&mut self,
_group_value: &GroupValue,
new: GroupValue,
) -> Result<GroupValue, CanonicalizeError> {
Ok(new)
}
fn reduce_value(
&mut self,
_value: &ValueExpression,
new: ValueExpression,
) -> Result<ValueExpression, CanonicalizeError> {
Ok(new)
}
fn reduce_binary(
&mut self,
binary: &BinaryExpression,
left: Expression,
right: Expression,
op: BinaryOperation,
_in_circuit: bool,
) -> Result<BinaryExpression, CanonicalizeError> {
Ok(BinaryExpression {
left: Box::new(left),
right: Box::new(right),
op,
span: binary.span.clone(),
})
}
fn reduce_unary(
&mut self,
unary: &UnaryExpression,
inner: Expression,
op: UnaryOperation,
_in_circuit: bool,
) -> Result<UnaryExpression, CanonicalizeError> {
Ok(UnaryExpression {
inner: Box::new(inner),
op,
span: unary.span.clone(),
})
}
fn reduce_ternary(
&mut self,
ternary: &TernaryExpression,
condition: Expression,
if_true: Expression,
if_false: Expression,
_in_circuit: bool,
) -> Result<TernaryExpression, CanonicalizeError> {
Ok(TernaryExpression {
condition: Box::new(condition),
if_true: Box::new(if_true),
if_false: Box::new(if_false),
span: ternary.span.clone(),
})
}
fn reduce_cast(
&mut self,
cast: &CastExpression,
inner: Expression,
target_type: Type,
_in_circuit: bool,
) -> Result<CastExpression, CanonicalizeError> {
Ok(CastExpression {
inner: Box::new(inner),
target_type,
span: cast.span.clone(),
})
}
fn reduce_array_inline(
&mut self,
array_inline: &ArrayInlineExpression,
elements: Vec<SpreadOrExpression>,
_in_circuit: bool,
) -> Result<ArrayInlineExpression, CanonicalizeError> {
Ok(ArrayInlineExpression {
elements,
span: array_inline.span.clone(),
})
}
fn reduce_array_init(
&mut self,
array_init: &ArrayInitExpression,
element: Expression,
_in_circuit: bool,
) -> Result<ArrayInitExpression, CanonicalizeError> {
Ok(ArrayInitExpression {
element: Box::new(element),
dimensions: array_init.dimensions.clone(),
span: array_init.span.clone(),
})
}
fn reduce_array_access(
&mut self,
array_access: &ArrayAccessExpression,
array: Expression,
index: Expression,
_in_circuit: bool,
) -> Result<ArrayAccessExpression, CanonicalizeError> {
Ok(ArrayAccessExpression {
array: Box::new(array),
index: Box::new(index),
span: array_access.span.clone(),
})
}
fn reduce_array_range_access(
&mut self,
array_rage_access: &ArrayRangeAccessExpression,
array: Expression,
left: Option<Expression>,
right: Option<Expression>,
_in_circuit: bool,
) -> Result<ArrayRangeAccessExpression, CanonicalizeError> {
Ok(ArrayRangeAccessExpression {
array: Box::new(array),
left: left.map(|expr| Box::new(expr)),
right: right.map(|expr| Box::new(expr)),
span: array_rage_access.span.clone(),
})
}
fn reduce_tuple_init(
&mut self,
tuple_init: &TupleInitExpression,
elements: Vec<Expression>,
_in_circuit: bool,
) -> Result<TupleInitExpression, CanonicalizeError> {
Ok(TupleInitExpression {
elements,
span: tuple_init.span.clone(),
})
}
fn reduce_tuple_access(
&mut self,
tuple_access: &TupleAccessExpression,
tuple: Expression,
_in_circuit: bool,
) -> Result<TupleAccessExpression, CanonicalizeError> {
Ok(TupleAccessExpression {
tuple: Box::new(tuple),
index: tuple_access.index.clone(),
span: tuple_access.span.clone(),
})
}
fn reduce_circuit_implied_variable_definition(
&mut self,
_variable: &CircuitImpliedVariableDefinition,
identifier: Identifier,
expression: Option<Expression>,
_in_circuit: bool,
) -> Result<CircuitImpliedVariableDefinition, CanonicalizeError> {
Ok(CircuitImpliedVariableDefinition { identifier, expression })
}
fn reduce_circuit_init(
&mut self,
circuit_init: &CircuitInitExpression,
name: Identifier,
members: Vec<CircuitImpliedVariableDefinition>,
_in_circuit: bool,
) -> Result<CircuitInitExpression, CanonicalizeError> {
Ok(CircuitInitExpression {
name,
members,
span: circuit_init.span.clone(),
})
}
fn reduce_circuit_member_access(
&mut self,
circuit_member_access: &CircuitMemberAccessExpression,
circuit: Expression,
name: Identifier,
_in_circuit: bool,
) -> Result<CircuitMemberAccessExpression, CanonicalizeError> {
Ok(CircuitMemberAccessExpression {
circuit: Box::new(circuit),
name,
span: circuit_member_access.span.clone(),
})
}
fn reduce_circuit_static_fn_access(
&mut self,
circuit_static_fn_access: &CircuitStaticFunctionAccessExpression,
circuit: Expression,
name: Identifier,
_in_circuit: bool,
) -> Result<CircuitStaticFunctionAccessExpression, CanonicalizeError> {
Ok(CircuitStaticFunctionAccessExpression {
circuit: Box::new(circuit),
name,
span: circuit_static_fn_access.span.clone(),
})
}
fn reduce_call(
&mut self,
call: &CallExpression,
function: Expression,
arguments: Vec<Expression>,
_in_circuit: bool,
) -> Result<CallExpression, CanonicalizeError> {
Ok(CallExpression {
function: Box::new(function),
arguments,
span: call.span.clone(),
})
}
// Statements
fn reduce_statement(
&mut self,
_statement: &Statement,
new: Statement,
_in_circuit: bool,
) -> Result<Statement, CanonicalizeError> {
Ok(new)
}
fn reduce_return(
&mut self,
return_statement: &ReturnStatement,
expression: Expression,
_in_circuit: bool,
) -> Result<ReturnStatement, CanonicalizeError> {
Ok(ReturnStatement {
expression,
span: return_statement.span.clone(),
})
}
fn reduce_variable_name(
&mut self,
variable_name: &VariableName,
identifier: Identifier,
) -> Result<VariableName, CanonicalizeError> {
Ok(VariableName {
mutable: variable_name.mutable,
identifier,
span: variable_name.span.clone(),
})
}
fn reduce_definition(
&mut self,
definition: &DefinitionStatement,
variable_names: Vec<VariableName>,
type_: Option<Type>,
value: Expression,
_in_circuit: bool,
) -> Result<DefinitionStatement, CanonicalizeError> {
Ok(DefinitionStatement {
declaration_type: definition.declaration_type.clone(),
variable_names,
type_,
value,
span: definition.span.clone(),
})
}
fn reduce_assignee_access(
&mut self,
_access: &AssigneeAccess,
new: AssigneeAccess,
_in_circuit: bool,
) -> Result<AssigneeAccess, CanonicalizeError> {
Ok(new)
}
fn reduce_assignee(
&mut self,
assignee: &Assignee,
identifier: Identifier,
accesses: Vec<AssigneeAccess>,
_in_circuit: bool,
) -> Result<Assignee, CanonicalizeError> {
Ok(Assignee {
identifier,
accesses,
span: assignee.span.clone(),
})
}
fn reduce_assign(
&mut self,
assign: &AssignStatement,
assignee: Assignee,
value: Expression,
_in_circuit: bool,
) -> Result<AssignStatement, CanonicalizeError> {
Ok(AssignStatement {
operation: assign.operation.clone(),
assignee,
value,
span: assign.span.clone(),
})
}
fn reduce_conditional(
&mut self,
conditional: &ConditionalStatement,
condition: Expression,
block: Block,
statement: Option<Statement>,
_in_circuit: bool,
) -> Result<ConditionalStatement, CanonicalizeError> {
Ok(ConditionalStatement {
condition,
block,
next: statement.map(|statement| Box::new(statement)),
span: conditional.span.clone(),
})
}
fn reduce_iteration(
&mut self,
iteration: &IterationStatement,
variable: Identifier,
start: Expression,
stop: Expression,
block: Block,
_in_circuit: bool,
) -> Result<IterationStatement, CanonicalizeError> {
Ok(IterationStatement {
variable,
start,
stop,
block,
span: iteration.span.clone(),
})
}
fn reduce_console(
&mut self,
console: &ConsoleStatement,
function: ConsoleFunction,
_in_circuit: bool,
) -> Result<ConsoleStatement, CanonicalizeError> {
Ok(ConsoleStatement {
function,
span: console.span.clone(),
})
}
fn reduce_expression_statement(
&mut self,
expression_statement: &ExpressionStatement,
expression: Expression,
_in_circuit: bool,
) -> Result<ExpressionStatement, CanonicalizeError> {
Ok(ExpressionStatement {
expression,
span: expression_statement.span.clone(),
})
}
fn reduce_block(
&mut self,
block: &Block,
statements: Vec<Statement>,
_in_circuit: bool,
) -> Result<Block, CanonicalizeError> {
Ok(Block {
statements,
span: block.span.clone(),
})
}
// Program
fn reduce_program(
&mut self,
program: &Program,
expected_input: Vec<FunctionInput>,
imports: Vec<ImportStatement>,
circuits: IndexMap<Identifier, Circuit>,
functions: IndexMap<Identifier, Function>,
) -> Result<Program, CanonicalizeError> {
Ok(Program {
name: program.name.clone(),
expected_input,
imports,
circuits,
functions,
})
}
fn reduce_function_input_variable(
&mut self,
variable: &FunctionInputVariable,
identifier: Identifier,
type_: Type,
_in_circuit: bool,
) -> Result<FunctionInputVariable, CanonicalizeError> {
Ok(FunctionInputVariable {
identifier,
const_: variable.const_,
mutable: variable.mutable,
type_,
span: variable.span.clone(),
})
}
fn reduce_function_input(
&mut self,
_input: &FunctionInput,
new: FunctionInput,
_in_circuit: bool,
) -> Result<FunctionInput, CanonicalizeError> {
Ok(new)
}
fn reduce_package_or_packages(
&mut self,
_package_or_packages: &PackageOrPackages,
new: PackageOrPackages,
) -> Result<PackageOrPackages, CanonicalizeError> {
Ok(new)
}
fn reduce_import(
&mut self,
import: &ImportStatement,
package_or_packages: PackageOrPackages,
) -> Result<ImportStatement, CanonicalizeError> {
Ok(ImportStatement {
package_or_packages,
span: import.span.clone(),
})
}
fn reduce_circuit_member(
&mut self,
_circuit_member: &CircuitMember,
new: CircuitMember,
) -> Result<CircuitMember, CanonicalizeError> {
Ok(new)
}
fn reduce_circuit(
&mut self,
_circuit: &Circuit,
circuit_name: Identifier,
members: Vec<CircuitMember>,
) -> Result<Circuit, CanonicalizeError> {
Ok(Circuit { circuit_name, members })
}
fn reduce_annotation(
&mut self,
annotation: &Annotation,
name: Identifier,
) -> Result<Annotation, CanonicalizeError> {
Ok(Annotation {
span: annotation.span.clone(),
name,
arguments: annotation.arguments.clone(),
})
}
#[allow(clippy::too_many_arguments)]
fn reduce_function(
&mut self,
function: &Function,
identifier: Identifier,
annotations: Vec<Annotation>,
input: Vec<FunctionInput>,
output: Option<Type>,
block: Block,
_in_circuit: bool,
) -> Result<Function, CanonicalizeError> {
Ok(Function {
identifier,
annotations,
input,
output,
block,
span: function.span.clone(),
})
}
}

View File

@ -263,7 +263,11 @@ impl<'a, F: PrimeField, G: GroupType<F>> Compiler<'a, F, G> {
Rc::new(lines),
);
let ast = parse_ast(self.main_file_path.to_str().unwrap_or_default(), program_string)?;
let mut ast = parse_ast(self.main_file_path.to_str().unwrap_or_default(), program_string)?;
// Preform compiler optimization via canonicalizing AST if its enabled.
if self.options.canonicalization_enabled {
ast.canonicalize()?;
}
// Store the main program file.
self.program = ast.into_repr();

View File

@ -16,7 +16,7 @@
use crate::errors::{FunctionError, ImportError, OutputBytesError, OutputFileError};
use leo_asg::{AsgConvertError, FormattedError};
use leo_ast::LeoError;
use leo_ast::{CanonicalizeError, LeoError};
use leo_imports::ImportParserError;
use leo_input::InputParserError;
use leo_parser::SyntaxError;
@ -77,6 +77,9 @@ pub enum CompilerError {
#[error("{}", _0)]
AsgConvertError(#[from] AsgConvertError),
#[error("{}", _0)]
CanonicalizeError(#[from] CanonicalizeError),
}
impl LeoError for CompilerError {

View File

@ -27,6 +27,7 @@ pub const STATE_VARIABLE_NAME: &str = "state";
pub const STATE_LEAF_VARIABLE_NAME: &str = "state_leaf";
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
#[allow(clippy::vec_init_then_push)]
pub fn allocate_input_keyword<CS: ConstraintSystem<F>>(
&mut self,
cs: &mut CS,

View File

@ -19,6 +19,7 @@
//! The [`Compiler`] type compiles Leo programs into R1CS circuits.
#![allow(clippy::module_inception)]
#![allow(clippy::upper_case_acronyms)]
#[macro_use]
extern crate thiserror;

View File

@ -19,6 +19,7 @@
///
#[derive(Clone)]
pub struct CompilerOptions {
pub canonicalization_enabled: bool,
pub constant_folding_enabled: bool,
pub dead_code_elimination_enabled: bool,
}
@ -29,6 +30,7 @@ impl Default for CompilerOptions {
///
fn default() -> Self {
CompilerOptions {
canonicalization_enabled: true,
constant_folding_enabled: true,
dead_code_elimination_enabled: true,
}

View File

@ -35,6 +35,7 @@ impl OutputBytes {
program: &Program<'a>,
registers: &Registers,
value: ConstrainedValue<'a, F, G>,
span: &Span,
) -> Result<Self, OutputBytesError> {
let return_values = match value {

View File

@ -0,0 +1,338 @@
{
"name": "",
"expected_input": [],
"imports": [],
"circuits": {},
"functions": {
"{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}": {
"annotations": [],
"identifier": "{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}",
"input": [
{
"Variable": {
"identifier": "{\"name\":\"a\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":15,\\\"col_stop\\\":16,\\\"path\\\":\\\"\\\"}\"}",
"const_": false,
"mutable": false,
"type_": {
"Array": [
{
"Array": [
"Group",
[
{
"value": "1"
}
]
]
},
[
{
"value": "2"
}
]
]
},
"span": {
"line_start": 1,
"line_stop": 1,
"col_start": 15,
"col_stop": 16,
"path": ""
}
}
}
],
"output": {
"Tuple": []
},
"block": {
"statements": [
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": false,
"identifier": "{\"name\":\"b\",\"span\":\"{\\\"line_start\\\":2,\\\"line_stop\\\":2,\\\"col_start\\\":7,\\\"col_stop\\\":8,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 7,
"col_stop": 8,
"path": ""
}
}
],
"type_": null,
"value": {
"ArrayInit": {
"element": {
"ArrayInit": {
"element": {
"ArrayInit": {
"element": {
"ArrayInit": {
"element": {
"ArrayInit": {
"element": {
"Value": {
"Boolean": [
"true",
{
"line_start": 2,
"line_stop": 2,
"col_start": 12,
"col_stop": 16,
"path": ""
}
]
}
},
"dimensions": [
{
"value": "2"
}
],
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 11,
"col_stop": 34,
"path": ""
}
}
},
"dimensions": [
{
"value": "3"
}
],
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 11,
"col_stop": 34,
"path": ""
}
}
},
"dimensions": [
{
"value": "4"
}
],
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 11,
"col_stop": 34,
"path": ""
}
}
},
"dimensions": [
{
"value": "5"
}
],
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 11,
"col_stop": 34,
"path": ""
}
}
},
"dimensions": [
{
"value": "6"
}
],
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 11,
"col_stop": 34,
"path": ""
}
}
},
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 3,
"col_stop": 34,
"path": ""
}
}
},
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": false,
"identifier": "{\"name\":\"c\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":7,\\\"col_stop\\\":8,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 7,
"col_stop": 8,
"path": ""
}
}
],
"type_": {
"Array": [
{
"Array": [
{
"IntegerType": "U32"
},
[
{
"value": "2"
}
]
]
},
[
{
"value": "1"
}
]
]
},
"value": {
"ArrayInit": {
"element": {
"ArrayInit": {
"element": {
"Value": {
"Integer": [
"U32",
"0",
{
"line_start": 3,
"line_stop": 3,
"col_start": 27,
"col_stop": 31,
"path": ""
}
]
}
},
"dimensions": [
{
"value": "2"
}
],
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 26,
"col_stop": 40,
"path": ""
}
}
},
"dimensions": [
{
"value": "1"
}
],
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 26,
"col_stop": 40,
"path": ""
}
}
},
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 3,
"col_stop": 40,
"path": ""
}
}
},
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": false,
"identifier": "{\"name\":\"d\",\"span\":\"{\\\"line_start\\\":4,\\\"line_stop\\\":4,\\\"col_start\\\":7,\\\"col_stop\\\":8,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 4,
"line_stop": 4,
"col_start": 7,
"col_stop": 8,
"path": ""
}
}
],
"type_": null,
"value": {
"ArrayInit": {
"element": {
"Value": {
"Integer": [
"I8",
"0",
{
"line_start": 4,
"line_stop": 4,
"col_start": 12,
"col_stop": 15,
"path": ""
}
]
}
},
"dimensions": [
{
"value": "1"
}
],
"span": {
"line_start": 4,
"line_stop": 4,
"col_start": 11,
"col_stop": 21,
"path": ""
}
}
},
"span": {
"line_start": 4,
"line_stop": 4,
"col_start": 3,
"col_stop": 21,
"path": ""
}
}
}
],
"span": {
"line_start": 1,
"line_stop": 5,
"col_start": 35,
"col_stop": 2,
"path": ""
}
},
"span": {
"line_start": 1,
"line_stop": 5,
"col_start": 1,
"col_stop": 2,
"path": ""
}
}
}
}

View File

@ -0,0 +1,7 @@
function main(a: [group; (2, 1)]) {
let b = [true; (6, 5, 4, 3, 2)];
let c: [u32; (1, 2)] = [0u32; (1, 2)];
let d = [0i8; (1)];
// let d = [true; 0];
// let e = [true; (0)];
}

View File

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

View File

@ -0,0 +1,206 @@
{
"name": "",
"expected_input": [],
"imports": [],
"circuits": {
"{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}": {
"circuit_name": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}",
"members": [
{
"CircuitVariable": [
"{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":2,\\\"line_stop\\\":2,\\\"col_start\\\":3,\\\"col_stop\\\":4,\\\"path\\\":\\\"\\\"}\"}",
{
"IntegerType": "U32"
}
]
},
{
"CircuitFunction": {
"annotations": [],
"identifier": "{\"name\":\"new\",\"span\":\"{\\\"line_start\\\":4,\\\"line_stop\\\":4,\\\"col_start\\\":12,\\\"col_stop\\\":15,\\\"path\\\":\\\"\\\"}\"}",
"input": [],
"output": {
"Circuit": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}"
},
"block": {
"statements": [
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": false,
"identifier": "{\"name\":\"new\",\"span\":\"{\\\"line_start\\\":5,\\\"line_stop\\\":5,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 5,
"line_stop": 5,
"col_start": 9,
"col_stop": 12,
"path": ""
}
}
],
"type_": {
"Circuit": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}"
},
"value": {
"CircuitInit": {
"name": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":9,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}",
"members": [
{
"identifier": "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":6,\\\"line_stop\\\":6,\\\"col_start\\\":7,\\\"col_stop\\\":8,\\\"path\\\":\\\"\\\"}\"}",
"expression": {
"Value": {
"Integer": [
"U32",
"1",
{
"line_start": 6,
"line_stop": 6,
"col_start": 10,
"col_stop": 14,
"path": ""
}
]
}
}
}
],
"span": {
"line_start": 5,
"line_stop": 7,
"col_start": 21,
"col_stop": 6,
"path": ""
}
}
},
"span": {
"line_start": 5,
"line_stop": 7,
"col_start": 5,
"col_stop": 6,
"path": ""
}
}
},
{
"Return": {
"expression": {
"Identifier": "{\"name\":\"new\",\"span\":\"{\\\"line_start\\\":9,\\\"line_stop\\\":9,\\\"col_start\\\":12,\\\"col_stop\\\":15,\\\"path\\\":\\\"\\\"}\"}"
},
"span": {
"line_start": 9,
"line_stop": 9,
"col_start": 5,
"col_stop": 15,
"path": ""
}
}
}
],
"span": {
"line_start": 4,
"line_stop": 10,
"col_start": 26,
"col_stop": 4,
"path": ""
}
},
"span": {
"line_start": 4,
"line_stop": 10,
"col_start": 3,
"col_stop": 4,
"path": ""
}
}
}
]
}
},
"functions": {
"{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":13,\\\"line_stop\\\":13,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}": {
"annotations": [],
"identifier": "{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":13,\\\"line_stop\\\":13,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}",
"input": [],
"output": {
"Tuple": []
},
"block": {
"statements": [
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": false,
"identifier": "{\"name\":\"foo\",\"span\":\"{\\\"line_start\\\":14,\\\"line_stop\\\":14,\\\"col_start\\\":7,\\\"col_stop\\\":10,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 14,
"line_stop": 14,
"col_start": 7,
"col_stop": 10,
"path": ""
}
}
],
"type_": {
"Circuit": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":14,\\\"line_stop\\\":14,\\\"col_start\\\":12,\\\"col_stop\\\":15,\\\"path\\\":\\\"\\\"}\"}"
},
"value": {
"Call": {
"function": {
"CircuitStaticFunctionAccess": {
"circuit": {
"Identifier": "{\"name\":\"Foo\",\"span\":\"{\\\"line_start\\\":14,\\\"line_stop\\\":14,\\\"col_start\\\":18,\\\"col_stop\\\":21,\\\"path\\\":\\\"\\\"}\"}"
},
"name": "{\"name\":\"new\",\"span\":\"{\\\"line_start\\\":14,\\\"line_stop\\\":14,\\\"col_start\\\":23,\\\"col_stop\\\":26,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 14,
"line_stop": 14,
"col_start": 18,
"col_stop": 26,
"path": ""
}
}
},
"arguments": [],
"span": {
"line_start": 14,
"line_stop": 14,
"col_start": 18,
"col_stop": 28,
"path": ""
}
}
},
"span": {
"line_start": 14,
"line_stop": 14,
"col_start": 3,
"col_stop": 28,
"path": ""
}
}
}
],
"span": {
"line_start": 13,
"line_stop": 15,
"col_start": 17,
"col_stop": 2,
"path": ""
}
},
"span": {
"line_start": 13,
"line_stop": 15,
"col_start": 1,
"col_stop": 2,
"path": ""
}
}
}
}

View File

@ -0,0 +1,15 @@
circuit Foo {
x: u32
function new() -> Self {
let new: Self = Self {
x: 1u32
};
return new
}
}
function main() {
let foo: Foo = Foo::new();
}

View File

@ -0,0 +1,15 @@
circuit Foo {
x: u32
function new() -> Self {
let new: Self = Self {
x: 1u32
};
return new
}
}
function main() {
let foo: Self = Foo::new();
}

View File

@ -0,0 +1,128 @@
{
"name": "",
"expected_input": [],
"imports": [],
"circuits": {},
"functions": {
"{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}": {
"annotations": [],
"identifier": "{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\"}\"}",
"input": [],
"output": {
"Tuple": []
},
"block": {
"statements": [
{
"Definition": {
"declaration_type": "Let",
"variable_names": [
{
"mutable": true,
"identifier": "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":2,\\\"line_stop\\\":2,\\\"col_start\\\":11,\\\"col_stop\\\":12,\\\"path\\\":\\\"\\\"}\"}",
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 7,
"col_stop": 12,
"path": ""
}
}
],
"type_": null,
"value": {
"Value": {
"Integer": [
"U32",
"10",
{
"line_start": 2,
"line_stop": 2,
"col_start": 15,
"col_stop": 20,
"path": ""
}
]
}
},
"span": {
"line_start": 2,
"line_stop": 2,
"col_start": 3,
"col_stop": 20,
"path": ""
}
}
},
{
"Assign": {
"operation": "Assign",
"assignee": {
"identifier": "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":3,\\\"col_stop\\\":4,\\\"path\\\":\\\"\\\"}\"}",
"accesses": [],
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 3,
"col_stop": 4,
"path": ""
}
},
"value": {
"Binary": {
"left": {
"Identifier": "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":3,\\\"line_stop\\\":3,\\\"col_start\\\":3,\\\"col_stop\\\":4,\\\"path\\\":\\\"\\\"}\"}"
},
"right": {
"Value": {
"Implicit": [
"20",
{
"line_start": 3,
"line_stop": 3,
"col_start": 8,
"col_stop": 10,
"path": ""
}
]
}
},
"op": "Add",
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 3,
"col_stop": 10,
"path": ""
}
}
},
"span": {
"line_start": 3,
"line_stop": 3,
"col_start": 3,
"col_stop": 10,
"path": ""
}
}
}
],
"span": {
"line_start": 1,
"line_stop": 4,
"col_start": 17,
"col_stop": 2,
"path": ""
}
},
"span": {
"line_start": 1,
"line_stop": 4,
"col_start": 1,
"col_stop": 2,
"path": ""
}
}
}
}

View File

@ -0,0 +1,4 @@
function main() {
let mut x = 10u32;
x += 20;
}

View File

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

View File

@ -0,0 +1,89 @@
// Copyright (C) 2019-2021 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::{assert_satisfied, parse_program, parse_program_with_input};
use leo_ast::Ast;
use leo_parser::parser;
pub fn parse_program_ast(file_string: &str) -> Ast {
const TEST_PROGRAM_PATH: &str = "";
let test_program_file_path = std::path::PathBuf::from(TEST_PROGRAM_PATH);
let mut ast = Ast::new(
parser::parse(test_program_file_path.to_str().expect("unwrap fail"), &file_string)
.expect("Failed to parse file."),
);
ast.canonicalize().expect("Failed to canonicalize program.");
ast
}
#[test]
fn test_big_self_in_circuit_replacement() {
// Check program is valid.
let program_string = include_str!("big_self_in_circuit_replacement.leo");
let program = parse_program(program_string).unwrap();
assert_satisfied(program);
// Check we get expected ast.
let ast = parse_program_ast(program_string);
let expected_json = include_str!("big_self_in_circuit_replacement.json");
let expected_ast: Ast = Ast::from_json_string(expected_json).expect("Unable to parse json.");
assert_eq!(expected_ast, ast);
}
#[test]
fn test_big_self_outside_circuit_fail() {
// Check program is invalid.
let program_string = include_str!("big_self_outside_circuit_fail.leo");
let program = parse_program(program_string);
assert!(program.is_err());
}
#[test]
fn test_array_expansion() {
let program_string = include_str!("array_expansion.leo");
let input_string = include_str!("input/array_expansion.in");
let program = parse_program_with_input(program_string, input_string).unwrap();
assert_satisfied(program);
let ast = parse_program_ast(program_string);
let expected_json = include_str!("array_expansion.json");
let expected_ast: Ast = Ast::from_json_string(expected_json).expect("Unable to parse json.");
assert_eq!(expected_ast, ast);
}
#[test]
fn test_array_size_zero_fail() {
let program_string = include_str!("array_size_zero_fail.leo");
let program = parse_program(program_string);
assert!(program.is_err());
}
#[test]
fn test_compound_assignment() {
let program_string = include_str!("compound_assignment.leo");
let program = parse_program(program_string).unwrap();
assert_satisfied(program);
let ast = parse_program_ast(program_string);
let expected_json = include_str!("compound_assignment.json");
let expected_ast: Ast = Ast::from_json_string(expected_json).expect("Unable to parse json.");
assert_eq!(expected_ast, ast);
}

View File

@ -15,11 +15,13 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
// allow the use of EdwardsTestCompiler::parse_program_from_string for tests
#![allow(deprecated)]
pub mod address;
pub mod array;
pub mod boolean;
pub mod canonicalization;
pub mod circuits;
pub mod compiler;
pub mod console;

View File

@ -14,6 +14,8 @@
// 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/>.
#![allow(clippy::from_over_into)]
#[macro_use]
extern crate thiserror;

View File

@ -13,10 +13,10 @@
// 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/>.
#![allow(clippy::upper_case_acronyms)]
//! Abstract syntax tree (ast) representation from leo-input.pest.
use pest::{error::Error, iterators::Pairs, Parser, Span};
#[derive(Parser)]
#[grammar = "leo-input.pest"]
pub struct LanguageParser;

View File

@ -13,6 +13,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/>.
#![allow(clippy::upper_case_acronyms)]
use crate::ast::Rule;

View File

@ -13,7 +13,6 @@
// 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/>.
#[macro_use]
extern crate pest_derive;
#[macro_use]

View File

@ -16,6 +16,7 @@
use std::io;
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Error)]
pub enum READMEError {
#[error("{}: {}", _0, _1)]

View File

@ -13,6 +13,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/>.
#![allow(clippy::upper_case_acronyms)]
//! The `README.md` file.

View File

@ -19,6 +19,8 @@
//! This module contains the [`parse_ast()`] method which calls the underlying [`parse()`]
//! method to create a new program ast.
#![allow(clippy::vec_init_then_push)]
#[macro_use]
extern crate thiserror;

View File

@ -309,8 +309,8 @@ impl ParserContext {
/// Otherwise, tries to parse the next token using [`parse_cast_expression`].
///
pub fn parse_exp_expression(&mut self) -> SyntaxResult<Expression> {
let mut exprs = vec![];
exprs.push(self.parse_cast_expression()?);
let mut exprs = vec![self.parse_cast_expression()?];
while self.eat(Token::Exp).is_some() {
exprs.push(self.parse_cast_expression()?);
}
@ -353,7 +353,7 @@ impl ParserContext {
/// Otherwise, tries to parse the next token using [`parse_access_expression`].
///
pub fn parse_unary_expression(&mut self) -> SyntaxResult<Expression> {
let mut ops = vec![];
let mut ops = Vec::new();
while let Some(token) = self.eat_any(&[Token::Not, Token::Minus, Token::BitNot]) {
ops.push(token);
}
@ -455,7 +455,7 @@ impl ParserContext {
}
}
Token::LeftParen => {
let mut arguments = vec![];
let mut arguments = Vec::new();
let end_span;
loop {
let end = self.eat(Token::RightParen);
@ -509,7 +509,7 @@ impl ParserContext {
///
pub fn parse_circuit_init(&mut self, identifier: Identifier) -> SyntaxResult<Expression> {
self.expect(Token::LeftCurly)?;
let mut members = vec![];
let mut members = Vec::new();
let end_span;
loop {
if let Some(end) = self.eat(Token::RightCurly) {
@ -603,7 +603,7 @@ impl ParserContext {
},
)))));
}
let mut args = vec![];
let mut args = Vec::new();
let end_span;
loop {
let end = self.eat(Token::RightParen);
@ -630,7 +630,7 @@ impl ParserContext {
Token::LeftSquare => {
if let Some(end) = self.eat(Token::RightSquare) {
return Ok(Expression::ArrayInline(ArrayInlineExpression {
elements: vec![],
elements: Vec::new(),
span: span + end.span,
}));
}

View File

@ -23,7 +23,7 @@ impl ParserContext {
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
///
pub fn parse_program(&mut self) -> SyntaxResult<Program> {
let mut imports = vec![];
let mut imports = Vec::new();
let mut circuits = IndexMap::new();
let mut functions = IndexMap::new();
// let mut tests = IndexMap::new();
@ -70,7 +70,7 @@ impl ParserContext {
}
Ok(Program {
name: String::new(),
expected_input: vec![],
expected_input: Vec::new(),
imports,
circuits,
functions,
@ -90,7 +90,7 @@ impl ParserContext {
}
let end_span;
let arguments = if self.eat(Token::LeftParen).is_some() {
let mut args = vec![];
let mut args = Vec::new();
loop {
if let Some(end) = self.eat(Token::RightParen) {
end_span = end.span;
@ -112,7 +112,7 @@ impl ParserContext {
args
} else {
end_span = name.span.clone();
vec![]
Vec::new()
};
Ok(Annotation {
name,
@ -126,7 +126,7 @@ impl ParserContext {
/// expressions within an import statement.
///
pub fn parse_package_accesses(&mut self) -> SyntaxResult<Vec<PackageAccess>> {
let mut out = vec![];
let mut out = Vec::new();
self.expect(Token::LeftParen)?;
while self.eat(Token::RightParen).is_none() {
let access = self.parse_package_access()?;
@ -293,7 +293,7 @@ impl ParserContext {
self.expect(Token::Circuit)?;
let name = self.expect_ident()?;
self.expect(Token::LeftCurly)?;
let mut members = vec![];
let mut members = Vec::new();
while self.eat(Token::RightCurly).is_none() {
let member = self.parse_circuit_member()?;
members.push(member);
@ -353,14 +353,14 @@ impl ParserContext {
/// and function definition.
///
pub fn parse_function(&mut self) -> SyntaxResult<(Identifier, Function)> {
let mut annotations = vec![];
let mut annotations = Vec::new();
while self.peek()?.token == Token::At {
annotations.push(self.parse_annotation()?);
}
let start = self.expect(Token::Function)?;
let name = self.expect_ident()?;
self.expect(Token::LeftParen)?;
let mut inputs = vec![];
let mut inputs = Vec::new();
while self.eat(Token::RightParen).is_none() {
let input = self.parse_function_input()?;
inputs.push(input);

View File

@ -72,7 +72,7 @@ impl ParserContext {
///
pub fn construct_assignee(expr: Expression) -> SyntaxResult<Assignee> {
let expr_span = expr.span().clone();
let mut accesses = vec![];
let mut accesses = Vec::new();
let identifier = Self::construct_assignee_access(expr, &mut accesses)?;
Ok(Assignee {
@ -139,7 +139,8 @@ impl ParserContext {
///
pub fn parse_block(&mut self) -> SyntaxResult<Block> {
let start = self.expect(Token::LeftCurly)?;
let mut statements = vec![];
let mut statements = Vec::new();
loop {
match self.eat(Token::RightCurly) {
None => {
@ -230,7 +231,7 @@ impl ParserContext {
}
SpannedToken { token, span } => return Err(SyntaxError::unexpected_str(&token, "formatted string", &span)),
};
let mut parameters = vec![];
let mut parameters = Vec::new();
while self.eat(Token::Comma).is_some() {
let param = self.parse_expression()?;
parameters.push(param);
@ -304,7 +305,7 @@ impl ParserContext {
///
pub fn parse_definition_statement(&mut self) -> SyntaxResult<DefinitionStatement> {
let declare = self.expect_oneof(&[Token::Let, Token::Const])?;
let mut variable_names = vec![];
let mut variable_names = Vec::new();
if self.eat(Token::LeftParen).is_some() {
variable_names.push(self.parse_variable_name()?);
while self.eat(Token::Comma).is_some() {

View File

@ -61,7 +61,7 @@ impl ParserContext {
ArrayDimensions(vec![int])
} else {
self.expect(Token::LeftParen)?;
let mut dimensions = vec![];
let mut dimensions = Vec::new();
loop {
if let Some((int, _)) = self.eat_int() {
dimensions.push(int);
@ -89,7 +89,7 @@ impl ParserContext {
let span = ident.span.clone();
(Type::Circuit(ident), span)
} else if let Some(token) = self.eat(Token::LeftParen) {
let mut types = vec![];
let mut types = Vec::new();
let end_span;
loop {
if let Some(end) = self.eat(Token::RightParen) {

View File

@ -43,8 +43,8 @@ pub fn find_tests<T: AsRef<Path>>(path: T, out: &mut Vec<(String, String)>) {
#[test]
pub fn parser_pass_tests() {
let mut pass = 0;
let mut fail = vec![];
let mut tests = vec![];
let mut fail = Vec::new();
let mut tests = Vec::new();
let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_dir.push("../tests/pass/parse/");
find_tests(&test_dir, &mut tests);
@ -76,8 +76,8 @@ pub fn parser_pass_tests() {
#[test]
pub fn parser_fail_tests() {
let mut pass = 0;
let mut fail = vec![];
let mut tests = vec![];
let mut fail = Vec::new();
let mut tests = Vec::new();
let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
test_dir.push("../tests/fail/parse/");
find_tests(&test_dir, &mut tests);

View File

@ -106,7 +106,7 @@ impl Token {
let mut i = 1;
let mut in_escape = false;
let mut start = 1usize;
let mut segments = vec![];
let mut segments = Vec::new();
while i < input.len() {
if !in_escape {
if input[i] == b'"' {
@ -212,7 +212,7 @@ impl Token {
let (input, comment) = if let Some(eol) = eol {
(&input[(eol + 1)..], &input[..eol])
} else {
(&input[input.len()..input.len()], &input[..])
(&input[input.len()..input.len()], input)
};
return (
input,
@ -226,7 +226,7 @@ impl Token {
let (input, comment) = if let Some(eol) = eol {
(&input[(eol + 4)..], &input[..eol + 4])
} else {
(&input[input.len()..input.len()], &input[..])
(&input[input.len()..input.len()], input)
};
return (
input,

View File

@ -34,7 +34,7 @@ use std::rc::Rc;
pub(crate) fn tokenize(path: &str, source: &str) -> Result<Vec<SpannedToken>, TokenError> {
let path = Rc::new(path.to_string());
let mut input = source.as_bytes();
let mut tokens = vec![];
let mut tokens = Vec::new();
let mut index = 0usize;
let mut line_no = 1usize;
let mut line_start = 0usize;

View File

@ -15,6 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::module_inception)]
#![allow(clippy::upper_case_acronyms)]
#[macro_use]
extern crate thiserror;