mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 18:21:38 +03:00
merge testnet3 regen tests
This commit is contained in:
commit
66b0fcc885
@ -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
1184
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
@ -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::*;
|
||||
|
||||
|
@ -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"),
|
||||
}
|
||||
|
917
compiler/ast/src/value/mod.rs
Normal file
917
compiler/ast/src/value/mod.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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,
|
||||
];
|
||||
|
||||
|
@ -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()),
|
||||
}
|
||||
|
@ -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)> {
|
||||
|
@ -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"),
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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"),
|
||||
|
@ -45,4 +45,7 @@ path = "../core"
|
||||
version = "1.5.3"
|
||||
|
||||
[dependencies.itertools]
|
||||
version = "0.10.3"
|
||||
version = "0.10.3"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2.15"
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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_)
|
||||
|
@ -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::*;
|
||||
|
49
compiler/passes/src/loop_unrolling/mod.rs
Normal file
49
compiler/passes/src/loop_unrolling/mod.rs
Normal 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()))
|
||||
}
|
||||
}
|
77
compiler/passes/src/loop_unrolling/range_iterator.rs
Normal file
77
compiler/passes/src/loop_unrolling/range_iterator.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
compiler/passes/src/loop_unrolling/unroll_expression.rs
Normal file
23
compiler/passes/src/loop_unrolling/unroll_expression.rs
Normal 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 = ();
|
||||
}
|
52
compiler/passes/src/loop_unrolling/unroll_program.rs
Normal file
52
compiler/passes/src/loop_unrolling/unroll_program.rs
Normal 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
|
||||
}
|
||||
}
|
93
compiler/passes/src/loop_unrolling/unroll_statement.rs
Normal file
93
compiler/passes/src/loop_unrolling/unroll_statement.rs
Normal 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
|
||||
}
|
||||
}
|
193
compiler/passes/src/loop_unrolling/unroller.rs
Normal file
193
compiler/passes/src/loop_unrolling/unroller.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
@ -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);
|
||||
}
|
||||
}
|
@ -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(
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
112
errors/src/errors/flattener/flattener_errors.rs
Normal file
112
errors/src/errors/flattener/flattener_errors.rs
Normal 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,
|
||||
}
|
||||
);
|
19
errors/src/errors/flattener/mod.rs
Normal file
19
errors/src/errors/flattener/mod.rs
Normal 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::*;
|
@ -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.
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
@ -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()),
|
||||
}
|
||||
);
|
||||
|
@ -2,3 +2,6 @@
|
||||
[main]
|
||||
public a: u32 = 1u32;
|
||||
b: u32 = 2u32; // Input variable `b` is private by default.
|
||||
|
||||
[foo]
|
||||
x: u64 = 5u64;
|
@ -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;
|
@ -1,4 +1,8 @@
|
||||
// The 'helloworld' main function.
|
||||
function main(public a: u32, b: u32) -> u32 {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function foo(x: u64) -> u64 {
|
||||
return x - x;
|
||||
}
|
@ -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 };
|
||||
|
@ -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;
|
@ -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;
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
[main]
|
||||
a: Record = Record {
|
||||
owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah,
|
||||
balance: 5u64,
|
||||
gates: 5u64,
|
||||
token_amount: 100u64
|
||||
};
|
||||
|
@ -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;
|
@ -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
2
examples/transfer/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
outputs/
|
||||
build/
|
8
examples/transfer/README.md
Normal file
8
examples/transfer/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# transfer.aleo
|
||||
|
||||
## Build Guide
|
||||
|
||||
To compile this Aleo program, run:
|
||||
```bash
|
||||
aleo build
|
||||
```
|
5
examples/transfer/inputs/transfer.in
Normal file
5
examples/transfer/inputs/transfer.in
Normal file
@ -0,0 +1,5 @@
|
||||
// The program input for transfer/src/main.leo
|
||||
[main]
|
||||
owner: address = aleo12aw0kcnzyn5xj46z9u6mzpa67tzuqnvmwe0q2ejfjm8c2ue4pgys3877fr;
|
||||
gates: u64 = 5u64;
|
||||
amount: u64 = 100u64;
|
10
examples/transfer/program.json
Normal file
10
examples/transfer/program.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"program": "transfer.aleo",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"development": {
|
||||
"private_key": "APrivateKey1zkp3FxmbSYjkHsRBzZbCMGXY5u2sGCkgddNKVL7CDwy7KSe",
|
||||
"address": "aleo12aw0kcnzyn5xj46z9u6mzpa67tzuqnvmwe0q2ejfjm8c2ue4pgys3877fr"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
35
examples/transfer/src/main.leo
Normal file
35
examples/transfer/src/main.leo
Normal 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);
|
||||
}
|
@ -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(())
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
65
leo/package/src/build/directory.rs
Normal file
65
leo/package/src/build/directory.rs
Normal 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()))
|
||||
}
|
||||
}
|
@ -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::*;
|
85
leo/package/src/imports/directory.rs
Normal file
85
leo/package/src/imports/directory.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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::*;
|
@ -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(())
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
@ -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));
|
||||
}
|
@ -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
|
||||
// }
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user