merge testnet3 regen tests

This commit is contained in:
collin 2022-07-18 15:33:40 -07:00
commit 66b0fcc885
479 changed files with 5901 additions and 5140 deletions

View File

@ -46,7 +46,7 @@ commands:
jobs:
check-style:
docker:
- image: cimg/rust:1.61
- image: cimg/rust:1.62
resource_class: xlarge
steps:
- checkout
@ -61,7 +61,7 @@ jobs:
clippy:
docker:
- image: cimg/rust:1.61
- image: cimg/rust:1.62
resource_class: xlarge
steps:
- checkout
@ -79,7 +79,7 @@ jobs:
# code-cov:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# environment:
# RUSTC_BOOTSTRAP: 1
@ -121,7 +121,7 @@ jobs:
leo-executable:
docker:
- image: cimg/rust:1.61
- image: cimg/rust:1.62
resource_class: xlarge
steps:
- checkout
@ -139,7 +139,7 @@ jobs:
#
# leo-new:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -152,7 +152,7 @@ jobs:
#
# leo-init:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -165,7 +165,7 @@ jobs:
#
# leo-clean:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -178,7 +178,7 @@ jobs:
#
# leo-setup:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -191,7 +191,7 @@ jobs:
# leo-add-remove:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -205,7 +205,7 @@ jobs:
# todo (collin): uncomment after compiler refactor
# leo-check-constraints:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -218,7 +218,7 @@ jobs:
#
# leo-login-logout:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -231,7 +231,7 @@ jobs:
#
# leo-clone:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:
@ -244,7 +244,7 @@ jobs:
#
# leo-publish:
# docker:
# - image: cimg/rust:1.61
# - image: cimg/rust:1.62
# resource_class: xlarge
# steps:
# - attach_workspace:

1184
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@ path = "leo/main.rs"
[workspace]
members = [
"compiler/compiler",
"compiler/parser",
"docs/grammar",
"errors",
"leo/package",
@ -46,25 +47,29 @@ version = "1.5.3"
path = "./leo/package"
version = "1.5.3"
[dependencies.leo-parser]
path = "./compiler/parser"
version = "1.5.3"
[dependencies.leo-span]
path = "./compiler/span"
version = "1.5.3"
[dependencies.aleo]
git = "https://github.com/AleoHQ/aleo.git"
rev = "a0895e0"
rev = "220e56"
[dependencies.snarkvm]
#path = "../snarkVM"
git = "https://github.com/AleoHQ/snarkVM.git"
rev = "f25e0b2"
rev = "5657881"
features = ["circuit", "console"]
[dependencies.backtrace]
version = "0.3.66"
[dependencies.clap]
version = "3.1"
version = "3.2"
features = ["derive", "env"]
[dependencies.color-backtrace]

View File

@ -24,19 +24,12 @@ pub struct CircuitVariableInitializer {
/// The expression to initialize the field with.
/// When `None`, a binding, in scope, with the name will be used instead.
pub expression: Option<Expression>,
/// `true` if the circuit is a `record` type.
pub is_record: bool,
}
impl fmt::Display for CircuitVariableInitializer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(expr) = &self.expression {
write!(f, "{}: {}", self.identifier, expr)?;
if self.is_record {
write!(f, "private")
} else {
write!(f, "")
}
write!(f, "{}: {}", self.identifier, expr)
} else {
write!(f, "{}", self.identifier)
}

View File

@ -32,13 +32,31 @@ pub enum Literal {
/// A group literal, either product or affine.
/// For example, `42group` or `(12, 52)group`.
Group(Box<GroupLiteral>),
/// An integer literal, e.g., `42`.
Integer(IntegerType, String, #[serde(with = "leo_span::span_json")] Span),
/// A scalar literal, e.g. `1scalar`.
/// An unsigned number followed by the keyword `scalar`.
Scalar(String, #[serde(with = "leo_span::span_json")] Span),
/// A string literal, e.g., `"foobar"`.
String(String, #[serde(with = "leo_span::span_json")] Span),
/// An 8-bit signed integer literal, e.g., `42i8`.
I8(String, #[serde(with = "leo_span::span_json")] Span),
/// A 16-bit signed integer literal, e.g., `42i16`.
I16(String, #[serde(with = "leo_span::span_json")] Span),
/// A 32-bit signed integer literal, e.g., `42i32`.
I32(String, #[serde(with = "leo_span::span_json")] Span),
/// A 64-bit signed integer literal, e.g., `42i64`.
I64(String, #[serde(with = "leo_span::span_json")] Span),
/// A 128-bit signed integer literal, e.g., `42i128`.
I128(String, #[serde(with = "leo_span::span_json")] Span),
/// A 8-bit unsigned integer literal, e.g., `42u8`.
U8(String, #[serde(with = "leo_span::span_json")] Span),
/// A 16-bit unsigned integer literal, e.g., `42u16`.
U16(String, #[serde(with = "leo_span::span_json")] Span),
/// A 32-bit unsigned integer literal, e.g., `42u32`.
U32(String, #[serde(with = "leo_span::span_json")] Span),
/// A 64-bit unsigned integer literal, e.g., `42u64`.
U64(String, #[serde(with = "leo_span::span_json")] Span),
/// A 128-bit unsigned integer literal, e.g., `42u128`.
U128(String, #[serde(with = "leo_span::span_json")] Span),
}
impl fmt::Display for Literal {
@ -46,11 +64,20 @@ impl fmt::Display for Literal {
match &self {
Self::Address(address, _) => write!(f, "{}", address),
Self::Boolean(boolean, _) => write!(f, "{}", boolean),
Self::I8(integer, _) => write!(f, "{}i8", integer),
Self::I16(integer, _) => write!(f, "{}i16", integer),
Self::I32(integer, _) => write!(f, "{}i32", integer),
Self::I64(integer, _) => write!(f, "{}i64", integer),
Self::I128(integer, _) => write!(f, "{}i128", integer),
Self::Field(field, _) => write!(f, "{}field", field),
Self::Group(group) => write!(f, "{}group", group),
Self::Integer(type_, value, _) => write!(f, "{}{}", value, type_),
Self::Scalar(scalar, _) => write!(f, "{}scalar", scalar),
Self::String(string, _) => write!(f, "{}", string),
Self::U8(integer, _) => write!(f, "{}u8", integer),
Self::U16(integer, _) => write!(f, "{}u16", integer),
Self::U32(integer, _) => write!(f, "{}u32", integer),
Self::U64(integer, _) => write!(f, "{}u64", integer),
Self::U128(integer, _) => write!(f, "{}u128", integer),
}
}
}
@ -61,9 +88,18 @@ impl Node for Literal {
Self::Address(_, span)
| Self::Boolean(_, span)
| Self::Field(_, span)
| Self::Integer(_, _, span)
| Self::I8(_, span)
| Self::I16(_, span)
| Self::I32(_, span)
| Self::I64(_, span)
| Self::I128(_, span)
| Self::Scalar(_, span)
| Self::String(_, span) => *span,
| Self::String(_, span)
| Self::U8(_, span)
| Self::U16(_, span)
| Self::U32(_, span)
| Self::U64(_, span)
| Self::U128(_, span) => *span,
Self::Group(group) => match &**group {
GroupLiteral::Single(_, span) => *span,
GroupLiteral::Tuple(tuple) => tuple.span,
@ -76,9 +112,18 @@ impl Node for Literal {
Self::Address(_, span)
| Self::Boolean(_, span)
| Self::Field(_, span)
| Self::Integer(_, _, span)
| Self::I8(_, span)
| Self::I16(_, span)
| Self::I32(_, span)
| Self::I64(_, span)
| Self::I128(_, span)
| Self::Scalar(_, span)
| Self::String(_, span) => *span = new_span,
| Self::String(_, span)
| Self::U8(_, span)
| Self::U16(_, span)
| Self::U32(_, span)
| Self::U64(_, span)
| Self::U128(_, span) => *span = new_span,
Self::Group(group) => match &mut **group {
GroupLiteral::Single(_, span) => *span = new_span,
GroupLiteral::Tuple(tuple) => tuple.span = new_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::{Identifier, IntegerType, Node};
use crate::{Identifier, Node};
use leo_span::Span;
use serde::{Deserialize, Serialize};

View File

@ -41,9 +41,10 @@ pub struct InputAst {
impl InputAst {
/// Returns all values of the input AST for execution with `leo run`.
pub fn values(&self) -> Vec<String> {
pub fn program_inputs(&self, program_name: &str) -> Vec<String> {
self.sections
.iter()
.filter(|section| section.name() == program_name)
.flat_map(|section| {
section
.definitions

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::{Expression, GroupLiteral, IntegerType, Literal, Node, Type, UnaryOperation};
use crate::{Expression, GroupLiteral, Literal, Node, Type, UnaryOperation};
use leo_errors::{InputError, LeoError, Result};
use serde::{Deserialize, Serialize};
@ -26,7 +26,16 @@ pub enum InputValue {
Boolean(bool),
Field(String),
Group(GroupLiteral),
Integer(IntegerType, String),
I8(String),
I16(String),
I32(String),
I64(String),
I128(String),
U8(String),
U16(String),
U32(String),
U64(String),
U128(String),
}
impl TryFrom<(Type, Expression)> for InputValue {
@ -38,13 +47,16 @@ impl TryFrom<(Type, Expression)> for InputValue {
(Type::Boolean, Literal::Boolean(value, _)) => Self::Boolean(value),
(Type::Field, Literal::Field(value, _)) => Self::Field(value),
(Type::Group, Literal::Group(value)) => Self::Group(*value),
(Type::IntegerType(expected), Literal::Integer(actual, value, span)) => {
if expected == actual {
Self::Integer(expected, value)
} else {
return Err(InputError::unexpected_type(expected.to_string(), actual, span).into());
}
}
(Type::I8, Literal::I8(value, _)) => Self::I8(value),
(Type::I16, Literal::I16(value, _)) => Self::I16(value),
(Type::I32, Literal::I32(value, _)) => Self::I32(value),
(Type::I64, Literal::I64(value, _)) => Self::I64(value),
(Type::I128, Literal::I128(value, _)) => Self::I128(value),
(Type::U8, Literal::U8(value, _)) => Self::U8(value),
(Type::U16, Literal::U16(value, _)) => Self::U16(value),
(Type::U32, Literal::U32(value, _)) => Self::U32(value),
(Type::U64, Literal::U64(value, _)) => Self::U64(value),
(Type::U128, Literal::U128(value, _)) => Self::U128(value),
(x, y) => {
return Err(InputError::unexpected_type(x, &y, y.span()).into());
}
@ -64,7 +76,16 @@ impl fmt::Display for InputValue {
InputValue::Boolean(ref boolean) => write!(f, "{}", boolean),
InputValue::Group(ref group) => write!(f, "{}", group),
InputValue::Field(ref field) => write!(f, "{}", field),
InputValue::Integer(ref type_, ref number) => write!(f, "{}{:?}", number, type_),
InputValue::I8(ref integer) => write!(f, "{}", integer),
InputValue::I16(ref integer) => write!(f, "{}", integer),
InputValue::I32(ref integer) => write!(f, "{}", integer),
InputValue::I64(ref integer) => write!(f, "{}", integer),
InputValue::I128(ref integer) => write!(f, "{}", integer),
InputValue::U8(ref integer) => write!(f, "{}", integer),
InputValue::U16(ref integer) => write!(f, "{}", integer),
InputValue::U32(ref integer) => write!(f, "{}", integer),
InputValue::U64(ref integer) => write!(f, "{}", integer),
InputValue::U128(ref integer) => write!(f, "{}", integer),
}
}
}

View File

@ -24,3 +24,9 @@ pub struct Section {
pub definitions: Vec<Definition>,
pub span: Span,
}
impl Section {
pub fn name(&self) -> String {
self.name.to_string()
}
}

View File

@ -54,6 +54,9 @@ pub use self::statements::*;
pub mod types;
pub use self::types::*;
pub mod value;
pub use self::value::*;
mod node;
pub use node::*;
@ -63,7 +66,7 @@ use leo_errors::{AstError, Result};
///
/// The [`Ast`] type represents a Leo program as a series of recursive data types.
/// These data types form a tree that begins from a [`Program`] type root.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Ast {
ast: Program,
}

View File

@ -205,7 +205,9 @@ pub trait StatementReconstructor: ExpressionReconstructor {
variable: input.variable,
type_: input.type_,
start: self.reconstruct_expression(input.start).0,
start_value: input.start_value,
stop: self.reconstruct_expression(input.stop).0,
stop_value: input.stop_value,
block: self.reconstruct_block(input.block),
inclusive: input.inclusive,
span: input.span,
@ -258,6 +260,11 @@ pub trait ProgramReconstructor: StatementReconstructor {
name: input.name,
network: input.network,
expected_input: input.expected_input,
imports: input
.imports
.into_iter()
.map(|(id, import)| (id, self.reconstruct_import(import)))
.collect(),
functions: input
.functions
.into_iter()
@ -285,4 +292,8 @@ pub trait ProgramReconstructor: StatementReconstructor {
fn reconstruct_circuit(&mut self, input: Circuit) -> Circuit {
input
}
fn reconstruct_import(&mut self, input: Program) -> Program {
input
}
}

View File

@ -173,6 +173,8 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
/// A Visitor trait for the program represented by the AST.
pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
fn visit_program(&mut self, input: &'a Program) {
input.imports.values().for_each(|import| self.visit_import(import));
input
.functions
.values()
@ -189,4 +191,8 @@ pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
}
fn visit_circuit(&mut self, _input: &'a Circuit) {}
fn visit_import(&mut self, input: &'a Program) {
self.visit_program(input)
}
}

View File

@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
/// Stores the Leo program abstract syntax tree.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Program {
/// The name of the program.
pub name: String,
@ -33,6 +33,8 @@ pub struct Program {
/// Expected main function inputs.
/// Empty after parsing.
pub expected_input: Vec<FunctionInput>,
/// A map from import names to import definitions.
pub imports: IndexMap<Identifier, Program>,
/// A map from function names to function definitions.
pub functions: IndexMap<Identifier, Function>,
/// A map from circuit names to circuit definitions.
@ -41,6 +43,9 @@ pub struct Program {
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (id, _import) in self.imports.iter() {
writeln!(f, "import {}.leo;", id)?;
}
for (_, function) in self.functions.iter() {
function.fmt(f)?;
writeln!(f,)?;
@ -60,6 +65,7 @@ impl Default for Program {
name: String::new(),
network: String::new(),
expected_input: vec![],
imports: IndexMap::new(),
functions: IndexMap::new(),
circuits: IndexMap::new(),
}

View File

@ -19,18 +19,18 @@ use std::fmt;
/// The sort of bindings to introduce, either `let` or `const`.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Declare {
pub enum DeclarationType {
/// This is a `const` binding.
Const,
/// This is a `let` binding.
Let,
}
impl fmt::Display for Declare {
impl fmt::Display for DeclarationType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Declare::Const => write!(f, "const"),
Declare::Let => write!(f, "let"),
DeclarationType::Const => write!(f, "const"),
DeclarationType::Let => write!(f, "let"),
}
}
}

View File

@ -20,14 +20,14 @@ use leo_span::Span;
use serde::{Deserialize, Serialize};
use std::fmt;
mod declare;
pub use declare::*;
mod declaration_type;
pub use declaration_type::*;
/// A `let` or `const` declaration statement.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct DefinitionStatement {
/// What sort of declaration is this? `let` or `const`?.
pub declaration_type: Declare,
pub declaration_type: DeclarationType,
/// The bindings / variable names to declare.
pub variable_name: Identifier,
/// The types of the bindings, if specified, or inferred otherwise.

View File

@ -14,10 +14,12 @@
// 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, Type};
use crate::{Block, Expression, Identifier, Node, Type, Value};
use leo_span::Span;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::fmt;
/// A bounded `for` loop statement `for variable in start .. =? stop block`.
@ -29,8 +31,14 @@ pub struct IterationStatement {
pub type_: Type,
/// The start of the iteration.
pub start: Expression,
/// The concrete value of `start`.
#[serde(skip)]
pub start_value: RefCell<Option<Value>>,
/// The end of the iteration, possibly `inclusive`.
pub stop: Expression,
/// The concrete value of `stop`.
#[serde(skip)]
pub stop_value: RefCell<Option<Value>>,
/// Whether `stop` is inclusive or not.
/// Signified with `=` when parsing.
pub inclusive: bool,

View File

@ -1,78 +0,0 @@
// Copyright (C) 2019-2022 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 leo_span::{sym, Symbol};
use serde::{Deserialize, Serialize};
use std::fmt;
/// Explicit integer type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IntegerType {
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
}
impl IntegerType {
/// Is the integer type a signed one?
pub fn is_signed(&self) -> bool {
use IntegerType::*;
matches!(self, I8 | I16 | I32 | I64 | I128)
}
/// Returns the symbol for the integer type.
pub fn symbol(self) -> Symbol {
match self {
Self::I8 => sym::i8,
Self::I16 => sym::i16,
Self::I32 => sym::i32,
Self::I64 => sym::i64,
Self::I128 => sym::i128,
Self::U8 => sym::u8,
Self::U16 => sym::u16,
Self::U32 => sym::u32,
Self::U64 => sym::u64,
Self::U128 => sym::u128,
}
}
}
impl fmt::Display for IntegerType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
IntegerType::U8 => write!(f, "u8"),
IntegerType::U16 => write!(f, "u16"),
IntegerType::U32 => write!(f, "u32"),
IntegerType::U64 => write!(f, "u64"),
IntegerType::U128 => write!(f, "u128"),
IntegerType::I8 => write!(f, "i8"),
IntegerType::I16 => write!(f, "i16"),
IntegerType::I32 => write!(f, "i32"),
IntegerType::I64 => write!(f, "i64"),
IntegerType::I128 => write!(f, "i128"),
}
}
}

View File

@ -14,9 +14,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/>.
pub mod integer_type;
pub use integer_type::*;
pub mod tuple;
pub use tuple::*;

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::{Identifier, IntegerType, Tuple};
use crate::{Identifier, Tuple};
use serde::{Deserialize, Serialize};
use std::fmt;
@ -31,14 +31,32 @@ pub enum Type {
Field,
/// The `group` type.
Group,
/// The 8-bit signed integer type.
I8,
/// The 16-bit signed integer type.
I16,
/// The 32-bit signed integer type.
I32,
/// The 64-bit signed integer type.
I64,
/// The 128-bit signed integer type.
I128,
/// A reference to a built in type.
Identifier(Identifier),
/// The `scalar` type.
Scalar,
/// The `string` type.
String,
/// An integer type.
IntegerType(IntegerType),
/// A reference to a built in type.
Identifier(Identifier),
/// The 8-bit unsigned integer type.
U8,
/// The 16-bit unsigned integer type.
U16,
/// The 32-bit unsigned integer type.
U32,
/// The 64-bit unsigned integer type.
U64,
/// The 128-bit unsigned integer type.
U128,
/// A static tuple of at least one type.
Tuple(Tuple),
@ -53,16 +71,24 @@ impl Type {
///
/// Flattens array syntax: `[[u8; 1]; 2] == [u8; (2, 1)] == true`
///
// TODO: Does not seem to flatten?
pub fn eq_flat(&self, other: &Self) -> bool {
match (self, other) {
(Type::Address, Type::Address)
| (Type::Boolean, Type::Boolean)
| (Type::Field, Type::Field)
| (Type::Group, Type::Group)
| (Type::I8, Type::I8)
| (Type::I16, Type::I16)
| (Type::I32, Type::I32)
| (Type::I64, Type::I64)
| (Type::I128, Type::I128)
| (Type::Scalar, Type::Scalar)
| (Type::String, Type::String) => true,
(Type::IntegerType(left), Type::IntegerType(right)) => left.eq(right),
| (Type::String, Type::String)
| (Type::U8, Type::U8)
| (Type::U16, Type::U16)
| (Type::U32, Type::U32)
| (Type::U64, Type::U64)
| (Type::U128, Type::U128) => true,
(Type::Tuple(left), Type::Tuple(right)) => left
.iter()
.zip(right.iter())
@ -80,10 +106,19 @@ impl fmt::Display for Type {
Type::Boolean => write!(f, "bool"),
Type::Field => write!(f, "field"),
Type::Group => write!(f, "group"),
Type::I8 => write!(f, "i8"),
Type::I16 => write!(f, "i16"),
Type::I32 => write!(f, "i32"),
Type::I64 => write!(f, "i64"),
Type::I128 => write!(f, "i128"),
Type::Identifier(ref variable) => write!(f, "circuit {}", variable),
Type::Scalar => write!(f, "scalar"),
Type::String => write!(f, "string"),
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
Type::Identifier(ref variable) => write!(f, "circuit {}", variable),
Type::U8 => write!(f, "u8"),
Type::U16 => write!(f, "u16"),
Type::U32 => write!(f, "u32"),
Type::U64 => write!(f, "u64"),
Type::U128 => write!(f, "u128"),
Type::Tuple(ref tuple) => write!(f, "{}", tuple),
Type::Err => write!(f, "error"),
}

View File

@ -0,0 +1,917 @@
// Copyright (C) 2019-2022 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::{GroupLiteral, Identifier, Literal, Type};
use leo_errors::{type_name, FlattenError, LeoError, Result};
use leo_span::{Span, Symbol};
use indexmap::IndexMap;
use std::{
fmt::Display,
ops::{BitAnd, BitOr, BitXor, Not},
};
// TODO: Consider refactoring this module to use the console implementations from snarkVM.
// This is temporary since the currently unused code is used in constant folding.
#[allow(dead_code)]
// Macro for making implementing unary operations over appropriate types easier.
macro_rules! implement_const_unary {
(
@overflowing
name: $name:ident,
method: $method:ident,
string: $str:expr,
patterns: [$([$type:ident, $m_type:ty]),+]
) => {
implement_const_unary!{
name: $name,
patterns: [$([
t: $type,
l: |l: $m_type, span| l.$method().ok_or_else(|| FlattenError::unary_overflow(l, $str, span))
]),+]
}
};
(
@non-overflowing
name: $name:ident,
method: $method:ident,
patterns: [$([$type:ident, $m_type:ty]),+]
) => {
implement_const_unary!{
name: $name,
patterns: [$([
t: $type,
l: |l: $m_type, _| -> Result<$m_type> { Ok(l.$method()) }
]),+]
}
};
(
name: $name:ident,
patterns: [$([
t: $type:ident,
l: $logic:expr
]),+]
) => {
// TODO: This is temporary since the currently unused code is used in constant folding.
#[allow(dead_code)]
pub(crate) fn $name(self, span: Span) -> Result<Self> {
use Value::*;
match self {
$(
$type(v, _) => {
Ok($type($logic(v.into(), span)?, span))
},
)+
// Unreachable because type checking should have already caught this and errored out.
s => unreachable!("Const operation not supported {}.{}()", type_name(&s), stringify!($name))
}
}
};
}
// Macro for making implementing binary operations over appropriate types easier.
macro_rules! implement_const_binary {
// for overflowing operations that can overflow
(
@overflowing
name: $name:ident,
method: $method:ident,
string: $str:expr,
patterns: [$(
// lhs, rhs, out, method left, method right
[$lhs:ident, [$($rhs:ident),+], $out:ident, $m_lhs:ty, $m_rhs:ty]
),+]
) => {
implement_const_binary!{
name: $name,
patterns: [$([
types: $lhs, [$($rhs),+], $out,
logic: |l: $m_lhs, r: $m_rhs, t, span| l.$method(r).ok_or_else(|| FlattenError::binary_overflow(l, $str, r, t, span))
]),+]
}
};
// for wrapping math operations
(
@non-overflowing
name: $name:ident,
method: $method:ident,
patterns: [$(
// lhs, rhs, out, method left, method right, method output
[$lhs:ident, [$($rhs:ident),+], $out:ident, $m_lhs:ty, $m_rhs:ty]
),+]
) => {
implement_const_binary!{
name: $name,
patterns: [$([
types: $lhs, [$($rhs),+], $out,
logic: |l: $m_lhs, r: $m_rhs, _, _| -> Result<$m_lhs> {Ok(l.$method(r))}
]),+]
}
};
// for cmp operations
(
@cmp
name: $name:ident,
method: $method:ident,
string: $str:expr,
patterns: [$(
// lhs, rhs, out, method left, method right, method output
[$lhs:ident, [$($rhs:ident),+], $out:ident, $m_lhs:ty, $m_rhs:ty]
),+]
) => {
implement_const_binary!{
name: $name,
patterns: [$([
types: $lhs, [$($rhs),+], $out,
logic: |l: $m_lhs, r: $m_rhs, _, _| -> Result<bool> {Ok(l.$method(&r))}
]),+]
}
};
(
name: $name:ident,
patterns: [$([
types: $lhs:ident, [$($rhs:ident),+], $out:ident,
logic: $logic:expr
]),+]
) => {
// This is temporary since the currently unused code is used in constant folding.
#[allow(dead_code)]
pub(crate) fn $name(self, other: Self, span: Span) -> Result<Self> {
use Value::*;
match (self, other) {
$(
$(
($lhs(types, _), $rhs(rhs, _)) => {
let rhs_type = type_name(&rhs);
let out = $logic(types, rhs.into(), rhs_type, span)?;
Ok($out(out, span))
},
)+
)+
// Unreachable because type checking should have already caught this and errored out.
(s, o) => unreachable!("Const operation not supported {}.{}({})", type_name(&s), stringify!($name), type_name(&o))
}
}
};
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value {
Input(Type, Identifier),
Address(String, Span),
Boolean(bool, Span),
Circuit(Identifier, IndexMap<Symbol, Value>),
Field(String, Span),
Group(Box<GroupLiteral>),
I8(i8, Span),
I16(i16, Span),
I32(i32, Span),
I64(i64, Span),
I128(i128, Span),
U8(u8, Span),
U16(u16, Span),
U32(u32, Span),
U64(u64, Span),
U128(u128, Span),
Scalar(String, Span),
String(String, Span),
}
impl Value {
// TODO: This is temporary since the currently unused code is used in constant folding.
#[allow(dead_code)]
pub(crate) fn is_supported_const_fold_type(&self) -> bool {
use Value::*;
matches!(
self,
Boolean(_, _)
| I8(_, _)
| I16(_, _)
| I32(_, _)
| I64(_, _)
| I128(_, _)
| U8(_, _)
| U16(_, _)
| U32(_, _)
| U64(_, _)
| U128(_, _)
)
}
implement_const_unary!(
@overflowing
name: abs,
method: checked_abs,
string: "abs",
patterns: [
[I8, i8],
[I16, i16],
[I32, i32],
[I64, i64],
[I128, i128]
]
);
implement_const_unary!(
@non-overflowing
name: abs_wrapped,
method: wrapping_abs,
patterns: [
[I8, i8],
[I16, i16],
[I32, i32],
[I64, i64],
[I128, i128]
]
);
implement_const_unary!(
@overflowing
name: neg,
method: checked_neg,
string: "neg",
patterns: [
// [Field, Field],
// [Group, Group],
[I8, i8],
[I16, i16],
[I32, i32],
[I64, i64],
[I128, i128]
]
);
implement_const_unary!(
@non-overflowing
name: not,
method: not,
patterns: [
[Boolean, bool],
[I8, i8],
[I16, i16],
[I32, i32],
[I64, i64],
[I128, i128],
[U8, u8],
[U16, u16],
[U32, u32],
[U64, u64],
[U128, u128]
]
);
implement_const_binary!(
@overflowing
name: add,
method: checked_add,
string: "+",
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: add_wrapped,
method: wrapping_add,
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: bitand,
method: bitand,
patterns: [
[Boolean, [Boolean], Boolean, bool, bool],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
]
);
implement_const_binary!(
@overflowing
name: div,
method: checked_div,
string: "/",
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: div_wrapped,
method: wrapping_div,
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@cmp
name: eq,
method: eq,
string: "==",
patterns: [
[Boolean, [Boolean], Boolean, bool, bool],
[I8, [I8], Boolean, i8, i8],
[I16, [I16], Boolean, i16, i16],
[I32, [I32], Boolean, i32, i32],
[I64, [I64], Boolean, i64, i64],
[I128, [I128], Boolean, i128, i128],
[U8, [U8], Boolean, u8, u8],
[U16, [U16], Boolean, u16, u16],
[U32, [U32], Boolean, u32, u32],
[U64, [U64], Boolean, u64, u64],
[U128, [U128], Boolean, u128, u128]
]
);
implement_const_binary!(
@cmp
name: ge,
method: ge,
string: ">=",
patterns: [
[I8, [I8], Boolean, i8, i8],
[I16, [I16], Boolean, i16, i16],
[I32, [I32], Boolean, i32, i32],
[I64, [I64], Boolean, i64, i64],
[I128, [I128], Boolean, i128, i128],
[U8, [U8], Boolean, u8, u8],
[U16, [U16], Boolean, u16, u16],
[U32, [U32], Boolean, u32, u32],
[U64, [U64], Boolean, u64, u64],
[U128, [U128], Boolean, u128, u128]
]
);
implement_const_binary!(
@cmp
name: gt,
method: gt,
string: ">",
patterns: [
[I8, [I8], Boolean, i8, i8],
[I16, [I16], Boolean, i16, i16],
[I32, [I32], Boolean, i32, i32],
[I64, [I64], Boolean, i64, i64],
[I128, [I128], Boolean, i128, i128],
[U8, [U8], Boolean, u8, u8],
[U16, [U16], Boolean, u16, u16],
[U32, [U32], Boolean, u32, u32],
[U64, [U64], Boolean, u64, u64],
[U128, [U128], Boolean, u128, u128]
]
);
implement_const_binary!(
@cmp
name: le,
method: le,
string: "<=",
patterns: [
[I8, [I8], Boolean, i8, i8],
[I16, [I16], Boolean, i16, i16],
[I32, [I32], Boolean, i32, i32],
[I64, [I64], Boolean, i64, i64],
[I128, [I128], Boolean, i128, i128],
[U8, [U8], Boolean, u8, u8],
[U16, [U16], Boolean, u16, u16],
[U32, [U32], Boolean, u32, u32],
[U64, [U64], Boolean, u64, u64],
[U128, [U128], Boolean, u128, u128]
]
);
implement_const_binary!(
@cmp
name: lt,
method: lt,
string: "<",
patterns: [
[I8, [I8], Boolean, i8, i8],
[I16, [I16], Boolean, i16, i16],
[I32, [I32], Boolean, i32, i32],
[I64, [I64], Boolean, i64, i64],
[I128, [I128], Boolean, i128, i128],
[U8, [U8], Boolean, u8, u8],
[U16, [U16], Boolean, u16, u16],
[U32, [U32], Boolean, u32, u32],
[U64, [U64], Boolean, u64, u64],
[U128, [U128], Boolean, u128, u128]
]
);
implement_const_binary!(
@overflowing
name: mul,
method: checked_mul,
string: "*",
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: mul_wrapped,
method: wrapping_mul,
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: bitor,
method: bitor,
patterns: [
[Boolean, [Boolean], Boolean, bool, bool],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
]
);
implement_const_binary!(
@overflowing
name: pow,
method: checked_pow,
string: "**",
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@non-overflowing
name: pow_wrapped,
method: wrapping_pow,
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@overflowing
name: shl,
method: checked_shl,
string: "<<",
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@non-overflowing
name: shl_wrapped,
method: wrapping_shl,
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@overflowing
name: shr,
method: checked_shr,
string: ">>",
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@non-overflowing
name: shr_wrapped,
method: wrapping_shr,
patterns: [
[I8, [U8, U16, U32], I8, i8, u32],
[I16, [U8, U16, U32], I16, i16, u32],
[I32, [U8, U16, U32], I32, i32, u32],
[I64, [U8, U16, U32], I64, i64, u32],
[I128, [U8, U16, U32], I128, i128, u32],
[U8, [U8, U16, U32], U8, u8, u32],
[U16, [U8, U16, U32], U16, u16, u32],
[U32, [U8, U16, U32], U32, u32, u32],
[U64, [U8, U16, U32], U64, u64, u32],
[U128, [U8, U16, U32], U128, u128, u32]
]
);
implement_const_binary!(
@overflowing
name: sub,
method: checked_sub,
string: "-",
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: sub_wrapped,
method: wrapping_sub,
patterns: [
// [Field, [Field], Field, _, _],
// [Group, [Group], Group, _, _],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
//[Scalar, [Scalar], Scalar, _, _],
]
);
implement_const_binary!(
@non-overflowing
name: xor,
method: bitxor,
patterns: [
[Boolean, [Boolean], Boolean, bool, bool],
[I8, [I8], I8, i8, i8],
[I16, [I16], I16, i16, i16],
[I32, [I32], I32, i32, i32],
[I64, [I64], I64, i64, i64],
[I128, [I128], I128, i128, i128],
[U8, [U8], U8, u8, u8],
[U16, [U16], U16, u16, u16],
[U32, [U32], U32, u32, u32],
[U64, [U64], U64, u64, u64],
[U128, [U128], U128, u128, u128]
]
);
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value::*;
match self {
Input(type_, ident) => write!(f, "input var {}: {type_}", ident.name),
Address(val, _) => write!(f, "{val}"),
Circuit(val, _) => write!(f, "{}", val.name),
Boolean(val, _) => write!(f, "{val}"),
Field(val, _) => write!(f, "{val}"),
Group(val) => write!(f, "{val}"),
I8(val, _) => write!(f, "{val}"),
I16(val, _) => write!(f, "{val}"),
I32(val, _) => write!(f, "{val}"),
I64(val, _) => write!(f, "{val}"),
I128(val, _) => write!(f, "{val}"),
U8(val, _) => write!(f, "{val}"),
U16(val, _) => write!(f, "{val}"),
U32(val, _) => write!(f, "{val}"),
U64(val, _) => write!(f, "{val}"),
U128(val, _) => write!(f, "{val}"),
Scalar(val, _) => write!(f, "{val}"),
String(val, _) => write!(f, "{val}"),
}
}
}
impl TryFrom<Value> for i128 {
type Error = LeoError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
value.as_ref().try_into()
}
}
impl TryFrom<&Value> for i128 {
type Error = LeoError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
use Value::*;
match value {
U8(val, span) => {
i128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
U16(val, span) => {
i128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
U32(val, span) => {
i128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
U64(val, span) => {
i128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
U128(val, span) => {
i128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
I8(val, _) => Ok(*val as i128),
I16(val, _) => Ok(*val as i128),
I32(val, _) => Ok(*val as i128),
I64(val, _) => Ok(*val as i128),
I128(val, _) => Ok(*val),
_ => unreachable!(),
}
}
}
impl TryFrom<Value> for u128 {
type Error = LeoError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
value.as_ref().try_into()
}
}
impl TryFrom<&Value> for u128 {
type Error = LeoError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
use Value::*;
match value {
I8(val, span) => {
u128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
I16(val, span) => {
u128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
I32(val, span) => {
u128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
I64(val, span) => {
u128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
I128(val, span) => {
u128::try_from(*val).map_err(|_| FlattenError::loop_has_neg_value(Type::from(value), *span).into())
}
U8(val, _) => Ok(*val as u128),
U16(val, _) => Ok(*val as u128),
U32(val, _) => Ok(*val as u128),
U64(val, _) => Ok(*val as u128),
U128(val, _) => Ok(*val),
_ => unreachable!(),
}
}
}
impl AsRef<Value> for Value {
fn as_ref(&self) -> &Self {
self
}
}
impl From<Value> for Type {
fn from(v: Value) -> Self {
v.as_ref().into()
}
}
impl From<&Value> for Type {
fn from(v: &Value) -> Self {
use Value::*;
match v {
Input(type_, _) => type_.clone(),
Address(_, _) => Type::Address,
Boolean(_, _) => Type::Boolean,
Circuit(ident, _) => Type::Identifier(*ident),
Field(_, _) => Type::Field,
Group(_) => Type::Group,
I8(_, _) => Type::I8,
I16(_, _) => Type::I16,
I32(_, _) => Type::I32,
I64(_, _) => Type::I64,
I128(_, _) => Type::I128,
U8(_, _) => Type::U8,
U16(_, _) => Type::U16,
U32(_, _) => Type::U32,
U64(_, _) => Type::U64,
U128(_, _) => Type::U128,
Scalar(_, _) => Type::Scalar,
String(_, _) => Type::String,
}
}
}
// TODO: Consider making this `Option<Value>` instead of `Value`.
impl From<&Literal> for Value {
/// Converts a literal to a value.
/// This should only be invoked on literals that are known to be valid.
fn from(literal: &Literal) -> Self {
match literal {
Literal::Address(string, span) => Self::Address(string.clone(), *span),
Literal::Boolean(bool, span) => Self::Boolean(*bool, *span),
Literal::Field(string, span) => Self::Field(string.clone(), *span),
Literal::Group(group_literal) => Self::Group(group_literal.clone()),
Literal::Scalar(string, span) => Self::Scalar(string.clone(), *span),
Literal::String(string, span) => Self::String(string.clone(), *span),
Literal::I8(string, span) => Self::I8(string.parse::<i8>().unwrap(), *span),
Literal::I16(string, span) => Self::I16(string.parse::<i16>().unwrap(), *span),
Literal::I32(string, span) => Self::I32(string.parse::<i32>().unwrap(), *span),
Literal::I64(string, span) => Self::I64(string.parse::<i64>().unwrap(), *span),
Literal::I128(string, span) => Self::I128(string.parse::<i128>().unwrap(), *span),
Literal::U8(string, span) => Self::U8(string.parse::<u8>().unwrap(), *span),
Literal::U16(string, span) => Self::U16(string.parse::<u16>().unwrap(), *span),
Literal::U32(string, span) => Self::U32(string.parse::<u32>().unwrap(), *span),
Literal::U64(string, span) => Self::U64(string.parse::<u64>().unwrap(), *span),
Literal::U128(string, span) => Self::U128(string.parse::<u128>().unwrap(), *span),
}
}
}
impl From<Value> for Literal {
fn from(v: Value) -> Self {
use Value::*;
match v {
Input(_, _) => todo!("We need to test if this is hittable"),
Address(v, span) => Literal::Address(v, span),
Boolean(v, span) => Literal::Boolean(v, span),
Circuit(_ident, _values) => todo!("We need to test if this is hittable"),
Field(v, span) => Literal::Field(v, span),
Group(v) => Literal::Group(v),
I8(v, span) => Literal::I8(v.to_string(), span),
I16(v, span) => Literal::I16(v.to_string(), span),
I32(v, span) => Literal::I32(v.to_string(), span),
I64(v, span) => Literal::I64(v.to_string(), span),
I128(v, span) => Literal::I128(v.to_string(), span),
U8(v, span) => Literal::U8(v.to_string(), span),
U16(v, span) => Literal::U16(v.to_string(), span),
U32(v, span) => Literal::U32(v.to_string(), span),
U64(v, span) => Literal::U64(v.to_string(), span),
U128(v, span) => Literal::U128(v.to_string(), span),
Scalar(v, span) => Literal::Scalar(v, span),
String(v, span) => Literal::String(v, span),
}
}
}

View File

@ -54,9 +54,7 @@ pub struct Compiler<'a> {
}
impl<'a> Compiler<'a> {
///
/// Returns a new Leo compiler.
///
pub fn new(
program_name: String,
network: String,
@ -77,9 +75,7 @@ impl<'a> Compiler<'a> {
}
}
///
/// Returns a SHA256 checksum of the program file.
///
pub fn checksum(&self) -> Result<String> {
// Read in the main file as string
let unparsed_file = fs::read_to_string(&self.main_file_path)
@ -93,7 +89,7 @@ impl<'a> Compiler<'a> {
Ok(format!("{:x}", hash))
}
// Parses and stores a program file content from a string, constructs a syntax tree, and generates a program.
/// Parses and stores a program file content from a string, constructs a syntax tree, and generates a program.
pub fn parse_program_from_string(&mut self, program_string: &str, name: FileName) -> Result<()> {
// Register the source (`program_string`) in the source map.
let prg_sf = with_session_globals(|s| s.source_map.new_source(program_string, name));
@ -103,7 +99,7 @@ impl<'a> Compiler<'a> {
ast = ast.set_program_name(self.program_name.clone());
ast = ast.set_network(self.network.clone());
if self.output_options.ast_initial {
if self.output_options.initial_ast {
// Write the AST snapshot post parsing.
if self.output_options.spans_enabled {
ast.to_json_file(self.output_directory.clone(), "initial_ast.json")?;
@ -135,7 +131,7 @@ impl<'a> Compiler<'a> {
// Parse and serialize it.
let input_ast = leo_parser::parse_input(self.handler, &input_sf.src, input_sf.start_pos)?;
if self.output_options.ast_initial {
if self.output_options.initial_ast {
// Write the input AST snapshot post parsing.
if self.output_options.spans_enabled {
input_ast.to_json_file(self.output_directory.clone(), "initial_input_ast.json")?;
@ -153,33 +149,40 @@ impl<'a> Compiler<'a> {
Ok(())
}
///
/// Runs the symbol table pass.
///
pub fn symbol_table_pass(&self) -> Result<SymbolTable> {
CreateSymbolTable::do_pass((&self.ast, self.handler))
}
///
/// Runs the type checker pass.
///
pub fn type_checker_pass(&'a self, symbol_table: SymbolTable) -> Result<SymbolTable> {
TypeChecker::do_pass((&self.ast, self.handler, symbol_table))
}
///
/// Runs the loop unrolling pass.
pub fn loop_unrolling_pass(&mut self, symbol_table: SymbolTable) -> Result<SymbolTable> {
let (ast, symbol_table) = Unroller::do_pass((std::mem::take(&mut self.ast), self.handler, symbol_table))?;
self.ast = ast;
if self.output_options.unrolled_ast {
self.write_ast_to_json("unrolled_ast.json")?;
}
Ok(symbol_table)
}
/// Runs the compiler stages.
///
pub fn compiler_stages(&self) -> Result<SymbolTable> {
pub fn compiler_stages(&mut self) -> Result<SymbolTable> {
let st = self.symbol_table_pass()?;
let st = self.type_checker_pass(st)?;
// TODO: Make this pass optional.
let st = self.loop_unrolling_pass(st)?;
Ok(st)
}
///
/// Returns a compiled Leo program and prints the resulting bytecode.
/// TODO: Remove when code generation is ready to be integrated into the compiler.
///
// TODO: Remove when code generation is ready to be integrated into the compiler.
pub fn compile_and_generate_instructions(&mut self) -> Result<(SymbolTable, String)> {
self.parse_program()?;
let symbol_table = self.compiler_stages()?;
@ -189,11 +192,21 @@ impl<'a> Compiler<'a> {
Ok((symbol_table, bytecode))
}
///
/// Returns a compiled Leo program.
///
pub fn compile(&mut self) -> Result<SymbolTable> {
self.parse_program()?;
self.compiler_stages()
}
/// Writes the AST to a JSON file.
fn write_ast_to_json(&self, file_name: &str) -> Result<()> {
// Remove `Span`s if they are not enabled.
if self.output_options.spans_enabled {
self.ast.to_json_file(self.output_directory.clone(), file_name)?;
} else {
self.ast
.to_json_file_without_keys(self.output_directory.clone(), file_name, &["span"])?;
}
Ok(())
}
}

View File

@ -19,7 +19,9 @@ pub struct OutputOptions {
/// Whether spans are enabled in the output ASTs.
pub spans_enabled: bool,
/// If enabled writes the AST after parsing.
pub ast_initial: bool,
pub initial_ast: bool,
/// If enabled writes the input AST after parsing.
pub input_ast_initial: bool,
pub initial_input_ast: bool,
/// If enabled writes the AST after loop unrolling.
pub unrolled_ast: bool,
}

View File

@ -48,8 +48,9 @@ fn new_compiler(handler: &Handler, main_file_path: PathBuf) -> Compiler<'_> {
output_dir,
Some(OutputOptions {
spans_enabled: false,
input_ast_initial: true,
ast_initial: true,
initial_input_ast: true,
initial_ast: true,
unrolled_ast: true,
}),
)
}
@ -95,15 +96,16 @@ impl Namespace for CompileNamespace {
}
}
#[derive(Deserialize, PartialEq, Serialize)]
#[derive(Deserialize, PartialEq, Eq, Serialize)]
struct OutputItem {
pub initial_input_ast: String,
}
#[derive(Deserialize, PartialEq, Serialize)]
#[derive(Deserialize, PartialEq, Eq, Serialize)]
struct CompileOutput {
pub output: Vec<OutputItem>,
pub initial_ast: String,
pub unrolled_ast: String,
}
/// Get the path of the `input_file` given in `input` into `list`.
@ -136,6 +138,7 @@ fn collect_all_inputs(test: &Test) -> Result<Vec<PathBuf>, String> {
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<SymbolTable, LeoError> {
let st = parsed.symbol_table_pass()?;
let st = parsed.type_checker_pass(st)?;
let st = parsed.loop_unrolling_pass(st)?;
Ok(st)
}
@ -215,6 +218,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
}
let initial_ast = hash_file("/tmp/output/initial_ast.json");
let unrolled_ast = hash_file("/tmp/output/unrolled_ast.json");
if fs::read_dir("/tmp/output").is_ok() {
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
@ -223,6 +227,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
let final_output = CompileOutput {
output: output_items,
initial_ast,
unrolled_ast,
};
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
}

View File

@ -23,7 +23,8 @@ mod poseidon;
pub use poseidon::*;
use crate::Types;
use leo_ast::{IntegerType, Type};
use leo_ast::Type;
use leo_span::{sym, Symbol};
use indexmap::IndexSet;
@ -191,45 +192,45 @@ const ALL_TYPES: [Type; 16] = [
Type::Boolean,
Type::Field,
Type::Group,
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::I128),
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
Type::IntegerType(IntegerType::U64),
Type::IntegerType(IntegerType::U128),
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::I128,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::U128,
Type::Scalar,
Type::String,
];
const BOOL_INT_STRING_TYPES: [Type; 12] = [
Type::Boolean,
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::I128),
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
Type::IntegerType(IntegerType::U64),
Type::IntegerType(IntegerType::U128),
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::I128,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::U128,
Type::String,
];
const BOOL_INT64_STRING_TYPES: [Type; 10] = [
Type::Boolean,
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
Type::IntegerType(IntegerType::U64),
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::String,
];

View File

@ -101,14 +101,6 @@ impl ParserContext<'_> {
Ok(expr)
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// binary AND expression.
///
/// Otherwise, tries to parse the next token using [`parse_equality_expression`].
fn parse_boolean_and_expression(&mut self) -> Result<Expression> {
self.parse_bin_expr(&[Token::And], Self::parse_equality_expression)
}
/// Returns an [`Expression`] AST node if the next tokens represent
/// a binary OR expression.
///
@ -117,6 +109,14 @@ impl ParserContext<'_> {
self.parse_bin_expr(&[Token::Or], Self::parse_boolean_and_expression)
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// binary AND expression.
///
/// Otherwise, tries to parse the next token using [`parse_equality_expression`].
fn parse_boolean_and_expression(&mut self) -> Result<Expression> {
self.parse_bin_expr(&[Token::And], Self::parse_equality_expression)
}
/// Eats one of binary operators matching any in `tokens`.
fn eat_bin_op(&mut self, tokens: &[Token]) -> Option<BinaryOperation> {
self.eat_any(tokens).then(|| match &self.prev_token.token {
@ -467,13 +467,7 @@ impl ParserContext<'_> {
None
};
let is_record = identifier.to_string().eq("Record");
Ok(CircuitVariableInitializer {
identifier,
expression,
is_record,
})
Ok(CircuitVariableInitializer { identifier, expression })
}
/// Returns an [`Expression`] AST node if the next tokens represent a
@ -530,8 +524,19 @@ impl ParserContext<'_> {
// Literal followed by other type suffix, e.g., `42u8`.
Some(suffix) => {
assert_no_whitespace(&suffix.to_string())?;
let int_ty = Self::token_to_int_type(suffix).expect("unknown int type token");
Expression::Literal(Literal::Integer(int_ty, value, full_span))
match suffix {
Token::I8 => Expression::Literal(Literal::I8(value, full_span)),
Token::I16 => Expression::Literal(Literal::I16(value, full_span)),
Token::I32 => Expression::Literal(Literal::I32(value, full_span)),
Token::I64 => Expression::Literal(Literal::I64(value, full_span)),
Token::I128 => Expression::Literal(Literal::I128(value, full_span)),
Token::U8 => Expression::Literal(Literal::U8(value, full_span)),
Token::U16 => Expression::Literal(Literal::U16(value, full_span)),
Token::U32 => Expression::Literal(Literal::U32(value, full_span)),
Token::U64 => Expression::Literal(Literal::U64(value, full_span)),
Token::U128 => Expression::Literal(Literal::U128(value, full_span)),
_ => return Err(ParserError::unexpected_token("Expected integer type suffix", span).into()),
}
}
None => return Err(ParserError::implicit_values_not_allowed(value, span).into()),
}

View File

@ -15,18 +15,27 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use leo_errors::{ParserError, ParserWarning, Result};
use crate::parse_ast;
use leo_errors::{CompilerError, ParserError, ParserWarning, Result};
use leo_span::source_map::FileName;
use leo_span::sym;
use leo_span::symbol::with_session_globals;
use std::fs;
impl ParserContext<'_> {
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
pub fn parse_program(&mut self) -> Result<Program> {
let mut imports = IndexMap::new();
let mut functions = IndexMap::new();
let mut circuits = IndexMap::new();
while self.has_next() {
match &self.token.token {
Token::Import => {
let (id, import) = self.parse_import()?;
imports.insert(id, import);
}
Token::Circuit | Token::Record => {
let (id, circuit) = self.parse_circuit()?;
circuits.insert(id, circuit);
@ -47,6 +56,7 @@ impl ParserContext<'_> {
name: String::new(),
network: String::new(),
expected_input: Vec::new(),
imports,
functions,
circuits,
})
@ -64,6 +74,53 @@ impl ParserContext<'_> {
)
}
/// Parses an import statement `import foo.leo;`.
pub(super) fn parse_import(&mut self) -> Result<(Identifier, Program)> {
// Parse `import`.
let _start = self.expect(&Token::Import)?;
// Parse `foo`.
let import_name = self.expect_identifier()?;
// Parse `.leo`.
self.expect(&Token::Dot)?;
let leo_file_extension = self.expect_identifier()?;
// Throw error for non-leo files.
if leo_file_extension.name.ne(&sym::leo) {
return Err(ParserError::leo_imports_only(leo_file_extension, self.token.span).into());
}
let _end = self.expect(&Token::Semicolon)?;
// Tokenize and parse import file.
// Todo: move this to a different module.
let mut import_file_path =
std::env::current_dir().map_err(|err| CompilerError::cannot_open_cwd(err, self.token.span))?;
import_file_path.push("imports");
import_file_path.push(format!("{}.leo", import_name.name));
// Throw an error if the import file doesn't exist.
if !import_file_path.exists() {
return Err(CompilerError::import_not_found(import_file_path.display(), self.token.span).into());
}
// Read the import file into string.
// Todo: protect against cyclic imports.
let program_string =
fs::read_to_string(&import_file_path).map_err(|e| CompilerError::file_read_error(&import_file_path, e))?;
// Create import file name.
let name: FileName = FileName::Real(import_file_path);
// Register the source (`program_string`) in the source map.
let prg_sf = with_session_globals(|s| s.source_map.new_source(&program_string, name));
// Use the parser to construct the imported abstract syntax tree (ast).
let program_ast = parse_ast(self.handler, &prg_sf.src, prg_sf.start_pos)?;
Ok((import_name, program_ast.into_repr()))
}
/// Returns a [`Vec<CircuitMember>`] AST node if the next tokens represent a circuit member variable
/// or circuit member function or circuit member constant.
fn parse_circuit_members(&mut self) -> Result<(Vec<CircuitMember>, Span)> {

View File

@ -120,7 +120,9 @@ impl ParserContext<'_> {
variable: ident,
type_: type_.0,
start,
start_value: Default::default(),
stop,
stop_value: Default::default(),
inclusive: false,
block,
})
@ -191,8 +193,8 @@ impl ParserContext<'_> {
self.expect_any(&[Token::Let, Token::Const])?;
let decl_span = self.prev_token.span;
let decl_type = match &self.prev_token.token {
Token::Let => Declare::Let,
Token::Const => Declare::Const,
Token::Let => DeclarationType::Let,
Token::Const => DeclarationType::Const,
_ => unreachable!("parse_definition_statement_ shouldn't produce this"),
};

View File

@ -15,7 +15,8 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use leo_errors::Result;
use leo_errors::{ParserError, Result};
pub(super) const TYPE_TOKENS: &[Token] = &[
Token::Address,
@ -37,39 +38,29 @@ pub(super) const TYPE_TOKENS: &[Token] = &[
];
impl ParserContext<'_> {
/// Returns a [`IntegerType`] AST node if the given token is a supported integer type, or [`None`].
pub(super) fn token_to_int_type(token: &Token) -> Option<IntegerType> {
Some(match token {
Token::I8 => IntegerType::I8,
Token::I16 => IntegerType::I16,
Token::I32 => IntegerType::I32,
Token::I64 => IntegerType::I64,
Token::I128 => IntegerType::I128,
Token::U8 => IntegerType::U8,
Token::U16 => IntegerType::U16,
Token::U32 => IntegerType::U32,
Token::U64 => IntegerType::U64,
Token::U128 => IntegerType::U128,
_ => return None,
})
}
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a primitive type.
/// Also returns the span of the parsed token.
pub fn parse_non_ident_types(&mut self) -> Result<(Type, Span)> {
pub fn parse_primitive_type(&mut self) -> Result<(Type, Span)> {
let span = self.expect_any(TYPE_TOKENS)?;
Ok((
match &self.prev_token.token {
Token::Address => Type::Address,
Token::Bool => Type::Boolean,
Token::Field => Type::Field,
Token::Group => Type::Group,
Token::Scalar => Type::Scalar,
Token::String => Type::String,
x => Type::IntegerType(Self::token_to_int_type(x).expect("invalid int type")),
},
span,
))
match &self.prev_token.token {
Token::Address => Ok((Type::Address, span)),
Token::Bool => Ok((Type::Boolean, span)),
Token::Field => Ok((Type::Field, span)),
Token::Group => Ok((Type::Group, span)),
Token::I8 => Ok((Type::I8, span)),
Token::I16 => Ok((Type::I16, span)),
Token::I32 => Ok((Type::I32, span)),
Token::I64 => Ok((Type::I64, span)),
Token::I128 => Ok((Type::I128, span)),
Token::Scalar => Ok((Type::Scalar, span)),
Token::String => Ok((Type::String, span)),
Token::U8 => Ok((Type::U8, span)),
Token::U16 => Ok((Type::U16, span)),
Token::U32 => Ok((Type::U32, span)),
Token::U64 => Ok((Type::U64, span)),
Token::U128 => Ok((Type::U128, span)),
_ => Err(ParserError::unexpected_token("Expected a primitive type.", span).into()),
}
}
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
@ -80,7 +71,7 @@ impl ParserContext<'_> {
} else if self.peek_is_left_par() {
self.parse_tuple_type()
} else {
self.parse_non_ident_types()
self.parse_primitive_type()
}
}

View File

@ -314,6 +314,7 @@ impl Token {
"i128" => Token::I128,
"if" => Token::If,
"in" => Token::In,
"import" => Token::Import,
"let" => Token::Let,
"public" => Token::Public,
"record" => Token::Record,

View File

@ -94,17 +94,18 @@ pub enum Token {
// Regular Keywords
Circuit,
Console,
/// Const variable and a const function.
// Const variable and a const function.
Const,
/// Constant parameter
// Constant parameter
Constant,
Else,
For,
Function,
If,
Import,
In,
Let,
/// For public inputs.
// For public inputs.
Public,
Return,
SelfLower,
@ -137,6 +138,7 @@ pub const KEYWORD_TOKENS: &[Token] = &[
Token::I64,
Token::I128,
Token::If,
Token::Import,
Token::In,
Token::Let,
Token::Public,
@ -182,6 +184,7 @@ impl Token {
Token::I128 => sym::i128,
Token::If => sym::If,
Token::In => sym::In,
Token::Import => sym::import,
Token::Let => sym::Let,
Token::Public => sym::Public,
Token::Record => sym::record,
@ -277,6 +280,7 @@ impl fmt::Display for Token {
For => write!(f, "for"),
Function => write!(f, "function"),
If => write!(f, "if"),
Import => write!(f, "import"),
In => write!(f, "in"),
Let => write!(f, "let"),
SelfLower => write!(f, "self"),

View File

@ -46,3 +46,6 @@ version = "1.5.3"
[dependencies.itertools]
version = "0.10.3"
[dependencies.num-traits]
version = "0.2.15"

View File

@ -154,9 +154,23 @@ impl<'a> CodeGenerator<'a> {
}
fn visit_circuit_init(&mut self, input: &'a CircuitExpression) -> (String, String) {
// Lookup circuit or record.
let name = if let Some(type_) = self.composite_mapping.get(&input.name.name) {
let name = input.name.to_string().to_lowercase();
if name.eq("record") {
// record.private;
format!("{}.{}", name, type_)
} else {
// foo; // no visibility for interfaces
name
}
} else {
unreachable!("All composite types should be known at this phase of compilation")
};
// Initialize instruction builder strings.
let mut instructions = String::new();
let mut circuit_init_instruction = format!(" {} ", input.name.to_string().to_lowercase());
let mut circuit_init_instruction = String::from(" cast ");
// Visit each circuit member and accumulate instructions from expressions.
for member in input.members.iter() {
@ -180,7 +194,14 @@ impl<'a> CodeGenerator<'a> {
// Push destination register to circuit init instruction.
let destination_register = format!("r{}", self.next_register);
writeln!(circuit_init_instruction, "into {};", destination_register).expect("failed to write to string");
writeln!(
circuit_init_instruction,
"into {dest} as {name};",
dest = destination_register,
name = name,
)
.expect("failed to write to string");
instructions.push_str(&circuit_init_instruction);
// Increment the register counter.

View File

@ -16,7 +16,7 @@
use crate::CodeGenerator;
use leo_ast::{Circuit, CircuitMember, Function, Program};
use leo_ast::{Circuit, CircuitMember, Function, Identifier, Program};
use indexmap::IndexMap;
use itertools::Itertools;
@ -24,9 +24,31 @@ use std::fmt::Write as _;
impl<'a> CodeGenerator<'a> {
pub(crate) fn visit_program(&mut self, input: &'a Program) -> String {
let mut program_string = format!("program {}.{};\n", input.name, input.network);
// Accumulate instructions into a program string.
let mut program_string = String::new();
// Visit each `Circuit` or `Record` in the Leo AST and produce a bytecode circuit.
if !input.imports.is_empty() {
// Visit each import statement and produce a Aleo import instruction.
program_string.push_str(
&input
.imports
.iter()
.map(|(identifier, imported_program)| self.visit_import(identifier, imported_program))
.join("\n"),
);
// Newline separator.
program_string.push('\n');
}
// Print the program id.
writeln!(program_string, "program {}.{};", input.name, input.network)
.expect("Failed to write program id to string.");
// Newline separator.
program_string.push('\n');
// Visit each `Circuit` or `Record` in the Leo AST and produce a Aleo interface instruction.
program_string.push_str(
&input
.circuits
@ -35,9 +57,10 @@ impl<'a> CodeGenerator<'a> {
.join("\n"),
);
// Newline separator.
program_string.push('\n');
// Visit each `Function` in the Leo AST and produce a bytecode function.
// Visit each `Function` in the Leo AST and produce a Aleo function instruction.
program_string.push_str(
&input
.functions
@ -49,6 +72,15 @@ impl<'a> CodeGenerator<'a> {
program_string
}
fn visit_import(&mut self, import_name: &'a Identifier, import_program: &'a Program) -> String {
// Load symbols into composite mapping.
let _import_program_string = self.visit_program(import_program);
// todo: We do not need the import program string because we generate instructions for imports separately during leo build.
// Generate string for import statement.
format!("import {}.aleo;", import_name)
}
fn visit_circuit_or_record(&mut self, circuit: &'a Circuit) -> String {
if circuit.is_record {
self.visit_record(circuit)

View File

@ -28,7 +28,16 @@ impl<'a> CodeGenerator<'a> {
| Type::Group
| Type::Scalar
| Type::String
| Type::IntegerType(..) => format!("{}", input),
| Type::I8
| Type::I16
| Type::I32
| Type::I64
| Type::I128
| Type::U8
| Type::U16
| Type::U32
| Type::U64
| Type::U128 => format!("{}", input),
Type::Identifier(ident) => {
if let Some(type_) = self.composite_mapping.get(&ident.name) {
format!("{}.{}", ident.to_string().to_lowercase(), type_)

View File

@ -22,8 +22,11 @@ pub use code_generation::*;
pub mod pass;
pub use self::pass::*;
pub mod loop_unrolling;
pub use self::loop_unrolling::*;
pub mod symbol_table;
pub use symbol_table::*;
pub mod type_checker;
pub use type_checker::*;
pub mod type_checking;
pub use type_checking::*;

View File

@ -0,0 +1,49 @@
// Copyright (C) 2019-2022 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 range_iterator;
pub(crate) use range_iterator::*;
pub mod unroller;
pub use unroller::*;
pub mod unroll_expression;
pub use unroll_expression::*;
pub mod unroll_program;
pub use unroll_program::*;
pub mod unroll_statement;
pub use unroll_statement::*;
use crate::{Pass, SymbolTable};
use leo_ast::{Ast, ProgramReconstructor};
use leo_errors::{emitter::Handler, Result};
impl<'a> Pass for Unroller<'a> {
type Input = (Ast, &'a Handler, SymbolTable);
type Output = Result<(Ast, SymbolTable)>;
fn do_pass((ast, handler, st): Self::Input) -> Self::Output {
// Reconstructs the AST based off any flattening work that is done.
let mut reconstructor = Self::new(st, handler);
let program = reconstructor.reconstruct_program(ast.into_repr());
handler.last_err()?;
Ok((Ast::new(program), reconstructor.symbol_table.take()))
}
}

View File

@ -0,0 +1,77 @@
// Copyright (C) 2019-2022 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 num_traits::One;
use std::fmt::Display;
use std::ops::Add;
use leo_ast::Value;
use leo_errors::LeoError;
// TODO: Consider the sealing this trait.
// TODO: Better name.
/// A trait for whose implementors are concrete values for loop bounds.
pub(crate) trait LoopBound:
Add<Output = Self> + Copy + Display + One + PartialOrd + TryFrom<Value, Error = LeoError>
{
}
impl LoopBound for i128 {}
impl LoopBound for u128 {}
/// Whether or not a bound is inclusive or exclusive.
pub(crate) enum Clusivity {
Inclusive,
Exclusive,
}
/// An iterator over a range of values.
pub(crate) struct RangeIterator<I: LoopBound> {
end: I,
current: Option<I>,
clusivity: Clusivity,
}
impl<I: LoopBound> RangeIterator<I> {
pub(crate) fn new(start: I, end: I, clusivity: Clusivity) -> Self {
Self {
end,
current: Some(start),
clusivity,
}
}
}
impl<I: LoopBound> Iterator for RangeIterator<I> {
type Item = I;
fn next(&mut self) -> Option<Self::Item> {
match self.current {
None => None,
Some(value) if value < self.end => {
self.current = Some(value.add(I::one()));
Some(value)
}
Some(value) => {
self.current = None;
match self.clusivity {
Clusivity::Exclusive => None,
Clusivity::Inclusive => Some(value),
}
}
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2019-2022 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 leo_ast::*;
use crate::Unroller;
impl ExpressionReconstructor for Unroller<'_> {
type AdditionalOutput = ();
}

View File

@ -0,0 +1,52 @@
// Copyright (C) 2019-2022 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 std::cell::RefCell;
use leo_ast::*;
use crate::Unroller;
impl ProgramReconstructor for Unroller<'_> {
fn reconstruct_function(&mut self, function: Function) -> Function {
let function_name = function.name();
// Grab our function scope.
let prev_st = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(prev_st.borrow().lookup_fn_scope(function_name).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(prev_st.into_inner()));
// Set our current block scope index to 0
self.block_index = 0;
// Reconstruct the function block.
let reconstructed_function = Function {
identifier: function.identifier,
input: function.input,
output: function.output,
core_mapping: function.core_mapping,
block: self.reconstruct_block(function.block),
span: function.span,
};
// Pop back to parent scope.
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table.swap(prev_st.lookup_fn_scope(function_name).unwrap());
self.symbol_table = RefCell::new(prev_st);
reconstructed_function
}
}

View File

@ -0,0 +1,93 @@
// Copyright (C) 2019-2022 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 leo_ast::*;
use crate::unroller::Unroller;
use crate::{VariableSymbol, VariableType};
impl StatementReconstructor for Unroller<'_> {
fn reconstruct_definition(&mut self, input: DefinitionStatement) -> Statement {
// If we are unrolling a loop, then we need to repopulate the symbol table.
if self.is_unrolling {
let declaration = if input.declaration_type == DeclarationType::Const {
VariableType::Const
} else {
VariableType::Mut
};
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
input.variable_name.name,
VariableSymbol {
type_: input.type_.clone(),
span: input.span(),
declaration: declaration.clone(),
},
) {
self.handler.emit_err(err);
}
}
Statement::Definition(input)
}
fn reconstruct_iteration(&mut self, input: IterationStatement) -> Statement {
// We match on start and stop cause loops require
// bounds to be constants.
match (
input.start_value.clone().into_inner(),
input.stop_value.clone().into_inner(),
) {
(Some(start), Some(stop)) => match (Type::from(&start), Type::from(&stop)) {
(Type::I8, Type::I8)
| (Type::I16, Type::I16)
| (Type::I32, Type::I32)
| (Type::I64, Type::I64)
| (Type::I128, Type::I128) => self.unroll_iteration_statement::<i128>(input, start, stop),
(Type::U8, Type::U8)
| (Type::U16, Type::U16)
| (Type::U32, Type::U32)
| (Type::U64, Type::U64)
| (Type::U128, Type::U128) => self.unroll_iteration_statement::<u128>(input, start, stop),
_ => unreachable!("Type checking ensures that `start` and `stop` have the same type."),
},
// If both loop bounds are not constant, then the loop is not unrolled.
_ => Statement::Iteration(Box::from(input)),
}
}
fn reconstruct_block(&mut self, input: Block) -> Block {
let scope_index = self.current_scope_index();
// Enter the block scope.
self.enter_block_scope(scope_index);
self.block_index = 0;
let block = Block {
statements: input
.statements
.into_iter()
.map(|s| self.reconstruct_statement(s))
.collect(),
span: input.span,
};
// Exit the block scope.
self.exit_block_scope(scope_index);
self.block_index = scope_index + 1;
block
}
}

View File

@ -0,0 +1,193 @@
// Copyright (C) 2019-2022 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 leo_ast::{
Block, DeclarationType, DefinitionStatement, Expression, IterationStatement, Literal, Statement,
StatementReconstructor, Type, Value,
};
use std::cell::RefCell;
use leo_errors::emitter::Handler;
use crate::{Clusivity, LoopBound, RangeIterator, SymbolTable};
pub struct Unroller<'a> {
/// The symbol table for the function being processed.
pub(crate) symbol_table: RefCell<SymbolTable>,
/// The index of the current block scope.
pub(crate) block_index: usize,
/// An error handler used for any errors found during unrolling.
pub(crate) handler: &'a Handler,
/// Are we in the midst of unrolling a loop?
pub(crate) is_unrolling: bool,
}
impl<'a> Unroller<'a> {
pub(crate) fn new(symbol_table: SymbolTable, handler: &'a Handler) -> Self {
Self {
symbol_table: RefCell::new(symbol_table),
block_index: 0,
handler,
is_unrolling: false,
}
}
/// Returns the index of the current scope.
/// Note that if we are in the midst of unrolling an IterationStatement, a new scope is created.
pub(crate) fn current_scope_index(&mut self) -> usize {
if self.is_unrolling {
self.symbol_table.borrow_mut().insert_block()
} else {
self.block_index
}
}
/// Enters a child block scope.
pub(crate) fn enter_block_scope(&mut self, index: usize) {
let previous_symbol_table = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(previous_symbol_table.borrow().lookup_scope_by_index(index).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(previous_symbol_table.into_inner()));
}
/// Exits the current block scope.
pub(crate) fn exit_block_scope(&mut self, index: usize) {
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table.swap(prev_st.lookup_scope_by_index(index).unwrap());
self.symbol_table = RefCell::new(prev_st);
}
/// Unrolls an IterationStatement.
pub(crate) fn unroll_iteration_statement<I: LoopBound>(
&mut self,
input: IterationStatement,
start: Value,
stop: Value,
) -> Statement {
// Closure to check that the constant values are valid u128.
// We already know these are integers since loop unrolling occurs after type checking.
let cast_to_number = |v: Value| -> Result<I, Statement> {
match v.try_into() {
Ok(val_as_u128) => Ok(val_as_u128),
Err(err) => {
self.handler.emit_err(err);
Err(Statement::dummy(input.span))
}
}
};
// Cast `start` to `I`.
let start = match cast_to_number(start) {
Ok(v) => v,
Err(s) => return s,
};
// Cast `stop` to `I`.
let stop = match cast_to_number(stop) {
Ok(v) => v,
Err(s) => return s,
};
// Get the index of the current scope.
let scope_index = self.current_scope_index();
// Enter the scope of the loop body.
self.enter_block_scope(scope_index);
self.block_index = 0;
// Clear the symbol table for the loop body.
// This is necessary because loop unrolling transforms the program, which requires reconstructing the symbol table.
self.symbol_table.borrow_mut().variables.clear();
self.symbol_table.borrow_mut().scopes.clear();
self.symbol_table.borrow_mut().scope_index = 0;
// Create a block statement to replace the iteration statement.
// Creates a new block per iteration inside the outer block statement.
let iter_blocks = Statement::Block(Block {
span: input.span,
statements: match input.inclusive {
true => {
let iter = RangeIterator::new(start, stop, Clusivity::Inclusive);
iter.map(|iteration_count| self.unroll_single_iteration(&input, iteration_count))
.collect()
}
false => {
let iter = RangeIterator::new(start, stop, Clusivity::Exclusive);
iter.map(|iteration_count| self.unroll_single_iteration(&input, iteration_count))
.collect()
}
},
});
// Exit the scope of the loop body.
self.exit_block_scope(scope_index);
self.block_index = scope_index + 1;
iter_blocks
}
/// A helper function to unroll a single iteration an IterationStatement.
fn unroll_single_iteration<I: LoopBound>(&mut self, input: &IterationStatement, iteration_count: I) -> Statement {
// Create a scope for a single unrolling of the `IterationStatement`.
let scope_index = self.symbol_table.borrow_mut().insert_block();
self.enter_block_scope(scope_index);
let prior_is_unrolling = self.is_unrolling;
self.is_unrolling = true;
// Reconstruct `iteration_count` as a `Literal`.
let value = match input.type_ {
Type::I8 => Literal::I8(iteration_count.to_string(), Default::default()),
Type::I16 => Literal::I16(iteration_count.to_string(), Default::default()),
Type::I32 => Literal::I32(iteration_count.to_string(), Default::default()),
Type::I64 => Literal::I64(iteration_count.to_string(), Default::default()),
Type::I128 => Literal::I128(iteration_count.to_string(), Default::default()),
Type::U8 => Literal::U8(iteration_count.to_string(), Default::default()),
Type::U16 => Literal::U16(iteration_count.to_string(), Default::default()),
Type::U32 => Literal::U32(iteration_count.to_string(), Default::default()),
Type::U64 => Literal::U64(iteration_count.to_string(), Default::default()),
Type::U128 => Literal::U128(iteration_count.to_string(), Default::default()),
_ => unreachable!(
"The iteration variable must be an integer type. This should be enforced by type checking."
),
};
// The first statement in the block is the assignment of the loop variable to the current iteration count.
let mut statements = vec![self.reconstruct_definition(DefinitionStatement {
declaration_type: DeclarationType::Const,
type_: input.type_.clone(),
value: Expression::Literal(value),
span: Default::default(),
variable_name: input.variable,
})];
// Reconstruct the statements in the loop body.
input.block.statements.clone().into_iter().for_each(|s| {
statements.push(self.reconstruct_statement(s));
});
let block = Statement::Block(Block {
statements,
span: input.block.span,
});
self.is_unrolling = prior_is_unrolling;
// Exit the scope.
self.exit_block_scope(scope_index);
block
}
}

View File

@ -57,4 +57,8 @@ impl<'a> ProgramVisitor<'a> for CreateSymbolTable<'a> {
self.handler.emit_err(err);
}
}
fn visit_import(&mut self, input: &'a Program) {
self.visit_program(input)
}
}

View File

@ -102,19 +102,19 @@ impl SymbolTable {
}
/// Attempts to lookup a function in the symbol table.
pub fn lookup_fn(&self, symbol: &Symbol) -> Option<&FunctionSymbol> {
if let Some(func) = self.functions.get(symbol) {
pub fn lookup_fn_symbol(&self, symbol: Symbol) -> Option<&FunctionSymbol> {
if let Some(func) = self.functions.get(&symbol) {
Some(func)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_fn(symbol)
parent.lookup_fn_symbol(symbol)
} else {
None
}
}
/// Attempts to lookup a circuit in the symbol table.
pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&Circuit> {
if let Some(circ) = self.circuits.get(symbol) {
pub fn lookup_circuit(&self, symbol: Symbol) -> Option<&Circuit> {
if let Some(circ) = self.circuits.get(&symbol) {
Some(circ)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_circuit(symbol)
@ -124,8 +124,8 @@ impl SymbolTable {
}
/// Attempts to lookup a variable in the symbol table.
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol> {
if let Some(var) = self.variables.get(symbol) {
pub fn lookup_variable(&self, symbol: Symbol) -> Option<&VariableSymbol> {
if let Some(var) = self.variables.get(&symbol) {
Some(var)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_variable(symbol)
@ -135,14 +135,14 @@ impl SymbolTable {
}
/// Returns true if the variable exists in the local scope
pub fn variable_in_local_scope(&self, symbol: &Symbol) -> bool {
self.variables.contains_key(symbol)
pub fn variable_in_local_scope(&self, symbol: Symbol) -> bool {
self.variables.contains_key(&symbol)
}
/// Returns true if the variable exists in any parent scope
pub fn variable_in_parent_scope(&self, symbol: &Symbol) -> bool {
pub fn variable_in_parent_scope(&self, symbol: Symbol) -> bool {
if let Some(parent) = self.parent.as_ref() {
if parent.variables.contains_key(symbol) {
if parent.variables.contains_key(&symbol) {
true
} else {
parent.variable_in_parent_scope(symbol)
@ -153,8 +153,8 @@ impl SymbolTable {
}
/// Returns a mutable reference to the `VariableSymbol` if it exists in the symbol table.
pub fn lookup_variable_mut(&mut self, symbol: &Symbol) -> Option<&mut VariableSymbol> {
if let Some(var) = self.variables.get_mut(symbol) {
pub fn lookup_variable_mut(&mut self, symbol: Symbol) -> Option<&mut VariableSymbol> {
if let Some(var) = self.variables.get_mut(&symbol) {
Some(var)
} else if let Some(parent) = self.parent.as_mut() {
parent.lookup_variable_mut(symbol)
@ -164,18 +164,12 @@ impl SymbolTable {
}
/// Returns the scope associated with the function symbol, if it exists in the symbol table.
pub fn get_fn_scope(&self, symbol: &Symbol) -> Option<&RefCell<Self>> {
if let Some(func) = self.functions.get(symbol) {
self.scopes.get(func.id)
} else if let Some(parent) = self.parent.as_ref() {
parent.get_fn_scope(symbol)
} else {
None
}
pub fn lookup_fn_scope(&self, symbol: Symbol) -> Option<&RefCell<Self>> {
self.lookup_fn_symbol(symbol).and_then(|func| self.scopes.get(func.id))
}
/// Returns the scope associated with `index`, if it exists in the symbol table.
pub fn get_block_scope(&self, index: usize) -> Option<&RefCell<Self>> {
pub fn lookup_scope_by_index(&self, index: usize) -> Option<&RefCell<Self>> {
self.scopes.get(index)
}
}

View File

@ -19,17 +19,17 @@ use std::fmt::Display;
use leo_ast::{ParamMode, Type};
use leo_span::Span;
/// An enumeration of the different types of declarations.
/// An enumeration of the different types of variable type.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Declaration {
pub enum VariableType {
Const,
Input(ParamMode),
Mut,
}
impl Display for Declaration {
impl Display for VariableType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Declaration::*;
use VariableType::*;
match self {
Const => write!(f, "const var"),
@ -47,7 +47,7 @@ pub struct VariableSymbol {
/// The `Span` associated with the variable.
pub span: Span,
/// The type of declaration for the variable.
pub declaration: Declaration,
pub declaration: VariableType,
}
impl Display for VariableSymbol {

View File

@ -126,7 +126,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
match self.visit_expression(&access.inner, &None) {
Some(Type::Identifier(identifier)) => {
// Retrieve the circuit definition associated with `identifier`.
let circ = self.symbol_table.borrow().lookup_circuit(&identifier.name).cloned();
let circ = self.symbol_table.borrow().lookup_circuit(identifier.name).cloned();
if let Some(circ) = circ {
// Check that `access.name` is a member of the circuit.
match circ
@ -166,7 +166,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
fn visit_circuit_init(&mut self, input: &'a CircuitExpression, additional: &Self::AdditionalInput) -> Self::Output {
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
let circ = self.symbol_table.borrow().lookup_circuit(input.name.name).cloned();
if let Some(circ) = circ {
// Check circuit type name.
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
@ -210,9 +210,9 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(&var.name) {
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(var.name) {
Some(self.assert_and_return_type(Type::Identifier(circuit.identifier), expected, var.span))
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(&var.name) {
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(var.name) {
Some(self.assert_and_return_type(var.type_.clone(), expected, var.span))
} else {
self.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
@ -221,85 +221,98 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output {
// Closure to produce a negated integer as a string.
let negate_int = |str_content: &String| {
if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
}
};
Some(match input {
Literal::Address(_, _) => self.assert_and_return_type(Type::Address, expected, input.span()),
Literal::Boolean(_, _) => self.assert_and_return_type(Type::Boolean, expected, input.span()),
Literal::Field(_, _) => self.assert_and_return_type(Type::Field, expected, input.span()),
Literal::Integer(type_, str_content, _) => {
match type_ {
IntegerType::I8 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
Literal::I8(str_content, _) => {
let int = negate_int(str_content);
if int.parse::<i8>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
}
}
IntegerType::I16 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i16>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
}
}
IntegerType::I32 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i32>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
}
}
IntegerType::I64 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i64>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
}
}
IntegerType::I128 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i128>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
}
}
IntegerType::U8 if str_content.parse::<u8>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()))
}
IntegerType::U16 if str_content.parse::<u16>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()))
}
IntegerType::U32 if str_content.parse::<u32>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()))
}
IntegerType::U64 if str_content.parse::<u64>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()))
}
IntegerType::U128 if str_content.parse::<u128>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()))
}
_ => {}
if int.parse::<i8>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
}
self.assert_and_return_type(Type::IntegerType(*type_), expected, input.span())
self.assert_and_return_type(Type::I8, expected, input.span())
}
Literal::I16(str_content, _) => {
let int = negate_int(str_content);
if int.parse::<i16>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
}
self.assert_and_return_type(Type::I16, expected, input.span())
}
Literal::I32(str_content, _) => {
let int = negate_int(str_content);
if int.parse::<i32>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
}
self.assert_and_return_type(Type::I32, expected, input.span())
}
Literal::I64(str_content, _) => {
let int = negate_int(str_content);
if int.parse::<i64>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
}
self.assert_and_return_type(Type::I64, expected, input.span())
}
Literal::I128(str_content, _) => {
let int = negate_int(str_content);
if int.parse::<i128>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
}
self.assert_and_return_type(Type::I128, expected, input.span())
}
Literal::U8(str_content, _) => {
if str_content.parse::<u8>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()));
}
self.assert_and_return_type(Type::U8, expected, input.span())
}
Literal::U16(str_content, _) => {
if str_content.parse::<u16>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()));
}
self.assert_and_return_type(Type::U16, expected, input.span())
}
Literal::U32(str_content, _) => {
if str_content.parse::<u32>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()));
}
self.assert_and_return_type(Type::U32, expected, input.span())
}
Literal::U64(str_content, _) => {
if str_content.parse::<u64>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()));
}
self.assert_and_return_type(Type::U64, expected, input.span())
}
Literal::U128(str_content, _) => {
if str_content.parse::<u128>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()));
}
self.assert_and_return_type(Type::U128, expected, input.span())
}
Literal::Group(_) => self.assert_and_return_type(Type::Group, expected, input.span()),
Literal::Scalar(_, _) => self.assert_and_return_type(Type::Scalar, expected, input.span()),
@ -435,19 +448,8 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
let t1 = self.visit_expression(&input.left, &None);
let t2 = self.visit_expression(&input.right, &None);
match (t1, t2) {
(Some(Type::IntegerType(_)), t2) => {
// Check rhs is integer and give detailed error message.
self.assert_int_type(&t2, input.left.span());
}
(t1, Some(Type::IntegerType(_))) => {
// Check lhs is integer and give detailed error message.
self.assert_int_type(&t1, input.right.span());
}
(t1, t2) => {
self.check_eq_types(&t1, &t2, input.span());
}
}
// Check that the types of the operands are equal.
self.check_eq_types(&t1, &t2, input.span());
// Operation returns a boolean.
self.assert_bool_type(destination, input.span());
@ -459,40 +461,20 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
let t1 = self.visit_expression(&input.left, &None);
let t2 = self.visit_expression(&input.right, &None);
match (t1, t2) {
match (&t1, &t2) {
(Some(Type::Address), _) | (_, Some(Type::Address)) => {
// Emit an error for address comparison.
self.emit_err(TypeCheckerError::compare_address(input.op, input.span()));
}
(Some(Type::Field), t2) => {
// Assert rhs is field.
self.assert_field_type(&t2, input.right.span());
}
(t1, Some(Type::Field)) => {
// Assert lhs is field.
self.assert_field_type(&t1, input.left.span());
}
(Some(Type::Scalar), t2) => {
// Assert rhs is scalar.
self.assert_scalar_type(&t2, input.right.span());
}
(t1, Some(Type::Scalar)) => {
// Assert lhs is scalar.
self.assert_scalar_type(&t1, input.left.span());
}
(Some(Type::IntegerType(_)), t2) => {
// Assert rhs is integer.
self.assert_int_type(&t2, input.right.span());
}
(t1, Some(Type::IntegerType(_))) => {
// Assert lhs is integer.
self.assert_int_type(&t1, input.left.span());
}
(_, _) => {
// Not enough info to assert type.
(t1, t2) => {
self.assert_field_scalar_int_type(t1, input.left.span());
self.assert_field_scalar_int_type(t2, input.right.span());
}
}
// Check that the types of the operands are equal.
self.check_eq_types(&t1, &t2, input.span());
// Operation returns a boolean.
self.assert_bool_type(destination, input.span());
@ -534,7 +516,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(ident) => {
// Note: The function symbol lookup is performed outside of the `if let Some(func) ...` block to avoid a RefCell lifetime bug in Rust.
// Do not move it into the `if let Some(func) ...` block or it will keep `self.symbol_table` alive for the entire block and will be very memory inefficient!
let func = self.symbol_table.borrow().lookup_fn(&ident.name).cloned();
let func = self.symbol_table.borrow().lookup_fn_symbol(ident.name).cloned();
if let Some(func) = func {
let ret = self.assert_and_return_type(func.output, expected, func.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::{Declaration, TypeChecker, VariableSymbol};
use crate::{TypeChecker, VariableSymbol, VariableType};
use leo_ast::*;
use leo_errors::TypeCheckerError;
@ -28,7 +28,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_function(&mut self, input: &'a Function) {
let prev_st = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(prev_st.borrow().get_fn_scope(&input.name()).unwrap());
.swap(prev_st.borrow().lookup_fn_scope(input.name()).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(prev_st.into_inner()));
self.has_return = false;
@ -44,7 +44,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
VariableSymbol {
type_: input_var.type_.clone(),
span: input_var.identifier.span(),
declaration: Declaration::Input(input_var.mode()),
declaration: VariableType::Input(input_var.mode()),
},
) {
self.handler.emit_err(err);
@ -64,7 +64,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
}
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table.swap(prev_st.get_fn_scope(&input.name()).unwrap());
self.symbol_table.swap(prev_st.lookup_fn_scope(input.name()).unwrap());
self.symbol_table = RefCell::new(prev_st);
}
@ -79,7 +79,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
});
}
// For records, enforce presence of `owner: Address` and `balance: u64` members.
// For records, enforce presence of `owner: Address` and `gates: u64` members.
if input.is_record {
let check_has_field = |need, expected_ty: Type| match input
.members
@ -103,7 +103,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
}
};
check_has_field(sym::owner, Type::Address);
check_has_field(sym::balance, Type::IntegerType(IntegerType::U64));
check_has_field(sym::gates, Type::U64);
}
// Ensure there are no tuple typed members.

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::{Declaration, TypeChecker, VariableSymbol};
use crate::{TypeChecker, VariableSymbol, VariableType};
use leo_ast::*;
use leo_errors::TypeCheckerError;
@ -26,7 +26,11 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// we can safely unwrap all self.parent instances because
// statements should always have some parent block
let parent = self.parent.unwrap();
let return_type = &self.symbol_table.borrow().lookup_fn(&parent).map(|f| f.output.clone());
let return_type = &self
.symbol_table
.borrow()
.lookup_fn_symbol(parent)
.map(|f| f.output.clone());
self.check_core_type_conflict(return_type);
self.has_return = true;
@ -35,10 +39,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
}
fn visit_definition(&mut self, input: &'a DefinitionStatement) {
let declaration = if input.declaration_type == Declare::Const {
Declaration::Const
let declaration = if input.declaration_type == DeclarationType::Const {
VariableType::Const
} else {
Declaration::Mut
VariableType::Mut
};
self.check_core_type_conflict(&Some(input.type_.clone()));
@ -66,11 +70,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
}
};
let var_type = if let Some(var) = self.symbol_table.borrow_mut().lookup_variable(&var_name.name) {
// TODO: Check where this check is moved to in `improved-flattening`.
let var_type = if let Some(var) = self.symbol_table.borrow_mut().lookup_variable(var_name.name) {
match &var.declaration {
Declaration::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
Declaration::Input(ParamMode::Const) => {
VariableType::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
VariableType::Input(ParamMode::Const) => {
self.emit_err(TypeCheckerError::cannot_assign_to_const_input(var_name, var.span))
}
_ => {}
@ -102,19 +105,46 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
self.assert_int_type(iter_type, input.variable.span);
self.check_core_type_conflict(iter_type);
// Create a new scope for the loop body.
let scope_index = self.symbol_table.borrow_mut().insert_block();
let prev_st = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(prev_st.borrow().lookup_scope_by_index(scope_index).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(prev_st.into_inner()));
// Add the loop variable to the scope of the loop body.
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
input.variable.name,
VariableSymbol {
type_: input.type_.clone(),
span: input.span(),
declaration: Declaration::Const,
declaration: VariableType::Const,
},
) {
self.handler.emit_err(err);
}
input.block.statements.iter().for_each(|s| self.visit_statement(s));
// Restore the previous scope.
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table
.swap(prev_st.lookup_scope_by_index(scope_index).unwrap());
self.symbol_table = RefCell::new(prev_st);
self.visit_expression(&input.start, iter_type);
// If `input.start` is a literal, instantiate it as a value.
if let Expression::Literal(literal) = &input.start {
input.start_value.replace(Some(Value::from(literal)));
}
self.visit_expression(&input.stop, iter_type);
// If `input.stop` is a literal, instantiate it as a value.
if let Expression::Literal(literal) = &input.stop {
input.stop_value.replace(Some(Value::from(literal)));
}
}
fn visit_console(&mut self, input: &'a ConsoleStatement) {
@ -132,26 +162,19 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// Creates a new sub-scope since we are in a block.
let scope_index = self.symbol_table.borrow_mut().insert_block();
let previous_symbol_table = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(previous_symbol_table.borrow().get_block_scope(scope_index).unwrap());
self.symbol_table.swap(
previous_symbol_table
.borrow()
.lookup_scope_by_index(scope_index)
.unwrap(),
);
self.symbol_table.borrow_mut().parent = Some(Box::new(previous_symbol_table.into_inner()));
input.statements.iter().for_each(|stmt| {
match stmt {
Statement::Return(stmt) => self.visit_return(stmt),
Statement::Definition(stmt) => self.visit_definition(stmt),
Statement::Assign(stmt) => self.visit_assign(stmt),
Statement::Conditional(stmt) => self.visit_conditional(stmt),
Statement::Iteration(stmt) => self.visit_iteration(stmt),
Statement::Console(stmt) => self.visit_console(stmt),
Statement::Block(stmt) => self.visit_block(stmt),
};
});
input.statements.iter().for_each(|stmt| self.visit_statement(stmt));
let previous_symbol_table = *self.symbol_table.borrow_mut().parent.take().unwrap();
// TODO: Is this swap necessary?
self.symbol_table
.swap(previous_symbol_table.get_block_scope(scope_index).unwrap());
.swap(previous_symbol_table.lookup_scope_by_index(scope_index).unwrap());
self.symbol_table = RefCell::new(previous_symbol_table);
}
}

View File

@ -16,7 +16,7 @@
use crate::SymbolTable;
use leo_ast::{Identifier, IntegerType, Node, Type};
use leo_ast::{Identifier, Node, Type};
use leo_core::*;
use leo_errors::{emitter::Handler, TypeCheckerError};
use leo_span::{Span, Symbol};
@ -44,31 +44,21 @@ const GROUP_TYPE: Type = Type::Group;
const SCALAR_TYPE: Type = Type::Scalar;
const INT_TYPES: [Type; 10] = [
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::I128),
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
Type::IntegerType(IntegerType::U64),
Type::IntegerType(IntegerType::U128),
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::I128,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::U128,
];
const SIGNED_INT_TYPES: [Type; 5] = [
Type::IntegerType(IntegerType::I8),
Type::IntegerType(IntegerType::I16),
Type::IntegerType(IntegerType::I32),
Type::IntegerType(IntegerType::I64),
Type::IntegerType(IntegerType::I128),
];
const SIGNED_INT_TYPES: [Type; 5] = [Type::I8, Type::I16, Type::I32, Type::I64, Type::I128];
const MAGNITUDE_TYPES: [Type; 3] = [
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
];
const MAGNITUDE_TYPES: [Type; 3] = [Type::U8, Type::U16, Type::U32];
impl<'a> TypeChecker<'a> {
/// Returns a new type checker given a symbol table and error handler.
@ -261,6 +251,16 @@ impl<'a> TypeChecker<'a> {
)
}
/// Emits an error to the handler if the given type is not a field, scalar, or integer.
pub(crate) fn assert_field_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(
|type_: &Type| FIELD_TYPE.eq(type_) | SCALAR_TYPE.eq(type_) | INT_TYPES.contains(type_),
format!("{}, {}, {}", FIELD_TYPE, SCALAR_TYPE, types_to_string(&INT_TYPES),),
type_,
span,
)
}
/// Emits an error to the handler if the given type is not a field, group, scalar or integer.
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(

View File

@ -26,10 +26,10 @@ use crate::symbol::with_session_globals;
/// This is used in many spots throughout the rest of the Leo crates.
#[derive(Copy, Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Span {
/// The start position of the span.
/// The start (low) position of the span, inclusive.
pub lo: BytePos,
/// The end position of the span.
/// The length is simply `end - start`.
/// The end (high) position of the span, exclusive.
/// The length is simply `hi - lo`.
pub hi: BytePos,
}

View File

@ -191,8 +191,10 @@ symbols! {
function,
If: "if",
In: "in",
import,
input,
Let: "let",
leo,
log,
main,
Mut: "mut",
@ -211,7 +213,7 @@ symbols! {
public,
private,
owner,
balance,
gates,
// todo: remove these.
CONTAINER_PSEUDO_CIRCUIT: "$InputContainer",

View File

@ -401,13 +401,6 @@ create_messages!(
help: None,
}
@backtraced
failed_to_open_manifest {
args: (error: impl Display),
msg: format!("Failed to open manifest file: {}", error),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_load_instructions {
args: (error: impl Display),
@ -456,6 +449,13 @@ create_messages!(
msg: format!("Failed to parse the `aleo run` command.\nSnarkVM Error: {}", error),
help: None,
}
@backtraced
failed_to_execute_aleo_clean {
args: (error: impl Display),
msg: format!("Failed to execute the `aleo clean` command.\nSnarkVM Error: {}", error),
help: None,
}
);
impl CliError {

View File

@ -309,7 +309,21 @@ create_messages!(
@formatted
illegal_static_member_assignment {
args: (member: impl Display),
msg: format!("Tried to assign to static member `{}`", member),
msg: format!("Tried to assign to static member `{member}`"),
help: None,
}
@formatted
import_not_found {
args: (file_path: impl Display),
msg: format!("Attempted to import a file that does not exist `{file_path}`."),
help: None,
}
@formatted
cannot_open_cwd {
args: (err: impl ErrorArg),
msg: format!("Failed to open current working directory. Error: {err}"),
help: None,
}
);

View File

@ -0,0 +1,112 @@
// Copyright (C) 2019-2022 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::create_messages;
use std::fmt::{Debug, Display};
/// Generates the type name of a value.
pub fn type_name<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
create_messages!(
/// CliError enum that represents all the errors for the `leo-lang` crate.
FlattenError,
code_mask: 3000i32,
code_prefix: "FLA",
/// For when a constant operation would cause an overflow.
@formatted
binary_overflow {
args: (left: impl Display, op: impl Display, right: impl Display, right_type: impl Display),
msg: format!("The const operation `{left}{} {op} {right}{right_type}` causes an overflow.", type_name(&left)),
help: None,
}
/// For when a constant operation would cause an overflow.
@formatted
unary_overflow {
args: (left: impl Display, op: impl Display),
msg: format!("The const operation `{left}{} {op}` causes an overflow.", type_name(&left)),
help: None,
}
/// For when a loop uses a negative value.
@formatted
loop_has_neg_value {
args: (value: impl Display),
msg: format!(
"The loop has a negative loop bound `{value}`.",
),
help: None,
}
/// For when a loop bound goes negative or above usize::MAX
@formatted
incorrect_loop_bound {
args: (pos: impl Display, bound: impl Display),
msg: format!(
"The loop has an incorrect `{pos}` bound of `{bound}`.",
),
help: None,
}
/// For when a loop bound is non const.
@formatted
non_const_loop_bounds {
args: (pos: impl Display),
msg: format!(
"The loop has a `{pos}` bound that is non_const.",
),
help: None,
}
/// For when there is no main function.
@backtraced
no_main_function {
args: (),
msg: "The program has no main function.",
help: None,
}
/// For when the main function has a mismatching type for a constant input.
@formatted
main_function_mismatching_const_input_type {
args: (type_: impl Display, input_type_: impl Display),
msg: format!(
"The input was expected to be `{type_}` but the input file has `{input_type_}`"
),
help: None,
}
/// For when the main function has constant variable but the input file does not.
@formatted
input_file_does_not_have_constant {
args: (constant: impl Display),
msg: format!(
"The main function expected a constant `{constant}` but the input file does not have one."
),
help: None,
}
/// For when the main function has constant variables but the input file has none.
@formatted
input_file_has_no_constants {
args: (),
msg: "The main function expected constant inputs but the input file does not have any.",
help: None,
}
);

View File

@ -0,0 +1,19 @@
// Copyright (C) 2019-2022 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 Flattener error definitions.
pub mod flattener_errors;
pub use self::flattener_errors::*;

View File

@ -29,6 +29,10 @@ pub use self::cli::*;
pub mod compiler;
pub use self::compiler::*;
/// Contains the Flattener error definitions.
pub mod flattener;
pub use self::flattener::*;
/// Contains the Input error definitions.
pub mod input;
pub use self::input::*;
@ -67,9 +71,12 @@ pub enum LeoError {
/// Represents an Parser Error in a Leo Error.
#[error(transparent)]
ParserError(#[from] ParserError),
/// Represents an Type Checker Error in a Leo Error.
/// Represents a Type Checker Error in a Leo Error.
#[error(transparent)]
TypeCheckerError(#[from] TypeCheckerError),
/// Represents a Flatten Error in a Leo Error.
#[error(transparent)]
FlattenError(#[from] FlattenError),
/// Purely for just exiting with the correct status code and
/// not re-displaying an error.
#[error("")]
@ -92,6 +99,7 @@ impl LeoError {
ParserError(error) => error.error_code(),
PackageError(error) => error.error_code(),
TypeCheckerError(error) => error.error_code(),
FlattenError(error) => error.error_code(),
LastErrorCode(_) => unreachable!(),
Anyhow(_) => unimplemented!(), // todo: implement error codes for snarkvm errors.
}
@ -109,6 +117,7 @@ impl LeoError {
ParserError(error) => error.exit_code(),
PackageError(error) => error.exit_code(),
TypeCheckerError(error) => error.exit_code(),
FlattenError(error) => error.exit_code(),
LastErrorCode(code) => *code,
Anyhow(_) => unimplemented!(), // todo: implement exit codes for snarkvm errors.
}

View File

@ -317,14 +317,6 @@ create_messages!(
help: None,
}
/// For when removing the circuit file failed.
@backtraced
failed_to_remove_aleo_file {
args: (path: impl Debug),
msg: format!("failed removing aleo file from the provided file path - {:?}", path),
help: None,
}
/// For when removing the circuit file failed.
@backtraced
failed_to_remove_circuit_file {
@ -427,47 +419,39 @@ create_messages!(
@backtraced
failed_to_create_source_directory {
args: (error: impl ErrorArg),
msg: format!("failed creating source directory {}", error),
msg: format!("Failed creating source directory {}.", error),
help: None,
}
/// For when getting a source file entry failed.
/// For when getting a Leo file entry failed.
@backtraced
failed_to_get_source_file_entry {
failed_to_get_leo_file_entry {
args: (error: impl ErrorArg),
msg: format!("failed to get input file entry: {}", error),
msg: format!("Failed to get Leo file entry: {}.", error),
help: None,
}
/// For when getting the source file extension failed.
@backtraced
failed_to_get_source_file_extension {
failed_to_get_leo_file_extension {
args: (extension: impl Debug),
msg: format!("failed to get source file extension: {:?}", extension),
msg: format!("Failed to get Leo file extension: {:?}.", extension),
help: None,
}
/// For when getting the source file type failed.
/// For when getting the Leo file type failed.
@backtraced
failed_to_get_source_file_type {
failed_to_get_leo_file_type {
args: (file: impl Debug, error: impl ErrorArg),
msg: format!("failed to get source file `{:?}` type: {}", file, error),
msg: format!("Failed to get Leo file `{:?}` type: {}.", file, error),
help: None,
}
/// For when getting the source file has an invalid extension.
/// For when the Leo file has an invalid extension.
@backtraced
invalid_source_file_extension {
invalid_leo_file_extension {
args: (file: impl Debug, extension: impl Debug),
msg: format!("source file `{:?}` has invalid extension: {:?}", file, extension),
help: None,
}
/// For when getting the source file has an invalid file type.
@backtraced
invalid_source_file_type {
args: (file: impl Debug, type_: std::fs::FileType),
msg: format!("source file `{:?}` has invalid type: {:?}", file, type_),
msg: format!("Source file `{:?}` has invalid extension: {:?}.", file, extension),
help: None,
}
@ -541,11 +525,19 @@ create_messages!(
help: None,
}
/// For when opening a directory failed.
@backtraced
directory_not_found {
args: (dirname: impl Display, path: impl Display),
msg: format!("The `{}` does not exist at `{}`.", dirname, path),
help: None,
}
/// For when creating a directory failed.
@backtraced
failed_to_create_directory {
args: (dirname: impl Display, error: impl ErrorArg),
msg: format!("failed to create directory: {}, error: {}", dirname, error),
msg: format!("failed to create directory `{}`, error: {}.", dirname, error),
help: None,
}
@ -580,4 +572,53 @@ create_messages!(
msg: format!("i/o operation failed, file: {}, error: {}", file, error),
help: None,
}
@backtraced
failed_to_get_file_name {
args: (),
msg: "Failed to get names of Leo files in the `src/` directory.".to_string(),
help: Some("Check your `src/` directory for invalid file names.".to_string()),
}
@backtraced
failed_to_set_cwd {
args: (dir: impl Display, error: impl ErrorArg),
msg: format!("Failed to set current working directory to `{}`. Error: {}.", dir, error),
help: None,
}
@backtraced
failed_to_open_manifest {
args: (error: impl Display),
msg: format!("Failed to open manifest file: {}", error),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_open_aleo_file {
args: (error: impl Display),
msg: format!("Failed to open Aleo file: {}", error),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_create_aleo_file {
args: (error: impl Display),
msg: format!("Failed to create Aleo file: {}.", error),
help: None,
}
@backtraced
failed_to_write_aleo_file {
args: (error: impl Display),
msg: format!("Failed to write aleo file: {}.", error),
help: None,
}
@backtraced
failed_to_remove_aleo_file {
args: (error: impl Display),
msg: format!("Failed to remove aleo file: {}.", error),
help: None,
}
);

View File

@ -381,4 +381,11 @@ create_messages!(
msg: format!("Invalid associated access call to circuit {name}."),
help: Some("Double colon `::` syntax is only supported for core circuits in Leo for testnet3.".to_string()),
}
@formatted
leo_imports_only {
args: (name: impl Display),
msg: format!("Invalid import call to non-leo file `{name}`."),
help: Some("Only imports of Leo `.leo` files are currently supported.".to_string()),
}
);

View File

@ -2,3 +2,6 @@
[main]
public a: u32 = 1u32;
b: u32 = 2u32; // Input variable `b` is private by default.
[foo]
x: u64 = 5u64;

View File

@ -1,13 +0,0 @@
program helloworld.aleo;
function compute:
input r0 as u32.private;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;
function main:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;

View File

@ -2,3 +2,7 @@
function main(public a: u32, b: u32) -> u32 {
return a + b;
}
function foo(x: u64) -> u64 {
return x - x;
}

View File

@ -1,3 +1,7 @@
// The program input for message/src/main.leo
// To pass "m" into the "main" function we
// 1. Define the "Message" type.
// 2. Use brackets `{ }` to enclose the circuit members.
// 3. Define each circuit member `name : value`.
[main]
a: Message = Message { first: 2field, second: 3field };
m: Message = Message { first: 2field, second: 3field };

View File

@ -1,10 +0,0 @@
program message.aleo;
interface message:
first as field;
second as field;
function main:
input r0 as message.private;
add r0.first r0.second into r1;
output r1 as field.private;

View File

@ -1,9 +1,27 @@
// The 'message' main function.
// This example demonstrates the definition and initialization of a "circuit" type in Leo.
// Circuit types are similar to composite types in other languages such as "struct".
// The "Message" circuit type.
circuit Message {
// A circuit member named "first" with type "field".
first: field,
// A circuit member named "second" with type "field".
second: field,
}
// The "main" function of this Leo program takes a "Message" circuit type as input.
// To see how to input variable "m" is passed in open up `inputs/message.in`.
function main(m: Message) -> field {
return m.first + m.second;
// 1. Define the "Message" type.
// 2. Use brackets `{ }` to enclose the circuit members.
// 3. Define each circuit member `name : value`.
let m1: Message = Message {
first: m.first,
second: m.second,
};
// Access the members of a circuit with dot syntax.
// `circuit_name.member`
return m1.first + m1.second;
}

View File

@ -2,6 +2,6 @@
[main]
a: Record = Record {
owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah,
balance: 5u64,
gates: 5u64,
token_amount: 100u64
};

View File

@ -1,10 +0,0 @@
program token.aleo;
record token:
owner as address.private;
balance as u64.private;
token_amount as u64.private;
function main:
input r0 as token.record;
add r0.token_amount r0.token_amount into r1;
output r1 as field.private;

View File

@ -1,9 +1,12 @@
record Token {
owner: address,
balance: u64,
token_amount: u64,
// The token owner.
owner: address,
// The Aleo balance (in gates).
gates: u64,
// The token amount.
amount: u64,
}
function main(t: Token) -> u64 {
return t.token_amount + t.token_amount;
return t.amount + t.amount;
}

2
examples/transfer/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
outputs/
build/

View File

@ -0,0 +1,8 @@
# transfer.aleo
## Build Guide
To compile this Aleo program, run:
```bash
aleo build
```

View File

@ -0,0 +1,5 @@
// The program input for transfer/src/main.leo
[main]
owner: address = aleo12aw0kcnzyn5xj46z9u6mzpa67tzuqnvmwe0q2ejfjm8c2ue4pgys3877fr;
gates: u64 = 5u64;
amount: u64 = 100u64;

View File

@ -0,0 +1,10 @@
{
"program": "transfer.aleo",
"version": "0.0.0",
"description": "",
"development": {
"private_key": "APrivateKey1zkp3FxmbSYjkHsRBzZbCMGXY5u2sGCkgddNKVL7CDwy7KSe",
"address": "aleo12aw0kcnzyn5xj46z9u6mzpa67tzuqnvmwe0q2ejfjm8c2ue4pgys3877fr"
},
"license": "MIT"
}

View File

@ -0,0 +1,35 @@
record Token {
owner: address,
gates: u64,
amount: u64,
}
function main(owner: address, amount: u64) -> Token {
return Token {
owner: owner,
gates: 0u64,
amount: amount,
};
}
function transfer(
t: Token,
r: address,
a: u64
) -> (Token, Token) {
let change: u64 = t.amount - a;
let t1: Token = Token {
owner: r,
gates: 0u64,
amount: a,
};
let t2: Token = Token {
owner: t.owner,
gates: t.gates,
amount: change
};
return (t1, t2);
}

View File

@ -14,20 +14,26 @@
// 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 super::*;
use crate::{commands::Command, context::Context};
use leo_compiler::{Compiler, InputAst, OutputOptions};
use leo_errors::{CliError, Result};
use leo_errors::{CliError, CompilerError, PackageError, Result};
use leo_package::source::{SourceDirectory, MAIN_FILENAME};
use leo_package::{
inputs::InputFile,
outputs::{ChecksumFile, OutputsDirectory, OUTPUTS_DIRECTORY_NAME},
source::{MainFile, MAIN_FILENAME, SOURCE_DIRECTORY_NAME},
outputs::{ChecksumFile, OutputsDirectory},
};
use leo_span::symbol::with_session_globals;
use aleo::commands::Build as AleoBuild;
use clap::StructOpt;
use colored::Colorize;
use std::io::Write;
use std::path::{Path, PathBuf};
use leo_errors::emitter::Handler;
use leo_package::build::BuildDirectory;
use leo_package::imports::ImportsDirectory;
use tracing::span::Span;
/// Compiler Options wrapper for Build command. Also used by other commands which
@ -42,6 +48,8 @@ pub struct BuildOptions {
pub enable_initial_input_ast_snapshot: bool,
#[structopt(long, help = "Writes AST snapshot of the initial parse.")]
pub enable_initial_ast_snapshot: bool,
#[structopt(long, help = "Writes AST snapshot of the unrolled AST.")]
pub enable_unrolled_ast_snapshot: bool,
// Note: This is currently made optional since code generation is just a prototype.
#[structopt(
long,
@ -54,12 +62,14 @@ impl From<BuildOptions> for OutputOptions {
fn from(options: BuildOptions) -> Self {
let mut out_options = Self {
spans_enabled: options.enable_spans,
input_ast_initial: options.enable_initial_input_ast_snapshot,
ast_initial: options.enable_initial_ast_snapshot,
initial_input_ast: options.enable_initial_input_ast_snapshot,
initial_ast: options.enable_initial_ast_snapshot,
unrolled_ast: options.enable_unrolled_ast_snapshot,
};
if options.enable_all_ast_snapshots {
out_options.input_ast_initial = true;
out_options.ast_initial = true;
out_options.initial_input_ast = true;
out_options.initial_ast = true;
out_options.unrolled_ast = true;
}
out_options
@ -87,111 +97,175 @@ impl Command for Build {
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
// Get the package path.
let path = context.dir()?;
let package_path = context.dir()?;
// Get the program name.
let program_name = context.program_name()?;
let package_name = context.open_manifest()?.program_id().name().to_string();
// Sanitize the package path to the root directory.
let mut package_path = path.clone();
if package_path.is_file() {
package_path.pop();
}
// Create the outputs directory.
let outputs_directory = OutputsDirectory::create(&package_path)?;
// Construct the path to the output directory.
let mut output_directory = package_path.clone();
output_directory.push(OUTPUTS_DIRECTORY_NAME);
tracing::info!("Starting...");
// Compile the main.leo file along with constraints
if !MainFile::exists_at(&package_path) {
return Err(CliError::package_main_file_not_found().into());
}
// Construct the path to the main file in the source directory
let mut main_file_path = package_path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILENAME);
// Load the input file at `package_name.in`
let input_path = InputFile::new(&program_name).setup_file_path(&path);
// Create the outputs directory
OutputsDirectory::create(&package_path)?;
// Log compilation of files to console
tracing::info!("Compiling main program... ({:?})", main_file_path);
// Open the build directory.
let build_directory = BuildDirectory::open(&package_path)?;
// Initialize error handler
let handler = leo_errors::emitter::Handler::default();
// Create a new instance of the Leo compiler.
let mut program = Compiler::new(
program_name.to_string(),
String::from("aleo"),
&handler,
main_file_path,
output_directory,
Some(self.compiler_options.into()),
);
program.parse_input(input_path.to_path_buf())?;
// Fetch paths to all .leo files in the source directory.
let source_files = SourceDirectory::files(&package_path)?;
// Compute the current program checksum
let program_checksum = program.checksum()?;
// Compile the program.
{
// Compile the Leo program into Aleo instructions.
let (_, instructions) = program.compile_and_generate_instructions()?;
// // Parse the generated Aleo instructions into an Aleo file.
// let file = AleoFile::<Network>::from_str(&instructions).map_err(CliError::failed_to_load_instructions)?;
//
// // Create the path to the Aleo file.
// let mut aleo_file_path = package_path.clone();
// aleo_file_path.push(AleoFile::<Network >::main_file_name());
//
// // Write the Aleo file to `main.aleo`.
// file.write_to(&aleo_file_path)
// .map_err(|err| CliError::failed_to_write_to_aleo_file(aleo_file_path.display(), err))?;
// Create the path to the main Aleo file.
let mut aleo_file_path = package_path.clone();
aleo_file_path.push(AleoFile::<Network>::main_file_name());
// Write the instructions.
std::fs::File::create(&aleo_file_path)
.map_err(CliError::failed_to_load_instructions)?
.write_all(instructions.as_bytes())
.map_err(CliError::failed_to_load_instructions)?;
// Call the `aleo build` command from the Aleo SDK.
let res = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?;
// Log the result of the build
tracing::info!("Result: {}", res);
// Compile all .leo files into .aleo files.
for file_path in source_files.into_iter() {
compile_leo_file(
file_path,
&package_path,
&package_name,
&outputs_directory,
&build_directory,
&handler,
self.compiler_options.clone(),
)?;
}
// If a checksum file exists, check if it differs from the new checksum
let checksum_file = ChecksumFile::new(&program_name);
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
if !ImportsDirectory::is_empty(&package_path)? {
// Create Aleo build/imports/ directory.
let build_imports_directory = ImportsDirectory::create(&build_directory)?;
// Fetch paths to all .leo files in the imports directory.
let import_files = ImportsDirectory::files(&package_path)?;
// Compile all .leo files into .aleo files.
for file_path in import_files.into_iter() {
compile_leo_file(
file_path,
&package_path,
&package_name,
&outputs_directory,
&build_imports_directory,
&handler,
self.compiler_options.clone(),
)?;
}
}
// Load the input file at `package_name.in`
let input_file_path = InputFile::new(&package_name).setup_file_path(&package_path);
// Parse the input file.
let input_ast = if input_file_path.exists() {
// Load the input file into the source map.
let input_sf = with_session_globals(|s| s.source_map.load_file(&input_file_path))
.map_err(|e| CompilerError::file_read_error(&input_file_path, e))?;
leo_parser::parse_input(&handler, &input_sf.src, input_sf.start_pos).ok()
} else {
None
};
// Change the cwd to the build directory to compile aleo files.
std::env::set_current_dir(&build_directory)
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
// Call the `aleo build` command from the Aleo SDK.
let result = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?;
// Log the result of the build
tracing::info!("{}", result);
Ok(input_ast)
}
}
fn compile_leo_file(
file_path: PathBuf,
package_path: &PathBuf,
package_name: &String,
outputs: &Path,
build: &Path,
handler: &Handler,
options: BuildOptions,
) -> Result<()> {
// Construct the Leo file name with extension `foo.leo`.
let file_name = file_path
.file_name()
.and_then(|name| name.to_str())
.ok_or_else(PackageError::failed_to_get_file_name)?;
// Construct program name from file_path name `foo`.
let program_name = file_name
.strip_suffix(".leo")
.ok_or_else(PackageError::failed_to_get_file_name)?;
// Construct program id header for aleo file.
// Do not create a program with main.aleo as the ID.
let program_id_name = if file_name.eq(MAIN_FILENAME) {
package_name
} else {
program_name
};
// Create a new instance of the Leo compiler.
let mut program = Compiler::new(
program_id_name.to_string(),
String::from("aleo"), // todo: fetch this from Network::Testnet3
handler,
file_path.clone(),
outputs.to_path_buf(),
Some(options.into()),
);
// Check if we need to compile the Leo program.
let checksum_differs = {
// Compute the current program checksum.
let program_checksum = program.checksum()?;
// Get the current program checksum.
let checksum_file = ChecksumFile::new(program_name);
// If a checksum file exists, check if it differs from the new checksum.
let checksum_differs = if checksum_file.exists_at(package_path) {
let previous_checksum = checksum_file.read_from(package_path)?;
program_checksum != previous_checksum
} else {
// By default, the checksum differs if there is no checksum to compare against
// By default, the checksum differs if there is no checksum to compare against.
true
};
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the output directory
checksum_file.write_to(&path, program_checksum)?;
checksum_file.write_to(package_path, program_checksum)?;
tracing::debug!("Checksum saved ({:?})", path);
tracing::debug!("Checksum saved ({:?})", package_path);
}
tracing::info!("Complete");
checksum_differs
};
Ok(program.input_ast)
if checksum_differs {
// Compile the Leo program into Aleo instructions.
let (_, instructions) = program.compile_and_generate_instructions()?;
// Create the path to the Aleo file.
let mut aleo_file_path = build.to_path_buf();
aleo_file_path.push(format!("{}.aleo", program_name));
// Write the instructions.
std::fs::File::create(&aleo_file_path)
.map_err(CliError::failed_to_load_instructions)?
.write_all(instructions.as_bytes())
.map_err(CliError::failed_to_load_instructions)?;
// Prepare the path string.
let path_string = format!("(in \"{}\")", aleo_file_path.display());
// Log the build as successful.
tracing::info!(
"✅ Compiled '{}' into Aleo instructions {}",
file_name,
path_string.dimmed()
);
}
Ok(())
}

View File

@ -16,9 +16,11 @@
use crate::{commands::Command, context::Context};
use leo_errors::Result;
// use leo_package::outputs::ChecksumFile;
use leo_package::build::BuildDirectory;
use leo_package::outputs::OutputsDirectory;
use clap::StructOpt;
use colored::Colorize;
use tracing::span::Span;
/// Clean outputs folder command
@ -37,11 +39,16 @@ impl Command for Clean {
Ok(())
}
fn apply(self, _context: Context, _: Self::Input) -> Result<Self::Output> {
// let path = context.dir()?;
// let package_name = context.manifest()?.program_id().name().to_string();
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let path = context.dir()?;
// Removes the aleo file from the output directory.
// Removes the outputs/ directory.
let outputs_path = OutputsDirectory::remove(&path)?;
tracing::info!("✅ Cleaned the outputs directory {}", outputs_path.dimmed());
// Removes the build/ directory.
let build_path = BuildDirectory::remove(&path)?;
tracing::info!("✅ Cleaned the build directory {}", build_path.dimmed());
// Remove the checksum from the output directory
// ChecksumFile::new(&package_name).remove(&path)?;

View File

@ -33,8 +33,6 @@ pub use run::Run;
use crate::context::*;
use leo_errors::Result;
use snarkvm::file::AleoFile;
use std::time::Instant;
use tracing::span::Span;

View File

@ -14,12 +14,15 @@
// 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::commands::Network;
use crate::{
commands::{Command, ALEO_CLI_COMMAND},
context::Context,
};
use leo_errors::{CliError, Result};
use leo_errors::{CliError, PackageError, Result};
use leo_package::build::BUILD_DIRECTORY_NAME;
use leo_package::package::Package;
use snarkvm::file::AleoFile;
use aleo::commands::New as AleoNew;
@ -46,25 +49,58 @@ impl Command for New {
}
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
tracing::info!("Starting...");
// Call the `aleo new` command from the Aleo SDK.
let command =
AleoNew::try_parse_from(&[ALEO_CLI_COMMAND, &self.name]).map_err(CliError::failed_to_parse_aleo_new)?;
let result = command.parse().map_err(CliError::failed_to_execute_aleo_new)?;
// Derive the program directory path.
let mut path = context.dir()?;
path.push(&self.name);
// Initialize the Leo package in the directory created by `aleo new`.
Package::initialize(&self.name, &path)?;
// todo: modify the readme file to recommend building with `leo build`.
// Log the output of the `aleo new` command.
tracing::info!("{}", result);
// Derive the program directory path.
let mut package_path = context.dir()?;
package_path.push(&self.name);
// Initialize the Leo package in the directory created by `aleo new`.
Package::initialize(&self.name, &package_path)?;
// Change the cwd to the Leo package directory. to compile aleo files.
std::env::set_current_dir(&package_path)
.map_err(|err| PackageError::failed_to_set_cwd(package_path.display(), err))?;
// Open the program manifest.
let manifest = context.open_manifest()?;
// Create a path to the build directory.
let mut build_directory = package_path.clone();
build_directory.push(BUILD_DIRECTORY_NAME);
// Write the Aleo file into the build directory.
AleoFile::create(&build_directory, manifest.program_id(), true)
.map_err(PackageError::failed_to_create_aleo_file)?;
// build_aleo_file.push(AleoFile::<Network>::main_file_name());
//
// println!("{}", build_aleo_file.display());
//
//
// std::fs::File::create(build_aleo_file).map_err()
// aleo_file.write_to(&build_aleo_file).map_err(PackageError::failed_to_write_aleo_file)?;
// Open the `main.aleo` file path.
let aleo_file = AleoFile::open(&package_path, manifest.program_id(), true)
.map_err(PackageError::failed_to_open_aleo_file)?;
let mut aleo_file_path = package_path.clone();
aleo_file_path.push(AleoFile::<Network>::main_file_name());
// Remove the Aleo file from the package directory.
aleo_file
.remove(&aleo_file_path)
.map_err(PackageError::failed_to_remove_aleo_file)?;
Ok(())
}
}

View File

@ -20,7 +20,8 @@ use crate::{
commands::{Build, Command},
context::Context,
};
use leo_errors::{CliError, Result};
use leo_errors::{CliError, PackageError, Result};
use leo_package::build::BuildDirectory;
use aleo::commands::Run as AleoRun;
@ -30,6 +31,9 @@ use tracing::span::Span;
/// Build, Prove and Run Leo program with inputs
#[derive(StructOpt, Debug)]
pub struct Run {
#[structopt(name = "NAME", help = "The name of the program to run.", default_value = "main")]
name: String,
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
pub(crate) skip_key_check: bool,
@ -52,18 +56,24 @@ impl Command for Run {
.execute(context)
}
fn apply(self, _context: Context, input: Self::Input) -> Result<Self::Output> {
// Compose the `aleo run` command.
let mut arguments = vec![ALEO_CLI_COMMAND.to_string(), "main".to_string()];
fn apply(self, context: Context, input: Self::Input) -> Result<Self::Output> {
// Get the input values.
let mut values = match input {
Some(input_ast) => input_ast.values(),
let mut inputs = match input {
Some(input_ast) => input_ast.program_inputs(&self.name),
None => Vec::new(),
};
arguments.append(&mut values);
tracing::info!("Starting...");
// Compose the `aleo run` command.
let mut arguments = vec![ALEO_CLI_COMMAND.to_string(), self.name];
arguments.append(&mut inputs);
// Open the Leo build/ directory
let path = context.dir()?;
let build_directory = BuildDirectory::open(&path)?;
// Change the cwd to the Leo build/ directory to compile aleo files.
std::env::set_current_dir(&build_directory)
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
// Call the `aleo run` command from the Aleo SDK.
let command = AleoRun::try_parse_from(&arguments).map_err(CliError::failed_to_parse_aleo_run)?;

View File

@ -15,10 +15,14 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::commands::Network;
use leo_errors::{CliError, Result};
use leo_errors::{CliError, PackageError, Result};
use snarkvm::file::Manifest;
use std::{env::current_dir, path::PathBuf};
use leo_package::build::{BuildDirectory, BUILD_DIRECTORY_NAME};
use std::{
env::current_dir,
path::{Path, PathBuf},
};
/// Project context, manifest, current directory etc
/// All the info that is relevant in most of the commands
@ -41,16 +45,28 @@ impl Context {
}
}
/// Returns the program name as a String.
pub fn program_name(&self) -> Result<String> {
/// Returns the package name as a String.
/// Opens the manifest file `program.json` and creates the build directory if it doesn't exist.
pub fn open_manifest(&self) -> Result<Manifest<Network>> {
// Open the manifest file.
let path = self.dir()?;
let manifest = Manifest::<Network>::open(&path).map_err(CliError::failed_to_open_manifest)?;
let manifest = Manifest::<Network>::open(&path).map_err(PackageError::failed_to_open_manifest)?;
// Lookup the program id.
let program_id = manifest.program_id();
// Create the Leo build/ directory if it doesn't exist.
let build_path = path.join(Path::new(BUILD_DIRECTORY_NAME));
if !build_path.exists() {
BuildDirectory::create(&build_path)?;
}
// Mirror the program.json file in the Leo build/ directory for Aleo SDK compilation.
if !Manifest::<Network>::exists_at(&build_path) {
Manifest::<Network>::create(&build_path, program_id).map_err(PackageError::failed_to_open_manifest)?;
}
// Get package name from program id.
Ok(program_id.name().to_string())
Ok(manifest)
}
}

View File

@ -0,0 +1,65 @@
// Copyright (C) 2019-2022 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 leo_errors::{PackageError, Result};
use std::path::PathBuf;
use std::{borrow::Cow, fs, path::Path};
pub static BUILD_DIRECTORY_NAME: &str = "build/";
pub struct BuildDirectory;
impl BuildDirectory {
/// Returns the path to the build directory if it exists.
pub fn open(path: &Path) -> Result<PathBuf> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(BUILD_DIRECTORY_NAME) {
path.to_mut().push(BUILD_DIRECTORY_NAME);
}
if path.exists() {
Ok(path.to_path_buf())
} else {
Err(PackageError::directory_not_found(BUILD_DIRECTORY_NAME, path.display()).into())
}
}
/// Creates a directory at the provided path with the default directory name.
pub fn create(path: &Path) -> Result<PathBuf> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(BUILD_DIRECTORY_NAME) {
path.to_mut().push(BUILD_DIRECTORY_NAME);
}
fs::create_dir_all(&path).map_err(|err| PackageError::failed_to_create_directory(BUILD_DIRECTORY_NAME, err))?;
Ok(path.to_path_buf())
}
/// Removes the directory at the provided path.
pub fn remove(path: &Path) -> Result<String> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(BUILD_DIRECTORY_NAME) {
path.to_mut().push(BUILD_DIRECTORY_NAME);
}
if path.exists() {
fs::remove_dir_all(&path).map_err(|e| PackageError::failed_to_remove_directory(path.display(), e))?;
}
Ok(format!("(in \"{}\")", path.display()))
}
}

View File

@ -14,4 +14,5 @@
// 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 initialize;
pub mod directory;
pub use directory::*;

View File

@ -0,0 +1,85 @@
// Copyright (C) 2019-2022 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::parse_file_paths;
use leo_errors::{PackageError, Result};
use std::path::PathBuf;
use std::{borrow::Cow, fs, path::Path};
pub static IMPORTS_DIRECTORY_NAME: &str = "imports/";
pub struct ImportsDirectory;
impl ImportsDirectory {
/// Creates a directory at the provided path with the default directory name if it does not exist.
pub fn create(path: &Path) -> Result<PathBuf> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(IMPORTS_DIRECTORY_NAME) {
path.to_mut().push(IMPORTS_DIRECTORY_NAME);
}
if !path.exists() {
fs::create_dir_all(&path)
.map_err(|err| PackageError::failed_to_create_directory(IMPORTS_DIRECTORY_NAME, err))?;
}
Ok(path.to_path_buf())
}
/// Removes the directory at the provided path.
pub fn remove(path: &Path) -> Result<String> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(IMPORTS_DIRECTORY_NAME) {
path.to_mut().push(IMPORTS_DIRECTORY_NAME);
}
if path.exists() {
fs::remove_dir_all(&path).map_err(|e| PackageError::failed_to_remove_directory(path.display(), e))?;
}
Ok(format!("(in \"{}\")", path.display()))
}
/// Returns true if the imports directory does not exist or does not contain files.
pub fn is_empty(path: &Path) -> Result<bool> {
let imports_path = path.join(Path::new(IMPORTS_DIRECTORY_NAME));
if !imports_path.exists() {
return Ok(true);
}
Ok(imports_path
.read_dir()
.map_err(|err| PackageError::failed_to_read_file(IMPORTS_DIRECTORY_NAME, err))?
.next()
.is_none())
}
/// Returns a list of files in the imports directory.
pub fn files(path: &Path) -> Result<Vec<PathBuf>> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(IMPORTS_DIRECTORY_NAME) {
path.to_mut().push(IMPORTS_DIRECTORY_NAME);
}
let directory = fs::read_dir(&path).map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
let mut file_paths = Vec::new();
parse_file_paths(directory, &mut file_paths)?;
Ok(file_paths)
}
}

View File

@ -14,4 +14,5 @@
// 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 manifest;
pub mod directory;
pub use directory::*;

View File

@ -16,32 +16,49 @@
#![doc = include_str!("../README.md")]
// pub mod imports;
pub mod build;
pub mod imports;
pub mod inputs;
pub mod outputs;
pub mod package;
pub mod root;
pub mod source;
// use std::path::Path;
//
// use leo_errors::Result;
//
// pub struct LeoPackage;
//
// impl LeoPackage {
// /// Initializes a Leo package at the given path.
// pub fn initialize(package_name: &str, path: &Path, author: Option<String>) -> Result<()> {
// package::Package::initialize(package_name, path, author)
// }
//
// /// Returns `true` if the given Leo package name is valid.
// pub fn is_package_name_valid(package_name: &str) -> bool {
// package::Package::is_package_name_valid(package_name)
// }
//
// /// Removes an imported Leo package
// pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<()> {
// package::Package::remove_imported_package(package_name, path)
// }
// }
use leo_errors::{PackageError, Result};
use std::fs::ReadDir;
use std::{fs, path::PathBuf};
pub static LEO_FILE_EXTENSION: &str = ".leo";
pub(crate) fn parse_file_paths(directory: ReadDir, file_paths: &mut Vec<PathBuf>) -> Result<()> {
for file_entry in directory {
let file_entry = file_entry.map_err(PackageError::failed_to_get_leo_file_entry)?;
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file or directory
if file_path.is_dir() {
let directory =
fs::read_dir(&file_path).map_err(|err| PackageError::failed_to_read_file(file_path.display(), err))?;
parse_file_paths(directory, file_paths)?;
continue;
} else {
// Verify that the file has the Leo file extension
let file_extension = file_path
.extension()
.ok_or_else(|| PackageError::failed_to_get_leo_file_extension(file_path.as_os_str().to_owned()))?;
if file_extension != LEO_FILE_EXTENSION.trim_start_matches('.') {
return Err(PackageError::invalid_leo_file_extension(
file_path.as_os_str().to_owned(),
file_extension.to_owned(),
)
.into());
}
file_paths.push(file_path);
}
}
Ok(())
}

View File

@ -16,6 +16,7 @@
use leo_errors::{PackageError, Result};
use std::path::PathBuf;
use std::{borrow::Cow, fs, path::Path};
pub static OUTPUTS_DIRECTORY_NAME: &str = "outputs/";
@ -24,27 +25,27 @@ pub struct OutputsDirectory;
impl OutputsDirectory {
/// Creates a directory at the provided path with the default directory name.
pub fn create(path: &Path) -> Result<()> {
pub fn create(path: &Path) -> Result<PathBuf> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.to_mut().push(OUTPUTS_DIRECTORY_NAME);
}
fs::create_dir_all(&path).map_err(PackageError::failed_to_create_inputs_directory)?;
Ok(())
Ok(path.to_path_buf())
}
/// Removes the directory at the provided path.
pub fn remove(path: &Path) -> Result<()> {
pub fn remove(path: &Path) -> Result<String> {
let mut path = Cow::from(path);
if path.is_dir() && !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.to_mut().push(OUTPUTS_DIRECTORY_NAME);
}
if path.exists() {
fs::remove_dir_all(&path).map_err(PackageError::failed_to_create_inputs_directory)?;
fs::remove_dir_all(&path).map_err(|e| PackageError::failed_to_remove_directory(path.display(), e))?;
}
Ok(())
Ok(format!("(in \"{}\")", path.display()))
}
}

View File

@ -22,6 +22,7 @@ use crate::{
use leo_errors::{PackageError, Result};
use crate::build::BuildDirectory;
use serde::Deserialize;
use std::path::Path;
@ -168,19 +169,21 @@ impl Package {
// Create the source directory.
SourceDirectory::create(path)?;
// Create the input directory.
// Create the inputs directory.
InputsDirectory::create(path)?;
// Create the Leo build/ directory
BuildDirectory::create(path)?;
// Create the input file in the inputs directory.
InputFile::new(package_name).write_to(path)?;
// Create the main file in the source directory.
MainFile::new(package_name).write_to(path)?;
// Next, verify that a valid Leo package has been initialized in this directory
{
if !Self::is_initialized(package_name, path) {
return Err(PackageError::failed_to_initialize_package(package_name, path.as_os_str()).into());
}
if !Self::is_initialized(package_name, path) {
return Err(PackageError::failed_to_initialize_package(package_name, path.as_os_str()).into());
}
Ok(())

View File

@ -14,6 +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::parse_file_paths;
use leo_errors::{PackageError, Result};
use std::{
@ -24,8 +25,6 @@ use std::{
pub static SOURCE_DIRECTORY_NAME: &str = "src/";
pub static SOURCE_FILE_EXTENSION: &str = ".leo";
pub struct SourceDirectory;
impl SourceDirectory {
@ -43,38 +42,15 @@ impl SourceDirectory {
/// Returns a list of files in the source directory.
pub fn files(path: &Path) -> Result<Vec<PathBuf>> {
let mut path = Cow::from(path);
path.to_mut().push(SOURCE_DIRECTORY_NAME);
let directory = fs::read_dir(&path).map_err(PackageError::failed_to_read_inputs_directory)?;
let mut file_paths = Vec::new();
for file_entry in directory {
let file_entry = file_entry.map_err(PackageError::failed_to_get_source_file_entry)?;
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file
let file_type = file_entry
.file_type()
.map_err(|e| PackageError::failed_to_get_source_file_type(file_path.as_os_str().to_owned(), e))?;
if !file_type.is_file() {
return Err(PackageError::invalid_source_file_type(file_path.as_os_str().to_owned(), file_type).into());
}
// Verify that the file has the default file extension
let file_extension = file_path
.extension()
.ok_or_else(|| PackageError::failed_to_get_source_file_extension(file_path.as_os_str().to_owned()))?;
if file_extension != SOURCE_FILE_EXTENSION.trim_start_matches('.') {
return Err(PackageError::invalid_source_file_extension(
file_path.as_os_str().to_owned(),
file_extension.to_owned(),
)
.into());
}
file_paths.push(file_path);
if path.is_dir() && !path.ends_with(SOURCE_DIRECTORY_NAME) {
path.to_mut().push(SOURCE_DIRECTORY_NAME);
}
let directory = fs::read_dir(&path).map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
let mut file_paths = Vec::new();
parse_file_paths(directory, &mut file_paths)?;
Ok(file_paths)
}
}

View File

@ -1,133 +0,0 @@
// Copyright (C) 2019-2022 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::test_dir;
use leo_package::{
inputs::{InputFile, InputsDirectory, StateFile},
package::Package,
root::Manifest,
source::{MainFile, SourceDirectory},
};
const TEST_PACKAGE_NAME: &str = "test-package";
#[test]
fn initialize_valid_package() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, None).is_ok());
// Ensure a package is initialized at the `test_directory`
assert!(Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}
#[test]
fn initialize_valid_package_with_author() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, Some(String::from("test_user"))).is_ok());
// Ensure a package is initialized at the `test_directory`
assert!(Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}
#[test]
#[ignore]
fn initialize_fails_with_invalid_package_names() {
unimplemented!()
}
#[test]
fn initialize_fails_with_existing_manifest() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Manually add a manifest file to the `test_directory`
Manifest::new(TEST_PACKAGE_NAME, None)
.unwrap()
.write_to(&test_directory)
.unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, None).is_err());
// Ensure package is not initialized at the `test_directory`
assert!(!Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}
#[test]
fn initialize_fails_with_existing_input_file() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Manually add an inputs directory and an input file to the `test_directory`
InputsDirectory::create(&test_directory).unwrap();
InputFile::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, Some(String::from("test_user"))).is_err());
// Ensure package is not initialized at the `test_directory`
assert!(!Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}
#[test]
fn initialize_fails_with_existing_state_file() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Manually add an inputs directory and a state file to the `test_directory`
InputsDirectory::create(&test_directory).unwrap();
StateFile::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, None).is_err());
// Ensure package is not initialized at the `test_directory`
assert!(!Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}
#[test]
fn initialize_fails_with_existing_main_file() {
let test_directory = test_dir();
// Ensure a package can be initialized at the `test_directory`
assert!(Package::can_initialize(TEST_PACKAGE_NAME, &test_directory));
// Manually add a source directory and a main file to the `test_directory`
SourceDirectory::create(&test_directory).unwrap();
MainFile::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, &test_directory, None).is_err());
// Ensure package is not initialized at the `test_directory`
assert!(!Package::is_initialized(TEST_PACKAGE_NAME, &test_directory));
}

View File

@ -1,169 +0,0 @@
// Copyright (C) 2019-2022 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/>.
// Tests for package manifest
use crate::test_dir;
use leo_package::root::{Manifest, MANIFEST_FILENAME};
use std::{
convert::TryFrom,
fs::File,
io::{Read, Write},
path::{Path, PathBuf},
};
const OLD_MANIFEST_FORMAT: &str = r#"[package]
name = "test-package"
version = "0.1.0"
description = "Testing manifest updates."
license = "MIT"
remote = "author/test-package"
"#;
const NEW_REMOTE_FORMAT: &str = r#"
[remote]
author = "author"
"#;
const OLD_PROJECT_FORMAT: &str = "[package]";
const NEW_PROJECT_FORMAT: &str = "[project]";
/// Create a manifest file with outdated formatting.
fn create_outdated_manifest_file(path: PathBuf) -> PathBuf {
let mut path = path;
if path.is_dir() {
path.push(MANIFEST_FILENAME);
}
let mut file = File::create(&path).unwrap();
file.write_all(OLD_MANIFEST_FORMAT.as_bytes()).unwrap();
path
}
/// Read the manifest file into a string.
fn read_manifest_file(path: &Path) -> String {
let mut file = File::open(path).unwrap();
let size = file.metadata().unwrap().len() as usize;
let mut buffer = String::with_capacity(size);
file.read_to_string(&mut buffer).unwrap();
buffer
}
/// Read the manifest file and check that the remote format is updated.
fn remote_is_updated(path: &Path) -> bool {
let manifest_string = read_manifest_file(path);
for line in manifest_string.lines() {
if line.starts_with("remote") {
return false;
}
}
manifest_string.contains(NEW_REMOTE_FORMAT)
}
/// Read the manifest file and check that the project format is updated.
fn project_is_updated(path: &Path) -> bool {
let manifest_string = read_manifest_file(path);
!manifest_string.contains(OLD_PROJECT_FORMAT) && manifest_string.contains(NEW_PROJECT_FORMAT)
}
#[test]
#[cfg_attr(
any(feature = "manifest_refactor_project", feature = "manifest_refactor_remote"),
ignore
)]
fn test_manifest_no_refactors() {
// Create an outdated manifest file.
let test_directory = test_dir();
let manifest_path = create_outdated_manifest_file(test_directory);
// Load the manifest file, and discard the new struct.
let _manifest = Manifest::try_from(manifest_path.as_path()).unwrap();
// Check that the manifest file project has NOT been updated.
assert!(!project_is_updated(&manifest_path));
// Check that the manifest file remote has NOT been updated.
assert!(!remote_is_updated(&manifest_path));
}
#[test]
#[cfg_attr(
any(feature = "manifest_refactor_project", not(feature = "manifest_refactor_remote")),
ignore
)]
fn test_manifest_refactor_remote() {
// Create an outdated manifest file.
let test_directory = test_dir();
let manifest_path = create_outdated_manifest_file(test_directory);
// Load the manifest file, and discard the new struct.
let _manifest = Manifest::try_from(manifest_path.as_path()).unwrap();
// Check that the manifest file project has NOT been updated.
assert!(!project_is_updated(&manifest_path));
// Check that the manifest file remote has been updated.
assert!(remote_is_updated(&manifest_path));
}
#[test]
#[cfg_attr(
any(not(feature = "manifest_refactor_project"), feature = "manifest_refactor_remote"),
ignore
)]
fn test_manifest_refactor_project() {
// Create an outdated manifest file.
let test_directory = test_dir();
let manifest_path = create_outdated_manifest_file(test_directory);
// Load the manifest file, and discard the new struct.
let _manifest = Manifest::try_from(manifest_path.as_path()).unwrap();
// Check that the manifest file project has been updated.
assert!(project_is_updated(&manifest_path));
// Check that the manifest file remote has NOT been updated.
assert!(!remote_is_updated(&manifest_path));
}
#[test]
#[cfg_attr(
any(
not(feature = "manifest_refactor_project"),
not(feature = "manifest_refactor_remote")
),
ignore
)]
fn test_manifest_refactors() {
// Create an outdated manifest file.
let test_directory = test_dir();
let manifest_path = create_outdated_manifest_file(test_directory);
// Load the manifest file, and discard the new struct.
let _manifest = Manifest::try_from(manifest_path.as_path()).unwrap();
// Check that the manifest file project has been updated.
assert!(project_is_updated(&manifest_path));
// Check that the manifest file remote has been updated.
assert!(remote_is_updated(&manifest_path));
}

View File

@ -1,74 +0,0 @@
// Copyright (C) 2019-2022 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/>.
#![allow(clippy::module_inception)]
// pub mod initialize;
// pub mod manifest;
//
// use lazy_static::lazy_static;
// use std::{
// cell::RefCell,
// env, fs,
// path::PathBuf,
// sync::atomic::{AtomicUsize, Ordering},
// };
//
// const PACKAGE_TEST_DIRECTORY: &str = "package-testing";
//
// thread_local! {
// /// Establish a test id for each test.
// pub static TEST_ID: RefCell<Option<usize>> = RefCell::new(None);
// }
//
// lazy_static! {
// /// Create a testing directory for packages in `target/`
// pub static ref TEST_DIR: PathBuf = {
// let mut path = env::current_exe().unwrap();
// path.pop(); // Remove executable name
// path.pop(); // Remove 'debug'
//
// // Attempt to point at the `target` directory
// if path.file_name().and_then(|s| s.to_str()) != Some("target") {
// path.pop();
// }
//
// path.push(PACKAGE_TEST_DIRECTORY);
// fs::create_dir_all(&path).unwrap();
//
// path
// };
// }
//
// /// Create a new directory for each test based on the ID of the test.
// pub(crate) fn test_dir() -> PathBuf {
// static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
//
// let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
// TEST_ID.with(|n| *n.borrow_mut() = Some(id));
//
// let path: PathBuf = TEST_DIR.join(&format!("t{}", id));
//
// if path.exists() {
// if let Err(e) = fs::remove_dir_all(&path) {
// panic!("failed to remove {:?}: {:?}", &path, e)
// }
// }
//
// fs::create_dir_all(&path).unwrap();
//
// path
// }

View File

@ -3,9 +3,9 @@ namespace: Compile
expectation: Fail
*/
// This record does define the `balance` variable but with the wrong type.
// This record does define the `gates` variable but with the wrong type.
record Token {
balance: address,
gates: address,
owner: address,
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}

View File

@ -9,7 +9,7 @@ record Token {
// The token owner.
owner: address, // Cannot define two record variables with the same name.
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}
@ -15,7 +15,7 @@ record Token {
function mint(r0: address, r1: u64) -> Token {
return Token {
owner: r0,
balance: 0u64,
gates: 0u64,
amount: r1,
};
}
@ -24,5 +24,5 @@ function main(x: address) -> u64 {
const c: u64 = 1u64;
let t: Token = mint(x, c);
return t.balance;
return t.gates;
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}
@ -15,7 +15,7 @@ record Token {
function mint(owner: address, amount: u64) -> Token {
return Token {
owner,
balance: 0u64,
gates: 0u64,
amount,
};
}
@ -24,5 +24,5 @@ function main(x: address) -> u64 {
const c: u64 = 1u64;
let t: Token = mint(x, c);
return t.balance;
return t.gates;
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}
@ -15,7 +15,7 @@ record Token {
function mint(r0: address, r1: u64) -> Token {
return Token {
owner: r1, // This variable should be type address.
balance: 0u64,
gates: 0u64,
amount: r0, // This variable should be type u64.
};
}
@ -24,5 +24,5 @@ function main(x: address) -> u64 {
const c: u64 = 1u64;
let t: Token = mint(x, c);
return t.balance;
return t.gates;
}

View File

@ -7,7 +7,7 @@ record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}
@ -15,7 +15,7 @@ record Token {
function mint(r0: address, r1: u64) -> Token {
return Token {
sender: r0, // This variable should be named `owner`.
balance: 0u64,
gates: 0u64,
amount: r1,
};
}
@ -24,5 +24,5 @@ function main(x: address) -> u64 {
const c: u64 = 1u64;
let t: Token = mint(x, c);
return t.balance;
return t.gates;
}

View File

@ -6,7 +6,7 @@ expectation: Fail
// This record does not define the `owner` variable required for a record type.
record Token {
// The Aleo balance (in gates).
balance: u64,
gates: u64,
// The token amount.
amount: u64,
}

View File

@ -5,7 +5,7 @@ expectation: Fail
// This record does define the `owner` variable but with the wrong type.
record Token {
balance: u64,
gates: u64,
owner: bool,
}

View File

@ -1,6 +1,6 @@
/*
namespace: Compile
expectation: Pass
expectation: Fail
input_file: inputs/i8.in
*/

Some files were not shown because too many files have changed in this diff Show More