Merge pull request #1 from AleoHQ/development

Development
This commit is contained in:
Collin Chin 2020-04-23 17:46:17 -07:00 committed by GitHub
commit 7500f9a5fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 4193 additions and 600 deletions

30
Cargo.lock generated
View File

@ -575,19 +575,6 @@ dependencies = [
"tiny-keccak",
]
[[package]]
name = "language"
version = "0.1.0"
dependencies = [
"from-pest",
"lazy_static",
"pest",
"pest-ast",
"pest_derive",
"snarkos-gadgets",
"snarkos-models",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -600,6 +587,23 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "leo"
version = "0.1.0"
dependencies = [
"from-pest",
"lazy_static",
"pest",
"pest-ast",
"pest_derive",
"rand 0.7.3",
"snarkos-algorithms",
"snarkos-curves",
"snarkos-errors",
"snarkos-gadgets",
"snarkos-models",
]
[[package]]
name = "libc"
version = "0.2.67"

View File

@ -1,15 +1,27 @@
[package]
name = "language"
name = "leo"
version = "0.1.0"
authors = ["howardwu <howardwu@berkeley.edu>"]
authors = ["The Aleo Team <hello@aleo.org>"]
edition = "2018"
[dependencies]
from-pest = "0.3.1"
lazy_static = "1.3.0"
pest = "2.0"
pest_derive = "2.0"
pest-ast = "0.3.3"
[lib]
name = "leo"
path = "src/lib.rs"
snarkos-gadgets = { path = "../snarkOS/gadgets" }
snarkos-models = { path = "../snarkOS/models" }
[[bin]]
name = "leo"
path = "src/main.rs"
[dependencies]
snarkos-algorithms = { path = "../snarkOS/algorithms", version = "0.8.0" }
snarkos-curves = { path = "../snarkOS/curves", version = "0.8.0" }
snarkos-errors = { path = "../snarkOS/errors", version = "0.8.0" }
snarkos-gadgets = { path = "../snarkOS/gadgets", version = "0.8.0" }
snarkos-models = { path = "../snarkOS/models", version = "0.8.0" }
from-pest = { version = "0.3.1" }
lazy_static = { version = "1.3.0" }
pest = { version = "2.0" }
pest-ast = { version = "0.3.3" }
pest_derive = { version = "2.0" }
rand = { version = "0.7" }

265
README.md
View File

@ -1 +1,264 @@
# language
# The Leo Language
* All code examples can be copied and pasted into simple.program directly and executed with cargo run
* Programs should be formatted:
1. Import definitions
2. Stuct definitions
3. Function definitions
### Integers:
Currently, all integers are parsed as u32.
You can choose to explicitly add the type or let the compiler interpret implicitly.
```rust
function main() -> (u32) {
a = 1u32 + 1u32
b = 1 - 1
c = 2 * 2
d = 4 / 2
e = 2 ** 3
return a
}
```
### Field Elements:
Field elements must have the type added explicitly.
```rust
function main() -> (fe) {
f = 21888242871839275222246405745257275088548364400416034343698204186575808495617fe
a = 1fe + 1fe
b = 1fe - 1fe
c = 2fe * 2fe
d = 4fe / 2fe
return a
}
```
### Booleans:
```rust
function main() -> (bool) {
a = true || false
b = false && false
c = 1 == 1
return a
}
```
### Arrays:
Leo supports static arrays with fixed length.
Array type must be explicitly stated
```rust
function main() -> (u32[2]) {
// initialize an integer array with integer values
u32[3] a = [1, 2, 3]
// set a member to a value
a[2] = 4
// initialize an array of 4 values all equal to 42
u32[4] b = [42; 4]
// initialize an array of 5 values copying all elements of b using a spread
u32[5] c = [1, ...b]
// initialize an array copying a slice from `c`
d = c[1..3]
// initialize a field array
fe[2] e = [5fe; 2]
// initialize a boolean array
bool[3] f = [true, false || true, true]
// return an array
return d
}
```
### Structs:
```rust
struct Point {
u32 x
u32 y
}
function main() -> (u32) {
Point p = Point {x: 1, y: 0}
return p.x
}
```
```rust
struct Foo {
bool x
}
function main() -> (Foo) {
Foo f = Foo {x: true}
f.x = false
return f
}
```
### Conditionals:
```rust
function main() -> (u32) {
y = if 3==3 then 1 else 5 fi
return y
}
```
```rust
function main() -> (fe) {
a = 1fe
for i in 0..4 do
a = a + 1fe
endfor
return a
}
```
### Functions:
```rust
function test1(a : u32) -> (u32) {
return a + 1
}
function test2(b: fe) -> (fe) {
return b * 2fe
}
function test3(c: bool) -> (bool) {
return c && true
}
function main() -> (u32) {
return test1(5)
}
```
#### Function Scope:
```rust
function foo() -> (field) {
// return myGlobal <- not allowed
return 42fe
}
function main() -> (field) {
myGlobal = 42fe
return foo()
}
```
### Parameters:
Main function arguments are allocated as public or private variables in the program's constaint system.
```rust
function main(a: private fe) -> (fe) {
return a
}
```
```rust
function main(a: public fe) -> (fe) {
return a
}
```
### Imports:
Note that there can only be one main function across all imported files.
/simple_import.leo
```rust
struct Point {
u32 x
u32 y
}
```
/simple.leo
```rust
from "./simple_import" import Point
function main() -> (Point) {
Point p = Point { x: 1, y: 2}
return p
}
```
Default exports are not currently supported.
There should only be one main function across all files.
# Leo CLI
## Develop
To compile your program and verify that it builds properly, run:
```
leo build
```
To execute unit tests on your program, run:
```
leo test
```
## Run
To perform the program setup, producing a proving key and verification key, run:
```
leo setup
```
Leo uses cryptographic randomness from your machine to perform the setup. The proving key and verification key are stored in the `target` directory as `.leo.pk` and `.leo.vk`:
```
{$LIBRARY}/target/{$PROGRAM}.leo.pk
{$LIBRARY}/target/{$PROGRAM}.leo.vk
```
To execute the program and produce an execution proof, run:
```
leo prove
```
Leo starts by checking the `target` directory for an existing `.leo.pk` file. If it doesn't exist, it will proceed to run `leo setup` and then continue.
Once again, Leo uses cryptographic randomness from your machine to produce the proof. The proof is stored in the `target` directory as `.leo.proof`:
```
{$LIBRARY}/target/{$PROGRAM}.leo.proof
```
To verify the program proof, run:
```
leo verify
```
Leo starts by checking the `target` directory for an existing `.leo.proof` file. If it doesn't exist, it will proceed to run `leo prove` and then continue.
After the verifier is run, Leo will output either `true` or `false` based on the verification.
## Remote
To use remote compilation features, start by authentication with:
```
leo login
```
You will proceed to authenticate using your username and password. Next, Leo will parse your `Leo.toml` file for `remote = True` to confirm whether remote compilation is enabled.
If remote compilation is enabled, Leo syncs your workspace so when you run `leo build`, `leo test`, `leo setup` and `leo prove`, your program will run the program setup and execution performantly on remote machines.
This speeds up the testing cycle and helps the developer to iterate significantly faster.
## Publish
To package your program as a gadget and publish it online, run:
```
leo publish
```
Leo will proceed to snapshot your directory and upload your directory to the circuit manager. Leo will verify that `leo build` succeeds and that `leo test` passes without error.
If your gadget name has already been taken, `leo publish` will fail.
## Deploy
To deploy your program to the blockchain, run:
```
leo deploy
```
## TODO
- Change `target` directory to some other directory to avoid collision.
- Figure out how `leo prove` should take in assignments.
- Come up with a serialization format for `.leo.pk`, `.leo.vk`, and `.leo.proof`.

View File

@ -1,5 +0,0 @@
65279,1179403647,1463895090
3.1415927,2.7182817,1.618034
-40,-273.15
13,42
65537
1 65279,1179403647,1463895090
2 3.1415927,2.7182817,1.618034
3 -40,-273.15
4 13,42
5 65537

4
simple.leo Normal file
View File

@ -0,0 +1,4 @@
function main() -> (u32) {
a = 1 + 1
return a
}

View File

@ -1,2 +0,0 @@
x = 5 + 3
y = x * (x * 2)

4
simple_import.leo Normal file
View File

@ -0,0 +1,4 @@
struct Point {
u32 x
u32 y
}

963
src/ast.rs Normal file
View File

@ -0,0 +1,963 @@
//! Abstract syntax tree (ast) representation from leo.pest.
//!
//! @file zokrates_program.rs
//! @author Howard Wu <howard@aleo.org>
//! @date 2020
use from_pest::{ConversionError, FromPest, Void};
use pest::{
error::Error,
iterators::{Pair, Pairs},
prec_climber::{Assoc, Operator, PrecClimber},
Parser, Span,
};
use pest_ast::FromPest;
use std::fmt;
#[derive(Parser)]
#[grammar = "leo.pest"]
pub struct LanguageParser;
pub fn parse(input: &str) -> Result<Pairs<Rule>, Error<Rule>> {
LanguageParser::parse(Rule::file, input)
}
fn span_into_string(span: Span) -> String {
span.as_str().to_string()
}
lazy_static! {
static ref PRECEDENCE_CLIMBER: PrecClimber<Rule> = precedence_climber();
}
// Visibility
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility_public))]
pub struct Public {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility_private))]
pub struct Private {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility))]
pub enum Visibility {
Public(Public),
Private(Private),
}
// Unary Operations
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_pre_not))]
pub struct Not<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::operation_post_increment))]
// pub struct Increment<'ast> {
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
//
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::operation_post_decrement))]
// pub struct Decrement<'ast> {
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
// Binary Operations
#[derive(Clone, Debug, PartialEq)]
pub enum BinaryOperator {
Or,
And,
Eq,
Neq,
Geq,
Gt,
Leq,
Lt,
Add,
Sub,
Mul,
Div,
Pow,
}
// Types
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_u32))]
pub struct U32Type<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_field))]
pub struct FieldType<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_bool))]
pub struct BooleanType<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_struct))]
pub struct StructType<'ast> {
pub variable: Variable<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_basic))]
pub enum BasicType<'ast> {
U32(U32Type<'ast>),
Field(FieldType<'ast>),
Boolean(BooleanType<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_basic_or_struct))]
pub enum BasicOrStructType<'ast> {
Struct(StructType<'ast>),
Basic(BasicType<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty_array))]
pub struct ArrayType<'ast> {
pub ty: BasicType<'ast>,
pub count: Value<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::ty))]
pub enum Type<'ast> {
Basic(BasicType<'ast>),
Array(ArrayType<'ast>),
Struct(StructType<'ast>),
}
impl<'ast> fmt::Display for Type<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Basic(ref _ty) => write!(f, "basic"),
Type::Array(ref _ty) => write!(f, "array"),
Type::Struct(ref _ty) => write!(f, "struct"),
}
}
}
// Values
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_number))]
pub struct Number<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Number<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_u32))]
pub struct U32<'ast> {
pub number: Number<'ast>,
pub ty: Option<U32Type<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for U32<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.number)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_field))]
pub struct Field<'ast> {
pub number: Number<'ast>,
pub ty: FieldType<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Field<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.number)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_boolean))]
pub struct Boolean<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Boolean<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value))]
pub enum Value<'ast> {
Field(Field<'ast>),
Boolean(Boolean<'ast>),
U32(U32<'ast>),
}
impl<'ast> Value<'ast> {
pub fn span(&self) -> &Span<'ast> {
match self {
Value::U32(value) => &value.span,
Value::Field(value) => &value.span,
Value::Boolean(value) => &value.span,
}
}
}
impl<'ast> fmt::Display for Value<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Value::U32(ref value) => write!(f, "{}", value),
Value::Field(ref value) => write!(f, "{}", value),
Value::Boolean(ref value) => write!(f, "{}", value),
}
}
}
// Variables
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::variable))]
pub struct Variable<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Variable<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
// Access
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::from_expression))]
pub struct FromExpression<'ast>(pub Expression<'ast>);
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::to_expression))]
pub struct ToExpression<'ast>(pub Expression<'ast>);
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::range))]
pub struct Range<'ast> {
pub from: Option<FromExpression<'ast>>,
pub to: Option<ToExpression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::range_or_expression))]
pub enum RangeOrExpression<'ast> {
Range(Range<'ast>),
Expression(Expression<'ast>),
}
impl<'ast> fmt::Display for RangeOrExpression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RangeOrExpression::Expression(ref expression) => write!(f, "{}", expression),
RangeOrExpression::Range(ref range) => write!(
f,
"{}..{}",
range
.from
.as_ref()
.map(|e| e.0.to_string())
.unwrap_or("".to_string()),
range
.to
.as_ref()
.map(|e| e.0.to_string())
.unwrap_or("".to_string())
),
}
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::access_call))]
pub struct CallAccess<'ast> {
pub expressions: Vec<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::access_array))]
pub struct ArrayAccess<'ast> {
pub expression: RangeOrExpression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::access_member))]
pub struct MemberAccess<'ast> {
pub variable: Variable<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::access))]
pub enum Access<'ast> {
Array(ArrayAccess<'ast>),
Call(CallAccess<'ast>),
Member(MemberAccess<'ast>),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_postfix))]
pub struct PostfixExpression<'ast> {
pub variable: Variable<'ast>,
pub accesses: Vec<Access<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::assignee_access))]
pub enum AssigneeAccess<'ast> {
Array(ArrayAccess<'ast>),
Member(MemberAccess<'ast>),
}
impl<'ast> fmt::Display for AssigneeAccess<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AssigneeAccess::Array(ref array) => write!(f, "[{}]", array.expression),
AssigneeAccess::Member(ref member) => write!(f, ".{}", member.variable),
}
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::assignee))]
pub struct Assignee<'ast> {
pub variable: Variable<'ast>,
pub accesses: Vec<AssigneeAccess<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Assignee<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.variable)?;
for (i, access) in self.accesses.iter().enumerate() {
write!(f, "{}", access)?;
if i < self.accesses.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::spread))]
pub struct Spread<'ast> {
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for Spread<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "...{}", self.expression)
}
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::spread_or_expression))]
pub enum SpreadOrExpression<'ast> {
Spread(Spread<'ast>),
Expression(Expression<'ast>),
}
impl<'ast> fmt::Display for SpreadOrExpression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SpreadOrExpression::Spread(ref spread) => write!(f, "{}", spread),
SpreadOrExpression::Expression(ref expression) => write!(f, "{}", expression),
}
}
}
// Arrays
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_array_inline))]
pub struct ArrayInlineExpression<'ast> {
pub expressions: Vec<SpreadOrExpression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_array_initializer))]
pub struct ArrayInitializerExpression<'ast> {
pub expression: Box<SpreadOrExpression<'ast>>,
pub count: Value<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// Structs
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::struct_field))]
pub struct StructField<'ast> {
pub ty: Type<'ast>,
pub variable: Variable<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::struct_definition))]
pub struct Struct<'ast> {
pub variable: Variable<'ast>,
pub fields: Vec<StructField<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::inline_struct_member))]
pub struct InlineStructMember<'ast> {
pub variable: Variable<'ast>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_inline_struct))]
pub struct StructInlineExpression<'ast> {
pub variable: Variable<'ast>,
pub members: Vec<InlineStructMember<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// Expressions
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_not))]
pub struct NotExpression<'ast> {
pub operation: Not<'ast>,
pub expression: Box<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::expression_increment))]
// pub struct IncrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Increment<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
//
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::expression_decrement))]
// pub struct DecrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Decrement<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
#[derive(Clone, Debug, PartialEq)]
pub struct BinaryExpression<'ast> {
pub operation: BinaryOperator,
pub left: Box<Expression<'ast>>,
pub right: Box<Expression<'ast>>,
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_conditional))]
pub struct TernaryExpression<'ast> {
pub first: Box<Expression<'ast>>,
pub second: Box<Expression<'ast>>,
pub third: Box<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expression<'ast> {
Value(Value<'ast>),
Variable(Variable<'ast>),
Not(NotExpression<'ast>),
// Increment(IncrementExpression<'ast>),
// Decrement(DecrementExpression<'ast>),
Binary(BinaryExpression<'ast>),
Ternary(TernaryExpression<'ast>),
ArrayInline(ArrayInlineExpression<'ast>),
ArrayInitializer(ArrayInitializerExpression<'ast>),
StructInline(StructInlineExpression<'ast>),
Postfix(PostfixExpression<'ast>),
}
impl<'ast> Expression<'ast> {
pub fn binary(
operation: BinaryOperator,
left: Box<Expression<'ast>>,
right: Box<Expression<'ast>>,
span: Span<'ast>,
) -> Self {
Expression::Binary(BinaryExpression {
operation,
left,
right,
span,
})
}
pub fn ternary(
first: Box<Expression<'ast>>,
second: Box<Expression<'ast>>,
third: Box<Expression<'ast>>,
span: Span<'ast>,
) -> Self {
Expression::Ternary(TernaryExpression {
first,
second,
third,
span,
})
}
pub fn span(&self) -> &Span<'ast> {
match self {
Expression::Value(expression) => &expression.span(),
Expression::Variable(expression) => &expression.span,
Expression::Not(expression) => &expression.span,
// Expression::Increment(expression) => &expression.span,
// Expression::Decrement(expression) => &expression.span,
Expression::Binary(expression) => &expression.span,
Expression::Ternary(expression) => &expression.span,
Expression::ArrayInline(expression) => &expression.span,
Expression::ArrayInitializer(expression) => &expression.span,
Expression::StructInline(expression) => &expression.span,
Expression::Postfix(expression) => &expression.span,
}
}
}
impl<'ast> fmt::Display for Expression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expression::Value(ref expression) => write!(f, "{}", expression),
Expression::Variable(ref expression) => write!(f, "{}", expression),
Expression::Not(ref expression) => write!(f, "!{}", expression.expression),
// Expression::Increment(ref expression) => write!(f, "{}++", expression.expression),
// Expression::Decrement(ref expression) => write!(f, "{}--", expression.expression),
Expression::Binary(ref expression) => {
write!(f, "{} == {}", expression.left, expression.right)
}
Expression::Ternary(ref expression) => write!(
f,
"if {} then {} else {} fi",
expression.first, expression.second, expression.third
),
Expression::ArrayInline(ref expression) => {
for (i, spread_or_expression) in expression.expressions.iter().enumerate() {
write!(f, "{}", spread_or_expression)?;
if i < expression.expressions.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
}
Expression::ArrayInitializer(ref expression) => {
write!(f, "[{} ; {}]", expression.expression, expression.count)
}
Expression::StructInline(ref expression) => {
write!(f, "inline struct display not impl {}", expression.variable)
}
Expression::Postfix(ref expression) => {
write!(f, "Postfix display not impl {}", expression.variable)
}
}
}
}
fn precedence_climber() -> PrecClimber<Rule> {
PrecClimber::new(vec![
Operator::new(Rule::operation_or, Assoc::Left),
Operator::new(Rule::operation_and, Assoc::Left),
Operator::new(Rule::operation_eq, Assoc::Left)
| Operator::new(Rule::operation_neq, Assoc::Left),
Operator::new(Rule::operation_geq, Assoc::Left)
| Operator::new(Rule::operation_gt, Assoc::Left)
| Operator::new(Rule::operation_leq, Assoc::Left)
| Operator::new(Rule::operation_lt, Assoc::Left),
Operator::new(Rule::operation_add, Assoc::Left)
| Operator::new(Rule::operation_sub, Assoc::Left),
Operator::new(Rule::operation_mul, Assoc::Left)
| Operator::new(Rule::operation_div, Assoc::Left),
Operator::new(Rule::operation_pow, Assoc::Left),
])
}
fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
Box::new(match pair.as_rule() {
Rule::expression_term => {
let clone = pair.clone();
let next = clone.into_inner().next().unwrap();
match next.as_rule() {
Rule::expression => Expression::from_pest(&mut pair.into_inner()).unwrap(), // Parenthesis case
Rule::expression_inline_struct => {
Expression::StructInline(
StructInlineExpression::from_pest(&mut pair.into_inner()).unwrap(),
)
},
Rule::expression_array_inline => {
Expression::ArrayInline(
ArrayInlineExpression::from_pest(&mut pair.into_inner()).unwrap()
)
},
Rule::expression_array_initializer => {
Expression::ArrayInitializer(
ArrayInitializerExpression::from_pest(&mut pair.into_inner()).unwrap()
)
},
Rule::expression_conditional => {
Expression::Ternary(
TernaryExpression::from_pest(&mut pair.into_inner()).unwrap(),
)
},
Rule::expression_not => {
let span = next.as_span();
let mut inner = next.into_inner();
let operation = match inner.next().unwrap().as_rule() {
Rule::operation_pre_not => Not::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
rule => unreachable!("`expression_not` should yield `operation_pre_not`, found {:#?}", rule)
};
let expression = parse_term(inner.next().unwrap());
Expression::Not(NotExpression { operation, expression, span })
},
// Rule::expression_increment => {
// println!("expression increment");
// // let span = next.as_span();
// // let mut inner = next.into_inner();
// // let expression = parse_term(inner.next().unwrap());
// // let operation = match inner.next().unwrap().as_rule() {
// // Rule::operation_post_increment => Increment::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// // rule => unreachable!("`expression_increment` should yield `operation_post_increment`, found {:#?}", rule)
// // };
// // Expression::Increment(IncrementExpression { operation, expression, span })
// },
// Rule::expression_decrement => {
// println!("expression decrement");
// // let span = next.as_span();
// // let mut inner = next.into_inner();
// // let expression = parse_term(inner.next().unwrap());
// // let operation = match inner.next().unwrap().as_rule() {
// // Rule::operation_post_decrement => Decrement::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// // rule => unreachable!("`expression_decrement` should yield `operation_post_decrement`, found {:#?}", rule)
// // };
// // Expression::Decrement(DecrementExpression { operation, expression, span })
// },
Rule::expression_postfix => {
Expression::Postfix(
PostfixExpression::from_pest(&mut pair.into_inner()).unwrap(),
)
}
Rule::expression_primitive => {
let next = next.into_inner().next().unwrap();
match next.as_rule() {
Rule::value => {
Expression::Value(
Value::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
)
},
Rule::variable => Expression::Variable(
Variable::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
),
rule => unreachable!("`expression_primitive` should contain one of [`value`, `variable`], found {:#?}", rule)
}
},
rule => unreachable!("`term` should contain one of ['value', 'variable', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}", rule)
}
}
rule => unreachable!(
"`parse_expression_term` should be invoked on `Rule::expression_term`, found {:#?}",
rule
),
})
}
fn binary_expression<'ast>(
lhs: Box<Expression<'ast>>,
pair: Pair<'ast, Rule>,
rhs: Box<Expression<'ast>>,
) -> Box<Expression<'ast>> {
let (start, _) = lhs.span().clone().split();
let (_, end) = rhs.span().clone().split();
let span = start.span(&end);
Box::new(match pair.as_rule() {
Rule::operation_or => Expression::binary(BinaryOperator::Or, lhs, rhs, span),
Rule::operation_and => Expression::binary(BinaryOperator::And, lhs, rhs, span),
Rule::operation_eq => Expression::binary(BinaryOperator::Eq, lhs, rhs, span),
Rule::operation_neq => Expression::binary(BinaryOperator::Neq, lhs, rhs, span),
Rule::operation_geq => Expression::binary(BinaryOperator::Geq, lhs, rhs, span),
Rule::operation_gt => Expression::binary(BinaryOperator::Gt, lhs, rhs, span),
Rule::operation_leq => Expression::binary(BinaryOperator::Leq, lhs, rhs, span),
Rule::operation_lt => Expression::binary(BinaryOperator::Lt, lhs, rhs, span),
Rule::operation_add => Expression::binary(BinaryOperator::Add, lhs, rhs, span),
Rule::operation_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span),
Rule::operation_mul => Expression::binary(BinaryOperator::Mul, lhs, rhs, span),
Rule::operation_div => Expression::binary(BinaryOperator::Div, lhs, rhs, span),
Rule::operation_pow => Expression::binary(BinaryOperator::Pow, lhs, rhs, span),
_ => unreachable!(),
})
}
impl<'ast> FromPest<'ast> for Expression<'ast> {
type Rule = Rule;
type FatalError = Void;
fn from_pest(pest: &mut Pairs<'ast, Rule>) -> Result<Self, ConversionError<Void>> {
let mut clone = pest.clone();
let pair = clone.next().ok_or(::from_pest::ConversionError::NoMatch)?;
match pair.as_rule() {
Rule::expression => {
// Transfer iterated state to pest.
*pest = clone;
Ok(*PRECEDENCE_CLIMBER.climb(pair.into_inner(), parse_term, binary_expression))
}
_ => Err(ConversionError::NoMatch),
}
}
}
// Statements
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_assign))]
pub struct AssignStatement<'ast> {
pub assignee: Assignee<'ast>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_definition))]
pub struct DefinitionStatement<'ast> {
pub ty: Type<'ast>,
pub variable: Variable<'ast>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_return))]
pub struct ReturnStatement<'ast> {
pub expressions: Vec<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_for))]
pub struct ForStatement<'ast> {
pub index: Variable<'ast>,
pub start: Expression<'ast>,
pub stop: Expression<'ast>,
pub statements: Vec<Statement<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement))]
pub enum Statement<'ast> {
Assign(AssignStatement<'ast>),
Definition(DefinitionStatement<'ast>),
Return(ReturnStatement<'ast>),
Iteration(ForStatement<'ast>),
}
impl<'ast> fmt::Display for AssignStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.assignee, self.expression)
}
}
impl<'ast> fmt::Display for DefinitionStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} = {}", self.ty, self.variable, self.expression)
}
}
impl<'ast> fmt::Display for ReturnStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, expression) in self.expressions.iter().enumerate() {
write!(f, "{}", expression)?;
if i < self.expressions.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
}
}
impl<'ast> fmt::Display for ForStatement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"for {} in {}..{} do {:#?} endfor",
self.index, self.start, self.stop, self.statements
)
}
}
impl<'ast> fmt::Display for Statement<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Assign(ref statement) => write!(f, "{}", statement),
Statement::Definition(ref statement) => write!(f, "{}", statement),
Statement::Return(ref statement) => write!(f, "{}", statement),
Statement::Iteration(ref statement) => write!(f, "{}", statement),
}
}
}
// Functions
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::parameter))]
pub struct Parameter<'ast> {
pub variable: Variable<'ast>,
pub visibility: Option<Visibility>,
pub ty: Type<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::function_name))]
pub struct FunctionName<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::function_definition))]
pub struct Function<'ast> {
pub function_name: FunctionName<'ast>,
pub parameters: Vec<Parameter<'ast>>,
pub returns: Vec<Type<'ast>>,
pub statements: Vec<Statement<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// Utilities
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::EOI))]
pub struct EOI;
// Imports
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::import_source))]
pub struct ImportSource<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::main_import))]
pub struct MainImport<'ast> {
pub source: ImportSource<'ast>,
pub alias: Option<Variable<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::from_import))]
pub struct FromImport<'ast> {
pub source: ImportSource<'ast>,
pub symbol: Variable<'ast>,
pub alias: Option<Variable<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::import))]
pub enum Import<'ast> {
Main(MainImport<'ast>),
From(FromImport<'ast>),
}
// File
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::file))]
pub struct File<'ast> {
pub imports: Vec<Import<'ast>>,
pub structs: Vec<Struct<'ast>>,
pub functions: Vec<Function<'ast>>,
pub eoi: EOI,
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

@ -1,3 +0,0 @@
field = { (ASCII_DIGIT | "." | "-")+ }
record = { field ~ ("," ~ field)* }
file = { SOI ~ (record ~ ("\r\n" | "\n"))* ~ EOI }

View File

@ -1,44 +0,0 @@
extern crate pest;
#[macro_use]
extern crate pest_derive;
use pest::Parser;
use std::fs;
#[derive(Parser)]
#[grammar = "csv.pest"]
pub struct CSVParser;
fn main() {
let unparsed_file = fs::read_to_string("numbers.csv").expect("cannot read file");
let file = CSVParser::parse(Rule::file, &unparsed_file)
.expect("unsuccessful parse") // unwrap the parse result
.next().unwrap(); // get and unwrap the `file` rule; never fails
let mut field_sum: f64 = 0.0;
let mut record_count: u64 = 0;
for record in file.into_inner() {
match record.as_rule() {
Rule::record => {
record_count += 1;
for field in record.into_inner() {
field_sum += field.as_str().parse::<f64>().unwrap();
}
}
Rule::EOI => (),
_ => unreachable!(),
}
}
println!("Sum of fields: {}", field_sum);
println!("Number of records: {}", record_count);
// let successful_parse = CSVParser::parse(Rule::field, "-273.15");
// println!("{:?}", successful_parse);
//
// let unsuccessful_parse = CSVParser::parse(Rule::field, "this is not a number");
// println!("{:?}", unsuccessful_parse);
}

View File

@ -1,108 +0,0 @@
// file = { SOI ~ NEWLINE* ~ import_section* ~ NEWLINE* ~ ty_struct_definition* ~ NEWLINE* ~ function_definition* ~ EOI }
// file = { SOI ~ NEWLINE* ~ import_section* ~ NEWLINE* ~ EOI }
file = { SOI ~ NEWLINE* ~ statement* ~ NEWLINE* ~ EOI }
/// Visibility
visibility_public = { "public" }
visibility_private = { "private" }
visibility = { visibility_public | visibility_private }
/// Unary Operations
operation_pre_not = { "!" }
operation_post_increment = { "++" }
operation_post_decrement = { "--" }
/// Binary Operations
operation_and = { "&&" }
operation_or = { "||" }
operation_eq = { "==" }
operation_neq = { "!=" }
operation_geq = { ">=" }
operation_gt = { ">" }
operation_leq = { "<=" }
operation_lt = { "<" }
operation_add = { "+" }
operation_sub = { "-" }
operation_mul = { "*" }
operation_div = { "/" }
operation_pow = { "**" }
operation_binary = _ {
operation_and | operation_or |
operation_eq | operation_neq |
operation_geq | operation_gt | operation_leq | operation_lt |
operation_add | operation_sub | operation_mul | operation_div | operation_pow
}
// operation_add_assign = { "+=" }
// operation_sub_assign = { "-=" }
// operation_mul_assign = { "*=" }
// operation_div_assign = { "/=" }
/// Values
value_boolean = { "true" | "false" }
value_field = @{ "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) }
// TODO: Boolean array, field array
value = { value_boolean | value_field }
/// Variables
// TODO: Include "import" and "conditional"
protected_name = { visibility | value_boolean | "return" }
// keyword = @{ "as" | "in" | "return" | "export" | "false" |
// "def" | "in" | "return" | "struct" | "true" }
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
/// Expressions
// Consider structs, conditionals, postfix, primary, inline array, array initializer, and unary
expression_primitive = { value | variable }
expression_not = { operation_pre_not ~ expression_term }
expression_term = { expression_primitive | expression_not | ("(" ~ expression ~ ")") }
expression = { expression_term ~ (operation_binary ~ expression_term)* }
// expression_increment = { expression ~ operation_post_increment }
// expression_decrement = { expression ~ operation_post_decrement }
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
/// Statements
statement_assign = { variable ~ "=" ~ expression }
statement_return = { "return" ~ expression_tuple }
statement = { (statement_return | (statement_assign) ~ NEWLINE) ~ NEWLINE* }
/// Utilities
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
WHITESPACE = _{ " " | "\t" ~ NEWLINE }
// /// Conditionals
//
// conditional_if = { "if" }
// conditional_else = { "else" }
//
// conditional_for = { "for" }
//
// conditional = { conditional_if | conditional_else | conditional_for }
// /// Helpers
//
// helper_range = { expression+ ~ ".." ~ expression+ } // Confirm that '+' is the correct repetition

178
src/leo.pest Normal file
View File

@ -0,0 +1,178 @@
/// Visibility
visibility_public = { "public" }
visibility_private = { "private" }
visibility = { visibility_public | visibility_private }
/// Unary Operations
operation_pre_not = { "!" }
expression_not = { operation_pre_not ~ expression_term }
// operation_post_increment = { "++" }
// expression_increment = { operation_post_increment ~ expression_term }
//
// operation_post_decrement = { "--" }
// expression_decrement = { operation_post_decrement ~ expression_term }
/// Binary Operations
operation_and = { "&&" }
operation_or = { "||" }
operation_eq = { "==" }
operation_neq = { "!=" }
operation_geq = { ">=" }
operation_gt = { ">" }
operation_leq = { "<=" }
operation_lt = { "<" }
operation_add = { "+" }
operation_sub = { "-" }
operation_mul = { "*" }
operation_div = { "/" }
operation_pow = { "**" }
operation_binary = _ {
operation_and | operation_or |
operation_eq | operation_neq |
operation_geq | operation_gt | operation_leq | operation_lt |
operation_add | operation_sub | operation_pow | operation_mul | operation_div
}
// operation_add_assign = { "+=" }
// operation_sub_assign = { "-=" }
// operation_mul_assign = { "*=" }
// operation_div_assign = { "/=" }
/// Types
ty_u32 = {"u32"}
ty_field = {"fe"}
ty_bool = {"bool"}
ty_basic = { ty_u32 | ty_field | ty_bool }
ty_struct = { variable }
ty_basic_or_struct = {ty_basic | ty_struct }
ty_array = {ty_basic ~ ("[" ~ value ~ "]")+ }
ty = {ty_array | ty_basic | ty_struct}
type_list = _{(ty ~ ("," ~ ty)*)?}
/// Values
value_number = @{ "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)}
value_u32 = { value_number ~ ty_u32? }
value_field = { value_number ~ ty_field }
value_boolean = { "true" | "false" }
value = { value_field | value_boolean | value_u32 }
/// Variables
// TODO: Include "import" and "conditional"
protected_name = { visibility | value_boolean | "return" }
// keyword = @{ "as" | "in" | "return" | "export" | "false" |
// "def" | "in" | "return" | "struct" | "true" }
variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
expression_primitive = { value | variable }
/// Access
from_expression = { expression }
to_expression = { expression }
range = { from_expression? ~ ".." ~ to_expression }
range_or_expression = { range | expression }
access_array = { "[" ~ range_or_expression ~ "]" }
access_call = { "(" ~ expression_tuple ~ ")" }
access_member = { "." ~ variable }
access = { access_array | access_call | access_member }
expression_postfix = { variable ~ access+ }
assignee_access = { access_array | access_member }
assignee = { variable ~ assignee_access* }
spread = { "..." ~ expression }
spread_or_expression = { spread | expression }
/// Arrays
inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expression)*)?}
expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"}
expression_array_initializer = { "[" ~ spread_or_expression ~ ";" ~ value ~ "]" }
/// Structs
struct_field = { ty ~ variable }
struct_field_list = _{(struct_field ~ (NEWLINE+ ~ struct_field)*)? }
struct_definition = { "struct" ~ variable ~ "{" ~ NEWLINE* ~ struct_field_list ~ NEWLINE* ~ "}" ~ NEWLINE* }
inline_struct_member = { variable ~ ":" ~ expression }
inline_struct_member_list = _{(inline_struct_member ~ ("," ~ NEWLINE* ~ inline_struct_member)*)? ~ ","? }
expression_inline_struct = { variable ~ "{" ~ NEWLINE* ~ inline_struct_member_list ~ NEWLINE* ~ "}" }
/// Conditionals
expression_conditional = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"}
/// Expressions
expression_term = {
("(" ~ expression ~ ")")
| expression_inline_struct
| expression_conditional
| expression_postfix
| expression_primitive
| expression_not
// | expression_increment
// | expression_decrement
| expression_array_inline
| expression_array_initializer
}
expression = { expression_term ~ (operation_binary ~ expression_term)* }
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
/// Statements
statement_assign = { assignee ~ "=" ~ expression }
statement_definition = { ty ~ variable ~ "=" ~ expression }
statement_return = { "return" ~ expression_tuple }
statement_for = { "for" ~ variable ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"}
statement = {
(statement_return
| (statement_for
| statement_definition
| statement_assign
) ~ NEWLINE
) ~ NEWLINE*
}
/// Functions
parameter = {variable ~ ":" ~ visibility? ~ ty}
parameter_list = _{(parameter ~ ("," ~ parameter)*)?}
function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
function_definition = {"function" ~ function_name ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ "{" ~ NEWLINE* ~ statement* ~ "}"}
/// Utilities
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* }
/// Imports
import = { main_import | from_import }
from_import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ variable ~ ("as" ~ variable)? ~ NEWLINE*}
main_import = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ variable)? ~ NEWLINE+}
import_source = @{(!"\"" ~ ANY)*}
/// Program File
file = { SOI ~ NEWLINE* ~ import* ~ NEWLINE* ~ struct_definition* ~ NEWLINE* ~ function_definition* ~ NEWLINE* ~ EOI }

11
src/lib.rs Normal file
View File

@ -0,0 +1,11 @@
extern crate from_pest;
extern crate pest;
extern crate pest_ast;
#[macro_use]
extern crate pest_derive;
#[macro_use]
extern crate lazy_static;
pub mod ast;
pub mod program;

View File

@ -1,440 +1,111 @@
extern crate pest;
#[macro_use]
extern crate pest_derive;
use leo::*;
extern crate from_pest;
#[macro_use]
extern crate pest_ast;
use snarkos_algorithms::snark::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
};
use snarkos_curves::bls12_377::{Bls12_377, Fr};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem},
};
#[macro_use]
extern crate lazy_static;
use from_pest::FromPest;
use rand::thread_rng;
use std::{
fs,
marker::PhantomData,
time::{Duration, Instant},
};
use pest::Parser;
use std::fs;
pub struct Benchmark<F: Field + PrimeField> {
_engine: PhantomData<F>,
}
#[derive(Parser)]
#[grammar = "language.pest"]
pub struct LanguageParser;
mod ast {
use from_pest::ConversionError;
use from_pest::FromPest;
use from_pest::Void;
use pest::iterators::{Pair, Pairs};
use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::Span;
use pest_ast::FromPest;
use super::Rule;
fn span_into_string(span: Span) -> String {
span.as_str().to_string()
}
lazy_static! {
static ref PRECEDENCE_CLIMBER: PrecClimber<Rule> = precedence_climber();
}
fn precedence_climber() -> PrecClimber<Rule> {
PrecClimber::new(vec![
Operator::new(Rule::operation_or, Assoc::Left),
Operator::new(Rule::operation_and, Assoc::Left),
Operator::new(Rule::operation_eq, Assoc::Left)
| Operator::new(Rule::operation_neq, Assoc::Left),
Operator::new(Rule::operation_geq, Assoc::Left)
| Operator::new(Rule::operation_gt, Assoc::Left)
| Operator::new(Rule::operation_leq, Assoc::Left)
| Operator::new(Rule::operation_lt, Assoc::Left),
Operator::new(Rule::operation_add, Assoc::Left)
| Operator::new(Rule::operation_sub, Assoc::Left),
Operator::new(Rule::operation_mul, Assoc::Left)
| Operator::new(Rule::operation_div, Assoc::Left),
Operator::new(Rule::operation_pow, Assoc::Left),
])
}
fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
Box::new(match pair.as_rule() {
Rule::expression_term => {
let clone = pair.clone();
let next = clone.into_inner().next().unwrap();
match next.as_rule() {
Rule::expression_primitive => {
let next = next.into_inner().next().unwrap();
match next.as_rule() {
Rule::value => Expression::Value(
Value::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
),
Rule::variable => Expression::Variable(
Variable::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
),
rule => unreachable!("`expression_primitive` should contain one of [`value`, `variable`], found {:#?}", rule)
}
}
Rule::expression_not => {
let span = next.as_span();
let mut inner = next.into_inner();
let operation = match inner.next().unwrap().as_rule() {
Rule::operation_pre_not => Not::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
rule => unreachable!("`expression_not` should yield `operation_pre_not`, found {:#?}", rule)
};
let expression = parse_term(inner.next().unwrap());
Expression::Not(NotExpression { operation, expression, span })
},
Rule::expression => Expression::from_pest(&mut pair.into_inner()).unwrap(), // Parenthesis case
// Rule::expression_increment => {
// let span = next.as_span();
// let mut inner = next.into_inner();
// let expression = parse_expression_term(inner.next().unwrap());
// let operation = match inner.next().unwrap().as_rule() {
// Rule::operation_post_increment => Increment::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// rule => unreachable!("`expression_increment` should yield `operation_post_increment`, found {:#?}", rule)
// };
// Expression::Increment(IncrementExpression { operation, expression, span })
// },
// Rule::expression_decrement => {
// let span = next.as_span();
// let mut inner = next.into_inner();
// let expression = parse_expression_term(inner.next().unwrap());
// let operation = match inner.next().unwrap().as_rule() {
// Rule::operation_post_decrement => Decrement::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
// rule => unreachable!("`expression_decrement` should yield `operation_post_decrement`, found {:#?}", rule)
// };
// Expression::Decrement(DecrementExpression { operation, expression, span })
// },
rule => unreachable!("`term` should contain one of ['value', 'variable', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}", rule)
}
}
rule => unreachable!("`parse_expression_term` should be invoked on `Rule::expression_term`, found {:#?}", rule),
})
}
fn binary_expression<'ast>(
lhs: Box<Expression<'ast>>,
pair: Pair<'ast, Rule>,
rhs: Box<Expression<'ast>>,
) -> Box<Expression<'ast>> {
let (start, _) = lhs.span().clone().split();
let (_, end) = rhs.span().clone().split();
let span = start.span(&end);
Box::new(match pair.as_rule() {
Rule::operation_or => Expression::binary(BinaryOperator::Or, lhs, rhs, span),
Rule::operation_and => Expression::binary(BinaryOperator::And, lhs, rhs, span),
Rule::operation_eq => Expression::binary(BinaryOperator::Eq, lhs, rhs, span),
Rule::operation_neq => Expression::binary(BinaryOperator::Neq, lhs, rhs, span),
Rule::operation_geq => Expression::binary(BinaryOperator::Geq, lhs, rhs, span),
Rule::operation_gt => Expression::binary(BinaryOperator::Gt, lhs, rhs, span),
Rule::operation_leq => Expression::binary(BinaryOperator::Leq, lhs, rhs, span),
Rule::operation_lt => Expression::binary(BinaryOperator::Lt, lhs, rhs, span),
Rule::operation_add => Expression::binary(BinaryOperator::Add, lhs, rhs, span),
Rule::operation_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span),
Rule::operation_mul => Expression::binary(BinaryOperator::Mul, lhs, rhs, span),
Rule::operation_div => Expression::binary(BinaryOperator::Div, lhs, rhs, span),
Rule::operation_pow => Expression::binary(BinaryOperator::Pow, lhs, rhs, span),
_ => unreachable!(),
})
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::file))]
pub struct File<'ast> {
pub statement: Vec<Statement<'ast>>,
pub eoi: EOI,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// Visibility
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility_public))]
pub struct Public {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility_private))]
pub struct Private {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::visibility))]
pub enum Visibility {
Public(Public),
Private(Private),
}
// Unary Operations
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_pre_not))]
pub struct Not<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_post_increment))]
pub struct Increment<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::operation_post_decrement))]
pub struct Decrement<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// Binary Operations
#[derive(Debug, PartialEq, Clone)]
pub enum BinaryOperator {
Or,
And,
Eq,
Neq,
Geq,
Gt,
Leq,
Lt,
Add,
Sub,
Mul,
Div,
Pow,
}
// Values
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_boolean))]
pub struct Boolean<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value_field))]
pub struct Field<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::value))]
pub enum Value<'ast> {
Boolean(Boolean<'ast>),
Field(Field<'ast>),
}
impl<'ast> Value<'ast> {
pub fn span(&self) -> &Span<'ast> {
match self {
Value::Boolean(value) => &value.span,
Value::Field(value) => &value.span,
}
impl<F: Field + PrimeField> Benchmark<F> {
pub fn new() -> Self {
Self {
_engine: PhantomData,
}
}
}
// Variables
impl<F: Field + PrimeField> ConstraintSynthesizer<F> for Benchmark<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
// Read in file as string
let unparsed_file = fs::read_to_string("simple.leo").expect("cannot read file");
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::variable))]
pub struct Variable<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
// Parse the file using leo.pest
let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse");
// Build the abstract syntax tree
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// println!("{:#?}", syntax_tree);
let program = program::Program::<'_, F>::from(syntax_tree);
println!(" compiled: {:#?}", program);
let program = program.name("simple".into());
program::ResolvedProgram::generate_constraints(cs, program);
Ok(())
}
// Expressions
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::expression_not))]
pub struct NotExpression<'ast> {
pub operation: Not<'ast>,
pub expression: Box<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::expression_increment))]
// pub struct IncrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Increment<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
//
// #[derive(Clone, Debug, FromPest, PartialEq)]
// #[pest_ast(rule(Rule::expression_decrement))]
// pub struct DecrementExpression<'ast> {
// pub expression: Box<Expression<'ast>>,
// pub operation: Decrement<'ast>,
// #[pest_ast(outer())]
// pub span: Span<'ast>,
// }
#[derive(Clone, Debug, PartialEq)]
pub struct BinaryExpression<'ast> {
pub operation: BinaryOperator,
pub left: Box<Expression<'ast>>,
pub right: Box<Expression<'ast>>,
pub span: Span<'ast>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expression<'ast> {
Value(Value<'ast>),
Variable(Variable<'ast>),
Not(NotExpression<'ast>),
Binary(BinaryExpression<'ast>),
// Increment(IncrementExpression<'ast>),
// Decrement(DecrementExpression<'ast>),
}
impl<'ast> Expression<'ast> {
pub fn binary(
operation: BinaryOperator,
left: Box<Expression<'ast>>,
right: Box<Expression<'ast>>,
span: Span<'ast>,
) -> Self {
Expression::Binary(BinaryExpression { operation, left, right, span })
}
pub fn span(&self) -> &Span<'ast> {
match self {
Expression::Value(expression) => &expression.span(),
Expression::Variable(expression) => &expression.span,
Expression::Not(expression) => &expression.span,
Expression::Binary(expression) => &expression.span,
// Expression::Increment(expression) => &expression.span,
// Expression::Decrement(expression) => &expression.span,
}
}
}
impl<'ast> FromPest<'ast> for Expression<'ast> {
type Rule = Rule;
type FatalError = Void;
fn from_pest(pest: &mut Pairs<'ast, Rule>) -> Result<Self, ConversionError<Void>> {
let mut clone = pest.clone();
let pair = clone.next().ok_or(::from_pest::ConversionError::NoMatch)?;
match pair.as_rule() {
Rule::expression => {
// Transfer iterated state to pest.
*pest = clone;
Ok(*PRECEDENCE_CLIMBER.climb(pair.into_inner(), parse_term, binary_expression))
}
_ => Err(ConversionError::NoMatch),
}
}
}
// Statements
#[derive(Debug, FromPest, PartialEq, Clone)]
#[pest_ast(rule(Rule::statement_assign))]
pub struct AssignStatement<'ast> {
pub variable: Variable<'ast>,
pub expression: Expression<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Debug, FromPest, PartialEq, Clone)]
#[pest_ast(rule(Rule::statement_return))]
pub struct ReturnStatement<'ast> {
pub expressions: Vec<Expression<'ast>>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement))]
pub enum Statement<'ast> {
Assign(AssignStatement<'ast>),
Return(ReturnStatement<'ast>),
}
// Utilities
#[derive(Debug, FromPest, PartialEq, Clone)]
#[pest_ast(rule(Rule::EOI))]
pub struct EOI;
}
fn main() {
use crate::from_pest::FromPest;
use snarkos_gadgets::curves::edwards_bls12::FqGadget;
use snarkos_models::gadgets::{r1cs::{ConstraintSystem, TestConstraintSystem, Fr}, utilities::{alloc::AllocGadget, boolean::Boolean}};
let mut setup = Duration::new(0, 0);
let mut proving = Duration::new(0, 0);
let mut verifying = Duration::new(0, 0);
let unparsed_file = fs::read_to_string("simple.program").expect("cannot read file");
let mut file = LanguageParser::parse(Rule::file, &unparsed_file).expect("unsuccessful parse");
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
let rng = &mut thread_rng();
for statement in syntax_tree.statement {
match statement {
ast::Statement::Assign(statement) => {
println!("{:#?}", statement);
},
ast::Statement::Return(statement) => {
let start = Instant::now();
}
}
}
let params = {
let circuit = Benchmark::<Fr>::new();
generate_random_parameters::<Bls12_377, _, _>(circuit, rng).unwrap()
};
let mut cs = TestConstraintSystem::<Fr>::new();
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&params.vk);
Boolean::alloc(cs.ns(|| format!("boolean")), || Ok(true));
setup += start.elapsed();
let start = Instant::now();
let proof = {
let c = Benchmark::new();
create_random_proof(c, &params, rng).unwrap()
};
println!("\n\n number of constraints for input: {}", cs.num_constraints());
proving += start.elapsed();
// let _inputs: Vec<_> = [1u32; 1].to_vec();
// for token in file.into_inner() {
// match token.as_rule() {
// Rule::statement => println!("{:?}", token.into_inner()),
// Rule::EOI => println!("END"),
// _ => println!("{:?}", token.into_inner()),
// }
// // println!("{:?}", token);
// }
let start = Instant::now();
let is_success = verify_proof(&prepared_verifying_key, &proof, &[]).unwrap();
// let mut field_sum: f64 = 0.0;
// let mut record_count: u64 = 0;
verifying += start.elapsed();
println!(" ");
println!(" Setup time : {:?} milliseconds", setup.as_millis());
println!(" Prover time : {:?} milliseconds", proving.as_millis());
println!(
" Verifier time : {:?} milliseconds",
verifying.as_millis()
);
println!(" Verifier output : {}", is_success);
println!(" ");
// let mut cs = TestConstraintSystem::<Fr>::new();
//
// for record in file.into_inner() {
// match record.as_rule() {
// Rule::record => {
// record_count += 1;
// println!("\n satisfied: {:?}", cs.is_satisfied());
//
// println!(
// "\n number of constraints for input: {}",
// cs.num_constraints()
// );
//
// for field in record.into_inner() {
// field_sum += field.as_str().parse::<f64>().unwrap();
// }
// }
// Rule::EOI => (),
// _ => unreachable!(),
// }
// }
// println!("Sum of fields: {}", field_sum);
// println!("Number of records: {}", record_count);
// let successful_parse = LanguageParser::parse(Rule::value, "-273");
// println!("{:?}", successful_parse);
// let unsuccessful_parse = CSVParser::parse(Rule::field, "this is not a number");
// println!("{:?}", unsuccessful_parse);
}

View File

@ -0,0 +1,154 @@
//! Methods to enforce constraints on booleans in a resolved aleo program.
//!
//! @file boolean.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::{ResolvedProgram, ResolvedValue};
use crate::program::{new_variable_from_variable, Parameter, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::ConstraintSystem,
utilities::{alloc::AllocGadget, boolean::Boolean, eq::EqGadget},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn bool_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<bool>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
} else {
Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::Boolean(number));
parameter_variable
}
pub(crate) fn boolean_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
unimplemented!("Cannot enforce boolean array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<bool>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// Boolean::alloc(cs.ns(|| name), || Ok(argument)).unwrap()
// } else {
// Boolean::alloc_input(cs.ns(|| name), || Ok(argument)).unwrap()
// };
//
// array_value.push(number);
// }
//
//
// let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
//
// // store array as variable in resolved program
// self.store_variable(parameter_variable.clone(), ResolvedValue::BooleanArray(array_value));
//
// parameter_variable
}
pub(crate) fn get_boolean_constant(bool: bool) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(bool))
}
pub(crate) fn enforce_not(value: ResolvedValue<F>) -> ResolvedValue<F> {
match value {
ResolvedValue::Boolean(boolean) => ResolvedValue::Boolean(boolean.not()),
value => unimplemented!("cannot enforce not on non-boolean value {}", value),
}
}
pub(crate) fn enforce_or(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::Boolean(left_bool), ResolvedValue::Boolean(right_bool)) => {
ResolvedValue::Boolean(Boolean::or(cs, &left_bool, &right_bool).unwrap())
}
(left_value, right_value) => unimplemented!(
"cannot enforce or on non-boolean values {} || {}",
left_value,
right_value
),
}
}
pub(crate) fn enforce_and(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::Boolean(left_bool), ResolvedValue::Boolean(right_bool)) => {
ResolvedValue::Boolean(Boolean::and(cs, &left_bool, &right_bool).unwrap())
}
(left_value, right_value) => unimplemented!(
"cannot enforce and on non-boolean values {} && {}",
left_value,
right_value
),
}
}
pub(crate) fn enforce_boolean_eq(
&mut self,
cs: &mut CS,
left: Boolean,
right: Boolean,
) -> ResolvedValue<F> {
left.enforce_equal(cs.ns(|| format!("enforce bool equal")), &right)
.unwrap();
ResolvedValue::Boolean(Boolean::Constant(true))
}
}

View File

@ -0,0 +1,231 @@
//! Methods to enforce constraints a resolved aleo program.
//!
//! @file constraints.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::ast;
use crate::program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
use crate::program::{Expression, Function, Import, Program, Statement, Type};
use from_pest::FromPest;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use std::fs;
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn enforce_function(
&mut self,
cs: &mut CS,
function: Function<F>,
arguments: Vec<Expression<F>>,
) -> ResolvedValue<F> {
// Make sure we are given the correct number of arguments
if function.parameters.len() != arguments.len() {
unimplemented!(
"function expected {} arguments, got {}",
function.parameters.len(),
arguments.len()
)
}
// Store arguments as variables in resolved program
function
.parameters
.clone()
.iter()
.zip(arguments.clone().into_iter())
.for_each(|(parameter, argument)| {
// Check that argument is correct type
match parameter.ty.clone() {
Type::U32 => {
match self.enforce_expression(cs, function.get_name(), argument) {
ResolvedValue::U32(number) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function.get_name(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::U32(number));
}
argument => {
unimplemented!("expected integer argument got {}", argument)
}
}
}
Type::FieldElement => {
match self.enforce_expression(cs, function.get_name(), argument) {
ResolvedValue::FieldElement(field) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function.get_name(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::FieldElement(field));
}
argument => unimplemented!("expected field argument got {}", argument),
}
}
Type::Boolean => {
match self.enforce_expression(cs, function.get_name(), argument) {
ResolvedValue::Boolean(bool) => {
// Store argument as variable with {function_name}_{parameter name}
let variable_name = new_scope_from_variable(
function.get_name(),
&parameter.variable,
);
self.store(variable_name, ResolvedValue::Boolean(bool));
}
argument => {
unimplemented!("expected boolean argument got {}", argument)
}
}
}
ty => unimplemented!("parameter type {} not matched yet", ty),
}
});
// Evaluate function statements
let mut return_values = ResolvedValue::Return(vec![]);
function
.statements
.clone()
.into_iter()
.for_each(|statement| match statement {
Statement::Definition(variable, expression) => {
self.enforce_definition_statement(
cs,
function.get_name(),
variable,
expression,
);
}
Statement::For(index, start, stop, statements) => {
self.enforce_for_statement(
cs,
function.get_name(),
index,
start,
stop,
statements,
);
}
Statement::Return(expressions) => {
return_values = self.enforce_return_statement(
cs,
function.get_name(),
expressions,
function.returns.to_owned(),
)
}
});
return_values
}
fn enforce_main_function(&mut self, cs: &mut CS, function: Function<F>) -> ResolvedValue<F> {
let mut arguments = vec![];
// Iterate over main function parameters
function
.parameters
.clone()
.into_iter()
.enumerate()
.for_each(|(i, parameter)| {
// append each variable to arguments vector
arguments.push(Expression::Variable(match parameter.ty {
Type::U32 => {
self.integer_from_parameter(cs, function.get_name(), i + 1, parameter)
}
Type::FieldElement => {
self.field_element_from_parameter(cs, function.get_name(), i + 1, parameter)
}
Type::Boolean => {
self.bool_from_parameter(cs, function.get_name(), i + 1, parameter)
}
Type::Array(ref ty, _length) => match *ty.clone() {
Type::U32 => self.integer_array_from_parameter(
cs,
function.get_name(),
i + 1,
parameter,
),
Type::FieldElement => self.field_element_array_from_parameter(
cs,
function.get_name(),
i + 1,
parameter,
),
Type::Boolean => self.boolean_array_from_parameter(
cs,
function.get_name(),
i + 1,
parameter,
),
ty => unimplemented!("parameter type not implemented {}", ty),
},
ty => unimplemented!("parameter type not implemented {}", ty),
}))
});
self.enforce_function(cs, function, arguments)
}
fn enforce_import(&mut self, cs: &mut CS, import: Import) {
// Resolve program file path
let unparsed_file = fs::read_to_string(import.get_file()).expect("cannot read file");
let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse");
// generate ast from file
let syntax_tree = ast::File::from_pest(&mut file).expect("infallible");
// generate aleo program from file
let program = Program::from(syntax_tree);
// recursively evaluate program statements TODO: in file scope
self.resolve_definitions(cs, program);
// store import under designated name
// self.store(name, value)
}
pub fn resolve_definitions(&mut self, cs: &mut CS, program: Program<F>) {
program
.imports
.into_iter()
.for_each(|import| self.enforce_import(cs, import));
program
.structs
.into_iter()
.for_each(|(variable, struct_def)| {
self.store_variable(variable, ResolvedValue::StructDefinition(struct_def));
});
program
.functions
.into_iter()
.for_each(|(function_name, function)| {
self.store(function_name.0, ResolvedValue::Function(function));
});
}
pub fn generate_constraints(cs: &mut CS, program: Program<F>) {
let mut resolved_program = ResolvedProgram::new();
resolved_program.resolve_definitions(cs, program);
let main = resolved_program
.get(&"main".into())
.expect("main function not defined");
let result = match main.clone() {
ResolvedValue::Function(function) => {
resolved_program.enforce_main_function(cs, function)
}
_ => unimplemented!("main must be a function"),
};
println!("\n {}", result);
}
}

View File

@ -0,0 +1,404 @@
//! Methods to enforce constraints on expressions in a resolved aleo program.
//!
//! @file expression.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
use crate::program::{Expression, RangeOrExpression, SpreadOrExpression, StructMember, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use snarkos_models::gadgets::utilities::boolean::Boolean;
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
/// Enforce a variable expression by getting the resolved value
fn enforce_variable(
&mut self,
scope: String,
unresolved_variable: Variable<F>,
) -> ResolvedValue<F> {
// Evaluate the variable name in the current function scope
let variable_name = new_scope_from_variable(scope, &unresolved_variable);
if self.contains_name(&variable_name) {
// Reassigning variable to another variable
self.get_mut(&variable_name).unwrap().clone()
} else if self.contains_variable(&unresolved_variable) {
// Check global scope (function and struct names)
self.get_mut_variable(&unresolved_variable).unwrap().clone()
} else {
unimplemented!("variable declaration {} not found", variable_name)
}
}
/// Enforce numerical operations
fn enforce_add_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_add(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_add(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot add {} + {}", val1, val2),
}
}
fn enforce_sub_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_sub(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_sub(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot subtract {} - {}", val1, val2),
}
}
fn enforce_mul_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_mul(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_mul(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
}
}
fn enforce_div_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_div(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_div(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
}
}
fn enforce_pow_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_pow(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_pow(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot multiply {} * {}", val1, val2),
}
}
/// Enforce Boolean operations
fn enforce_eq_expression(
&mut self,
cs: &mut CS,
left: ResolvedValue<F>,
right: ResolvedValue<F>,
) -> ResolvedValue<F> {
match (left, right) {
(ResolvedValue::Boolean(bool1), ResolvedValue::Boolean(bool2)) => {
self.enforce_boolean_eq(cs, bool1, bool2)
}
(ResolvedValue::U32(num1), ResolvedValue::U32(num2)) => {
Self::enforce_u32_eq(cs, num1, num2)
}
(ResolvedValue::FieldElement(fe1), ResolvedValue::FieldElement(fe2)) => {
self.enforce_field_eq(fe1, fe2)
}
(val1, val2) => unimplemented!("cannot enforce equality between {} == {}", val1, val2),
}
}
/// Enforce array expressions
fn enforce_array_expression(
&mut self,
cs: &mut CS,
scope: String,
array: Vec<Box<SpreadOrExpression<F>>>,
) -> ResolvedValue<F> {
let mut result = vec![];
array.into_iter().for_each(|element| match *element {
SpreadOrExpression::Spread(spread) => match spread {
Expression::Variable(variable) => {
let array_name = new_scope_from_variable(scope.clone(), &variable);
match self.get(&array_name) {
Some(value) => match value {
ResolvedValue::Array(array) => result.extend(array.clone()),
value => {
unimplemented!("spreads only implemented for arrays, got {}", value)
}
},
None => unimplemented!(
"cannot copy elements from array that does not exist {}",
variable.name
),
}
}
value => unimplemented!("spreads only implemented for arrays, got {}", value),
},
SpreadOrExpression::Expression(expression) => {
result.push(self.enforce_expression(cs, scope.clone(), expression));
}
});
ResolvedValue::Array(result)
}
pub(crate) fn enforce_index(
&mut self,
cs: &mut CS,
scope: String,
index: Expression<F>,
) -> usize {
match self.enforce_expression(cs, scope.clone(), index) {
ResolvedValue::U32(number) => number.value.unwrap() as usize,
value => unimplemented!("From index must resolve to an integer, got {}", value),
}
}
fn enforce_array_access_expression(
&mut self,
cs: &mut CS,
scope: String,
array: Box<Expression<F>>,
index: RangeOrExpression<F>,
) -> ResolvedValue<F> {
match self.enforce_expression(cs, scope.clone(), *array) {
ResolvedValue::Array(array) => {
match index {
RangeOrExpression::Range(from, to) => {
let from_resolved = match from {
Some(from_index) => from_index.to_usize(),
None => 0usize, // Array slice starts at index 0
};
let to_resolved = match to {
Some(to_index) => to_index.to_usize(),
None => array.len(), // Array slice ends at array length
};
ResolvedValue::Array(array[from_resolved..to_resolved].to_owned())
}
RangeOrExpression::Expression(index) => {
let index_resolved = self.enforce_index(cs, scope.clone(), index);
array[index_resolved].to_owned()
}
}
}
value => unimplemented!("Cannot access element of untyped array {}", value),
}
}
fn enforce_struct_expression(
&mut self,
cs: &mut CS,
scope: String,
variable: Variable<F>,
members: Vec<StructMember<F>>,
) -> ResolvedValue<F> {
if let Some(resolved_value) = self.get_mut_variable(&variable) {
match resolved_value {
ResolvedValue::StructDefinition(struct_definition) => {
struct_definition
.fields
.clone()
.iter()
.zip(members.clone().into_iter())
.for_each(|(field, member)| {
if field.variable != member.variable {
unimplemented!("struct field variables do not match")
}
// Resolve and possibly enforce struct fields
// do we need to store the results here?
let _result =
self.enforce_expression(cs, scope.clone(), member.expression);
});
ResolvedValue::StructExpression(variable, members)
}
_ => unimplemented!("Inline struct type is not defined as a struct"),
}
} else {
unimplemented!("Struct must be declared before it is used in an inline expression")
}
}
fn enforce_struct_access_expression(
&mut self,
cs: &mut CS,
scope: String,
struct_variable: Box<Expression<F>>,
struct_member: Variable<F>,
) -> ResolvedValue<F> {
match self.enforce_expression(cs, scope.clone(), *struct_variable) {
ResolvedValue::StructExpression(_name, members) => {
let matched_member = members
.into_iter()
.find(|member| member.variable == struct_member);
match matched_member {
Some(member) => self.enforce_expression(cs, scope.clone(), member.expression),
None => unimplemented!("Cannot access struct member {}", struct_member.name),
}
}
value => unimplemented!("Cannot access element of untyped struct {}", value),
}
}
fn enforce_function_access_expression(
&mut self,
cs: &mut CS,
scope: String,
function: Box<Expression<F>>,
arguments: Vec<Expression<F>>,
) -> ResolvedValue<F> {
match self.enforce_expression(cs, scope, *function) {
ResolvedValue::Function(function) => self.enforce_function(cs, function, arguments),
value => unimplemented!("Cannot call unknown function {}", value),
}
}
pub(crate) fn enforce_expression(
&mut self,
cs: &mut CS,
scope: String,
expression: Expression<F>,
) -> ResolvedValue<F> {
match expression {
// Variables
Expression::Variable(unresolved_variable) => {
self.enforce_variable(scope, unresolved_variable)
}
// Values
Expression::Integer(integer) => Self::get_integer_constant(integer),
Expression::FieldElement(fe) => ResolvedValue::FieldElement(fe),
Expression::Boolean(bool) => Self::get_boolean_constant(bool),
// Binary operations
Expression::Add(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_add_expression(cs, resolved_left, resolved_right)
}
Expression::Sub(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_sub_expression(cs, resolved_left, resolved_right)
}
Expression::Mul(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_mul_expression(cs, resolved_left, resolved_right)
}
Expression::Div(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_div_expression(cs, resolved_left, resolved_right)
}
Expression::Pow(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_pow_expression(cs, resolved_left, resolved_right)
}
// Boolean operations
Expression::Not(expression) => {
Self::enforce_not(self.enforce_expression(cs, scope, *expression))
}
Expression::Or(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_or(cs, resolved_left, resolved_right)
}
Expression::And(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_and(cs, resolved_left, resolved_right)
}
Expression::Eq(left, right) => {
let resolved_left = self.enforce_expression(cs, scope.clone(), *left);
let resolved_right = self.enforce_expression(cs, scope.clone(), *right);
self.enforce_eq_expression(cs, resolved_left, resolved_right)
}
Expression::Geq(left, right) => {
unimplemented!("expression {} >= {} unimplemented", left, right)
}
Expression::Gt(left, right) => {
unimplemented!("expression {} > {} unimplemented", left, right)
}
Expression::Leq(left, right) => {
unimplemented!("expression {} <= {} unimplemented", left, right)
}
Expression::Lt(left, right) => {
unimplemented!("expression {} < {} unimplemented", left, right)
}
// Conditionals
Expression::IfElse(first, second, third) => {
let resolved_first = match self.enforce_expression(cs, scope.clone(), *first) {
ResolvedValue::Boolean(resolved) => resolved,
_ => unimplemented!("if else conditional must resolve to boolean"),
};
if resolved_first.eq(&Boolean::Constant(true)) {
self.enforce_expression(cs, scope, *second)
} else {
self.enforce_expression(cs, scope, *third)
}
}
// Arrays
Expression::Array(array) => self.enforce_array_expression(cs, scope, array),
Expression::ArrayAccess(array, index) => {
self.enforce_array_access_expression(cs, scope, array, *index)
}
// Structs
Expression::Struct(struct_name, members) => {
self.enforce_struct_expression(cs, scope, struct_name, members)
}
Expression::StructMemberAccess(struct_variable, struct_member) => {
self.enforce_struct_access_expression(cs, scope, struct_variable, struct_member)
}
// Functions
Expression::FunctionCall(function, arguments) => {
self.enforce_function_access_expression(cs, scope, function, arguments)
} // _ => unimplemented!(),
}
}
}

View File

@ -0,0 +1,118 @@
//! Methods to enforce constraints on field elements in a resolved aleo program.
//!
//! @file field_element.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::{ResolvedProgram, ResolvedValue};
use crate::program::{new_variable_from_variable, Parameter, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean};
// use std::ops::{Add, Div, Mul, Neg, Sub};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn field_element_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument: F = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<F>()
.unwrap_or_default();
// Check visibility of parameter
let name = parameter.variable.name.clone();
if parameter.private {
cs.alloc(|| name, || Ok(argument.clone())).unwrap();
} else {
cs.alloc_input(|| name, || Ok(argument.clone())).unwrap();
}
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// store each argument as variable in resolved program
self.store_variable(
parameter_variable.clone(),
ResolvedValue::FieldElement(argument),
);
parameter_variable
}
pub(crate) fn field_element_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
unimplemented!("Cannot enforce field element array as parameter")
// // Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<F>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// if parameter.private {
// cs.alloc(|| name, || Ok(argument.clone())).unwrap();
// } else {
// cs.alloc_input(|| name, || Ok(argument.clone())).unwrap();
// };
// }
//
//
// let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
//
// // store array as variable in resolved program
// self.store_variable(parameter_variable.clone(), ResolvedValue::FieldElementArray(argument_array));
//
// parameter_variable
}
pub(crate) fn enforce_field_eq(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::Boolean(Boolean::Constant(fe1.eq(&fe2)))
}
pub(crate) fn enforce_field_add(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.add(&fe2))
}
pub(crate) fn enforce_field_sub(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.sub(&fe2))
}
pub(crate) fn enforce_field_mul(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.mul(&fe2))
}
pub(crate) fn enforce_field_div(&mut self, fe1: F, fe2: F) -> ResolvedValue<F> {
ResolvedValue::FieldElement(fe1.div(&fe2))
}
pub(crate) fn enforce_field_pow(&mut self, _fe1: F, _fe2: F) -> ResolvedValue<F> {
unimplemented!("field element exponentiation not supported")
// ResolvedValue::FieldElement(fe1.pow(&fe2))
}
}

View File

@ -0,0 +1,166 @@
//! Methods to enforce constraints on integers in a resolved aleo program.
//!
//! @file integer.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::{ResolvedProgram, ResolvedValue};
use crate::program::{new_variable_from_variable, Integer, Parameter, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, eq::ConditionalEqGadget, uint32::UInt32},
};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn integer_from_parameter(
&mut self,
cs: &mut CS,
scope: String,
index: usize,
parameter: Parameter<F>,
) -> Variable<F> {
// Get command line argument for each parameter in program
let argument = std::env::args()
.nth(index)
.expect(&format!(
"expected command line argument at index {}",
index
))
.parse::<u32>()
.expect(&format!(
"expected main function parameter {} at index {}",
parameter, index
));
// Check visibility of parameter
let name = parameter.variable.name.clone();
let number = if parameter.private {
UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
} else {
UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
};
let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
// store each argument as variable in resolved program
self.store_variable(parameter_variable.clone(), ResolvedValue::U32(number));
parameter_variable
}
pub(crate) fn integer_array_from_parameter(
&mut self,
_cs: &mut CS,
_scope: String,
_index: usize,
_parameter: Parameter<F>,
) -> Variable<F> {
unimplemented!("Cannot enforce integer array as parameter")
// Get command line argument for each parameter in program
// let argument_array = std::env::args()
// .nth(index)
// .expect(&format!(
// "expected command line argument at index {}",
// index
// ))
// .parse::<Vec<u32>>()
// .expect(&format!(
// "expected main function parameter {} at index {}",
// parameter, index
// ));
//
// // Check visibility of parameter
// let mut array_value = vec![];
// let name = parameter.variable.name.clone();
// for argument in argument_array {
// let number = if parameter.private {
// UInt32::alloc(cs.ns(|| name), Some(argument)).unwrap()
// } else {
// UInt32::alloc_input(cs.ns(|| name), Some(argument)).unwrap()
// };
//
// array_value.push(number);
// }
//
//
// let parameter_variable = new_variable_from_variable(scope, &parameter.variable);
//
// // store array as variable in resolved program
// self.store_variable(parameter_variable.clone(), ResolvedValue::U32Array(array_value));
//
// parameter_variable
}
pub(crate) fn get_integer_constant(integer: Integer) -> ResolvedValue<F> {
match integer {
Integer::U32(u32_value) => ResolvedValue::U32(UInt32::constant(u32_value)),
}
}
pub(crate) fn enforce_u32_eq(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
left.conditional_enforce_equal(
cs.ns(|| format!("enforce field equal")),
&right,
&Boolean::Constant(true),
)
.unwrap();
ResolvedValue::Boolean(Boolean::Constant(true))
}
pub(crate) fn enforce_u32_add(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
UInt32::addmany(
cs.ns(|| format!("enforce {} + {}", left.value.unwrap(), right.value.unwrap())),
&[left, right],
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_sub(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.sub(
cs.ns(|| format!("enforce {} - {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_mul(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.mul(
cs.ns(|| format!("enforce {} * {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_div(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.div(
cs.ns(|| format!("enforce {} / {}", left.value.unwrap(), right.value.unwrap())),
&right,
)
.unwrap(),
)
}
pub(crate) fn enforce_u32_pow(cs: &mut CS, left: UInt32, right: UInt32) -> ResolvedValue<F> {
ResolvedValue::U32(
left.pow(
cs.ns(|| {
format!(
"enforce {} ** {}",
left.value.unwrap(),
right.value.unwrap()
)
}),
&right,
)
.unwrap(),
)
}
}

View File

@ -0,0 +1,29 @@
//! Module containing methods to enforce constraints in an aleo program
//!
//! @file constraints/mod.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
pub mod boolean;
pub use boolean::*;
pub mod constraints;
pub use constraints::*;
pub mod expression;
pub use expression::*;
pub mod integer;
pub use integer::*;
pub mod field_element;
pub use field_element::*;
pub mod resolved_program;
pub use resolved_program::*;
pub mod resolved_value;
pub use resolved_value::*;
pub mod statement;
pub use statement::*;

View File

@ -0,0 +1,79 @@
//! An in memory store to keep track of defined names when constraining an aleo program.
//!
//! @file resolved_program.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::ResolvedValue;
use crate::program::types::Variable;
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use std::collections::HashMap;
use std::marker::PhantomData;
pub struct ResolvedProgram<F: Field + PrimeField, CS: ConstraintSystem<F>> {
pub resolved_names: HashMap<String, ResolvedValue<F>>,
pub _cs: PhantomData<CS>,
}
pub fn new_scope(outer: String, inner: String) -> String {
format!("{}_{}", outer, inner)
}
pub fn new_scope_from_variable<F: Field + PrimeField>(
outer: String,
inner: &Variable<F>,
) -> String {
new_scope(outer, inner.name.clone())
}
pub fn new_variable_from_variable<F: Field + PrimeField>(
outer: String,
inner: &Variable<F>,
) -> Variable<F> {
Variable {
name: new_scope_from_variable(outer, inner),
_field: PhantomData::<F>,
}
}
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub fn new() -> Self {
Self {
resolved_names: HashMap::new(),
_cs: PhantomData::<CS>,
}
}
pub(crate) fn store(&mut self, name: String, value: ResolvedValue<F>) {
self.resolved_names.insert(name, value);
}
pub(crate) fn store_variable(&mut self, variable: Variable<F>, value: ResolvedValue<F>) {
self.store(variable.name, value);
}
pub(crate) fn contains_name(&self, name: &String) -> bool {
self.resolved_names.contains_key(name)
}
pub(crate) fn contains_variable(&self, variable: &Variable<F>) -> bool {
self.contains_name(&variable.name)
}
pub(crate) fn get(&self, name: &String) -> Option<&ResolvedValue<F>> {
self.resolved_names.get(name)
}
pub(crate) fn get_mut(&mut self, name: &String) -> Option<&mut ResolvedValue<F>> {
self.resolved_names.get_mut(name)
}
pub(crate) fn get_mut_variable(
&mut self,
variable: &Variable<F>,
) -> Option<&mut ResolvedValue<F>> {
self.get_mut(&variable.name)
}
}

View File

@ -0,0 +1,92 @@
//! The in memory stored value for a defined name in a resolved aleo program.
//!
//! @file resolved_value.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::types::{Function, Struct, StructMember, Type, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{utilities::boolean::Boolean, utilities::uint32::UInt32};
use std::fmt;
#[derive(Clone)]
pub enum ResolvedValue<F: Field + PrimeField> {
U32(UInt32),
FieldElement(F),
Boolean(Boolean),
Array(Vec<ResolvedValue<F>>),
StructDefinition(Struct<F>),
StructExpression(Variable<F>, Vec<StructMember<F>>),
Function(Function<F>),
Return(Vec<ResolvedValue<F>>), // add Null for function returns
}
impl<F: Field + PrimeField> ResolvedValue<F> {
pub(crate) fn match_type(&self, ty: &Type<F>) -> bool {
match (self, ty) {
(ResolvedValue::U32(ref _a), Type::U32) => true,
(ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true,
(ResolvedValue::Boolean(ref _a), Type::Boolean) => true,
(ResolvedValue::Array(ref arr), Type::Array(ref _ty, ref len)) => arr.len() == *len, // todo: add array types
(
ResolvedValue::StructExpression(ref actual_name, ref _members),
Type::Struct(ref expected_name),
) => actual_name == expected_name,
(ResolvedValue::Return(ref values), ty) => {
let mut res = true;
for value in values {
res &= value.match_type(ty)
}
res
}
(_, _) => false,
}
}
}
impl<F: Field + PrimeField> fmt::Display for ResolvedValue<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ResolvedValue::U32(ref value) => write!(f, "{}", value.value.unwrap()),
ResolvedValue::FieldElement(ref value) => write!(f, "{}", value),
ResolvedValue::Boolean(ref value) => write!(f, "{}", value.get_value().unwrap()),
ResolvedValue::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ResolvedValue::StructExpression(ref variable, ref members) => {
write!(f, "{} {{", variable)?;
for (i, member) in members.iter().enumerate() {
write!(f, "{}: {}", member.variable, member.expression)?;
if i < members.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
ResolvedValue::Return(ref values) => {
write!(f, "Return values : [")?;
for (i, value) in values.iter().enumerate() {
write!(f, "{}", value)?;
if i < values.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
ResolvedValue::StructDefinition(ref _definition) => {
unimplemented!("cannot return struct definition in program")
}
ResolvedValue::Function(ref _function) => {
unimplemented!("cannot return function definition in program")
} // _ => unimplemented!("display not impl for value"),
}
}
}

View File

@ -0,0 +1,198 @@
//! Methods to enforce constraints on statements in a resolved aleo program.
//!
//! @file statement.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
use crate::program::{Assignee, Expression, Integer, RangeOrExpression, Statement, Type, Variable};
use snarkos_models::curves::{Field, PrimeField};
use snarkos_models::gadgets::{r1cs::ConstraintSystem, utilities::uint32::UInt32};
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
fn resolve_assignee(&mut self, scope: String, assignee: Assignee<F>) -> String {
match assignee {
Assignee::Variable(name) => new_scope_from_variable(scope, &name),
Assignee::Array(array, _index) => self.resolve_assignee(scope, *array),
Assignee::StructMember(struct_variable, _member) => {
self.resolve_assignee(scope, *struct_variable)
}
}
}
pub(crate) fn enforce_definition_statement(
&mut self,
cs: &mut CS,
scope: String,
assignee: Assignee<F>,
expression: Expression<F>,
) {
// Create or modify the lhs variable in the current function scope
match assignee {
Assignee::Variable(name) => {
// Store the variable in the current scope
let definition_name = new_scope_from_variable(scope.clone(), &name);
// Evaluate the rhs expression in the current function scope
let result = self.enforce_expression(cs, scope, expression);
self.store(definition_name, result);
}
Assignee::Array(array, index_expression) => {
// Evaluate the rhs expression in the current function scope
let result = &mut self.enforce_expression(cs, scope.clone(), expression);
// Check that array exists
let expected_array_name = self.resolve_assignee(scope.clone(), *array);
// Resolve index so we know if we are assigning to a single value or a range of values
match index_expression {
RangeOrExpression::Expression(index) => {
let index = self.enforce_index(cs, scope.clone(), index);
// Modify the single value of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match value {
ResolvedValue::Array(old) => {
old[index] = result.to_owned();
}
_ => {
unimplemented!("Cannot assign single index to array of values ")
}
},
None => unimplemented!(
"tried to assign to unknown array {}",
expected_array_name
),
}
}
RangeOrExpression::Range(from, to) => {
let from_index = match from {
Some(integer) => integer.to_usize(),
None => 0usize,
};
let to_index_option = match to {
Some(integer) => Some(integer.to_usize()),
None => None,
};
// Modify the range of values of the array in place
match self.get_mut(&expected_array_name) {
Some(value) => match (value, result) {
(ResolvedValue::Array(old), ResolvedValue::Array(new)) => {
let to_index = to_index_option.unwrap_or(old.len());
old.splice(from_index..to_index, new.iter().cloned());
}
_ => unimplemented!(
"Cannot assign a range of array values to single value"
),
},
None => unimplemented!(
"tried to assign to unknown array {}",
expected_array_name
),
}
}
}
}
Assignee::StructMember(struct_variable, struct_member) => {
// Check that struct exists
let expected_struct_name = self.resolve_assignee(scope.clone(), *struct_variable);
match self.get_mut(&expected_struct_name) {
Some(value) => match value {
ResolvedValue::StructExpression(_variable, members) => {
// Modify the struct member in place
let matched_member = members
.into_iter()
.find(|member| member.variable == struct_member);
match matched_member {
Some(mut member) => member.expression = expression,
None => unimplemented!(
"struct member {} does not exist in {}",
struct_member,
expected_struct_name
),
}
}
_ => unimplemented!(
"tried to assign to unknown struct {}",
expected_struct_name
),
},
None => {
unimplemented!("tried to assign to unknown struct {}", expected_struct_name)
}
}
}
};
}
pub(crate) fn enforce_return_statement(
&mut self,
cs: &mut CS,
scope: String,
statements: Vec<Expression<F>>,
return_types: Vec<Type<F>>,
) -> ResolvedValue<F> {
ResolvedValue::Return(
statements
.into_iter()
.zip(return_types.into_iter())
.map(|(expression, ty)| {
let result = self.enforce_expression(cs, scope.clone(), expression);
if !result.match_type(&ty) {
unimplemented!("expected return type {}, got {}", ty, result)
} else {
result
}
})
.collect::<Vec<ResolvedValue<F>>>(),
)
}
fn enforce_statement(
&mut self,
cs: &mut CS,
scope: String,
statement: Statement<F>,
return_types: Vec<Type<F>>,
) {
match statement {
Statement::Definition(variable, expression) => {
self.enforce_definition_statement(cs, scope, variable, expression);
}
Statement::For(index, start, stop, statements) => {
self.enforce_for_statement(cs, scope, index, start, stop, statements);
}
Statement::Return(statements) => {
// TODO: add support for early termination
let _res = self.enforce_return_statement(cs, scope, statements, return_types);
}
};
}
pub(crate) fn enforce_for_statement(
&mut self,
cs: &mut CS,
scope: String,
index: Variable<F>,
start: Integer,
stop: Integer,
statements: Vec<Statement<F>>,
) {
for i in start.to_usize()..stop.to_usize() {
// Store index in current function scope.
// For loop scope is not implemented.
let index_name = new_scope_from_variable(scope.clone(), &index);
self.store(index_name, ResolvedValue::U32(UInt32::constant(i as u32)));
// Evaluate statements (for loop statements should not have a return type)
statements
.clone()
.into_iter()
.for_each(|statement| self.enforce_statement(cs, scope.clone(), statement, vec![]));
}
}
}

75
src/program/imports.rs Normal file
View File

@ -0,0 +1,75 @@
use std::fmt;
use std::path::Path;
type ImportPath<'ast> = &'ast Path;
pub(crate) type PathString<'ast> = &'ast str;
#[derive(Clone)]
pub struct Import<'ast> {
source: ImportPath<'ast>,
symbol: Option<PathString<'ast>>,
alias: Option<PathString<'ast>>,
}
impl<'ast> Import<'ast> {
pub fn new(symbol: Option<PathString<'ast>>, source: ImportPath<'ast>) -> Import<'ast> {
Import {
source,
symbol,
alias: None,
}
}
pub fn new_with_alias(
symbol: Option<PathString<'ast>>,
source: ImportPath<'ast>,
alias: PathString<'ast>,
) -> Import<'ast> {
Import {
source,
symbol,
alias: Some(alias),
}
}
pub fn alias(mut self, alias: Option<PathString<'ast>>) -> Self {
self.alias = alias;
self
}
pub fn get_alias(&self) -> &Option<PathString<'ast>> {
&self.alias
}
pub fn get_source(&self) -> &Path {
&self.source
}
pub fn get_file(&self) -> String {
let path = self.get_source().to_str().unwrap();
format!("{}.leo", path)
}
}
impl<'ast> fmt::Display for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(f, "import {} as {}", self.source.display(), alias),
None => write!(f, "import {}", self.source.display()),
}
}
}
impl<'ast> fmt::Debug for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(
f,
"import(source: {}, alias: {})",
self.source.display(),
alias
),
None => write!(f, "import( source: {})", self.source.display()),
}
}
}

20
src/program/mod.rs Normal file
View File

@ -0,0 +1,20 @@
//! Module containing structs and types that make up an aleo program.
//!
//! @file aleo_program/mod.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
pub mod constraints;
pub use self::constraints::*;
pub mod imports;
pub use self::imports::*;
pub mod types;
pub use self::types::*;
pub mod types_display;
pub use self::types_display::*;
pub mod types_from;
pub use self::types_from::*;

185
src/program/types.rs Normal file
View File

@ -0,0 +1,185 @@
//! A typed program in aleo consists of import, struct, and function definitions.
//! Each defined type consists of typed statements and expressions.
//!
//! @file types.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::Import;
use snarkos_models::curves::{Field, PrimeField};
use std::collections::HashMap;
use std::marker::PhantomData;
/// A variable in a constraint system.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Variable<F: Field + PrimeField> {
pub name: String,
pub(crate) _field: PhantomData<F>,
}
/// An integer type enum wrapping the integer value
#[derive(Debug, Clone)]
pub enum Integer {
// U8(u8),
U32(u32),
// U64(u64),
}
impl Integer {
pub fn to_usize(&self) -> usize {
match *self {
// U8(u8)
Integer::U32(num) => num as usize,
// U64(u64)
}
}
}
/// Range or expression enum
#[derive(Debug, Clone)]
pub enum RangeOrExpression<F: Field + PrimeField> {
Range(Option<Integer>, Option<Integer>),
Expression(Expression<F>),
}
/// Spread or expression
#[derive(Debug, Clone)]
pub enum SpreadOrExpression<F: Field + PrimeField> {
Spread(Expression<F>),
Expression(Expression<F>),
}
/// Expression that evaluates to a value
#[derive(Debug, Clone)]
pub enum Expression<F: Field + PrimeField> {
// Variable
Variable(Variable<F>),
// Values
Integer(Integer),
FieldElement(F),
Boolean(bool),
// Number operations
Add(Box<Expression<F>>, Box<Expression<F>>),
Sub(Box<Expression<F>>, Box<Expression<F>>),
Mul(Box<Expression<F>>, Box<Expression<F>>),
Div(Box<Expression<F>>, Box<Expression<F>>),
Pow(Box<Expression<F>>, Box<Expression<F>>),
// Boolean operations
Not(Box<Expression<F>>),
Or(Box<Expression<F>>, Box<Expression<F>>),
And(Box<Expression<F>>, Box<Expression<F>>),
Eq(Box<Expression<F>>, Box<Expression<F>>),
Geq(Box<Expression<F>>, Box<Expression<F>>),
Gt(Box<Expression<F>>, Box<Expression<F>>),
Leq(Box<Expression<F>>, Box<Expression<F>>),
Lt(Box<Expression<F>>, Box<Expression<F>>),
// Conditionals
IfElse(Box<Expression<F>>, Box<Expression<F>>, Box<Expression<F>>),
// Arrays
Array(Vec<Box<SpreadOrExpression<F>>>),
ArrayAccess(Box<Expression<F>>, Box<RangeOrExpression<F>>),
// Structs
Struct(Variable<F>, Vec<StructMember<F>>),
StructMemberAccess(Box<Expression<F>>, Variable<F>), // (struct name, struct member name)
// Functions
FunctionCall(Box<Expression<F>>, Vec<Expression<F>>),
}
/// Definition assignee: v, arr[0..2], Point p.x
#[derive(Debug, Clone)]
pub enum Assignee<F: Field + PrimeField> {
Variable(Variable<F>),
Array(Box<Assignee<F>>, RangeOrExpression<F>),
StructMember(Box<Assignee<F>>, Variable<F>),
}
/// Program statement that defines some action (or expression) to be carried out.
#[derive(Clone)]
pub enum Statement<F: Field + PrimeField> {
// Declaration(Variable),
Definition(Assignee<F>, Expression<F>),
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
Return(Vec<Expression<F>>),
}
/// Explicit type used for defining struct members and function parameters
#[derive(Clone, Debug, PartialEq)]
pub enum Type<F: Field + PrimeField> {
U32,
FieldElement,
Boolean,
Array(Box<Type<F>>, usize),
Struct(Variable<F>),
}
#[derive(Clone, Debug)]
pub struct StructMember<F: Field + PrimeField> {
pub variable: Variable<F>,
pub expression: Expression<F>,
}
#[derive(Clone)]
pub struct StructField<F: Field + PrimeField> {
pub variable: Variable<F>,
pub ty: Type<F>,
}
#[derive(Clone)]
pub struct Struct<F: Field + PrimeField> {
pub variable: Variable<F>,
pub fields: Vec<StructField<F>>,
}
/// Function parameters
#[derive(Clone)]
pub struct Parameter<F: Field + PrimeField> {
pub private: bool,
pub ty: Type<F>,
pub variable: Variable<F>,
}
/// The given name for a defined function in the program.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FunctionName(pub String);
#[derive(Clone)]
pub struct Function<F: Field + PrimeField> {
pub function_name: FunctionName,
pub parameters: Vec<Parameter<F>>,
pub returns: Vec<Type<F>>,
pub statements: Vec<Statement<F>>,
}
impl<F: Field + PrimeField> Function<F> {
pub fn get_name(&self) -> String {
self.function_name.0.clone()
}
}
/// A simple program with statement expressions, program arguments and program returns.
#[derive(Debug, Clone)]
pub struct Program<'ast, F: Field + PrimeField> {
pub name: Variable<F>,
pub imports: Vec<Import<'ast>>,
pub structs: HashMap<Variable<F>, Struct<F>>,
pub functions: HashMap<FunctionName, Function<F>>,
}
impl<'ast, F: Field + PrimeField> Program<'ast, F> {
pub fn name(mut self, name: String) -> Self {
self.name = Variable {
name,
_field: PhantomData::<F>,
};
self
}
}

View File

@ -0,0 +1,284 @@
//! Format display functions for zokrates_program types.
//!
//! @file zokrates_program.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::program::{
Assignee, Expression, Function, FunctionName, Integer, Parameter, RangeOrExpression,
SpreadOrExpression, Statement, Struct, StructField, Type, Variable,
};
use snarkos_models::curves::{Field, PrimeField};
use std::fmt;
impl<F: Field + PrimeField> fmt::Display for Variable<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl<F: Field + PrimeField> fmt::Debug for Variable<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Integer::U32(ref num) => write!(f, "{}", num),
}
}
}
impl<'ast, F: Field + PrimeField> fmt::Display for RangeOrExpression<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RangeOrExpression::Range(ref from, ref to) => write!(
f,
"{}..{}",
from.as_ref()
.map(|e| format!("{}", e))
.unwrap_or("".to_string()),
to.as_ref()
.map(|e| format!("{}", e))
.unwrap_or("".to_string())
),
RangeOrExpression::Expression(ref e) => write!(f, "{}", e),
}
}
}
impl<F: Field + PrimeField> fmt::Display for SpreadOrExpression<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SpreadOrExpression::Spread(ref spread) => write!(f, "...{}", spread),
SpreadOrExpression::Expression(ref expression) => write!(f, "{}", expression),
}
}
}
impl<'ast, F: Field + PrimeField> fmt::Display for Expression<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
// Variables
Expression::Variable(ref variable) => write!(f, "{}", variable),
// Values
Expression::Integer(ref integer) => write!(f, "{}", integer),
Expression::FieldElement(ref fe) => write!(f, "{}", fe),
Expression::Boolean(ref bool) => write!(f, "{}", bool),
// Number operations
Expression::Add(ref left, ref right) => write!(f, "{} + {}", left, right),
Expression::Sub(ref left, ref right) => write!(f, "{} - {}", left, right),
Expression::Mul(ref left, ref right) => write!(f, "{} * {}", left, right),
Expression::Div(ref left, ref right) => write!(f, "{} / {}", left, right),
Expression::Pow(ref left, ref right) => write!(f, "{} ** {}", left, right),
// Boolean operations
Expression::Not(ref expression) => write!(f, "!{}", expression),
Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs),
Expression::And(ref lhs, ref rhs) => write!(f, "{} && {}", lhs, rhs),
Expression::Eq(ref lhs, ref rhs) => write!(f, "{} == {}", lhs, rhs),
Expression::Geq(ref lhs, ref rhs) => write!(f, "{} >= {}", lhs, rhs),
Expression::Gt(ref lhs, ref rhs) => write!(f, "{} > {}", lhs, rhs),
Expression::Leq(ref lhs, ref rhs) => write!(f, "{} <= {}", lhs, rhs),
Expression::Lt(ref lhs, ref rhs) => write!(f, "{} < {}", lhs, rhs),
// Conditionals
Expression::IfElse(ref first, ref second, ref third) => {
write!(f, "if {} then {} else {} fi", first, second, third)
}
Expression::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
Expression::ArrayAccess(ref array, ref index) => write!(f, "{}[{}]", array, index),
Expression::Struct(ref var, ref members) => {
write!(f, "{} {{", var)?;
for (i, member) in members.iter().enumerate() {
write!(f, "{}: {}", member.variable, member.expression)?;
if i < members.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "}}")
}
Expression::StructMemberAccess(ref struct_variable, ref member) => {
write!(f, "{}.{}", struct_variable, member)
}
Expression::FunctionCall(ref function, ref arguments) => {
write!(f, "{}(", function,)?;
for (i, param) in arguments.iter().enumerate() {
write!(f, "{}", param)?;
if i < arguments.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
} // _ => unimplemented!("can't display expression yet"),
}
}
}
impl<F: Field + PrimeField> fmt::Display for Assignee<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Assignee::Variable(ref variable) => write!(f, "{}", variable),
Assignee::Array(ref array, ref index) => write!(f, "{}[{}]", array, index),
Assignee::StructMember(ref struct_variable, ref member) => {
write!(f, "{}.{}", struct_variable, member)
}
}
}
}
impl<F: Field + PrimeField> fmt::Display for Statement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Definition(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
Statement::For(ref var, ref start, ref stop, ref list) => {
write!(f, "for {} in {}..{} do\n", var, start, stop)?;
for l in list {
write!(f, "\t\t{}\n", l)?;
}
write!(f, "\tendfor")
}
Statement::Return(ref statements) => {
statements.iter().for_each(|statement| {
write!(f, "return {}", statement).unwrap();
});
write!(f, "\n")
}
}
}
}
impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Statement::Definition(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
Statement::For(ref var, ref start, ref stop, ref list) => {
write!(f, "for {:?} in {:?}..{:?} do\n", var, start, stop)?;
for l in list {
write!(f, "\t\t{:?}\n", l)?;
}
write!(f, "\tendfor")
}
Statement::Return(ref statements) => {
statements.iter().for_each(|statement| {
write!(f, "return {}", statement).unwrap();
});
write!(f, "\n")
}
}
}
}
impl<F: Field + PrimeField> fmt::Display for Type<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::U32 => write!(f, "u32"),
Type::FieldElement => write!(f, "fe"),
Type::Boolean => write!(f, "bool"),
Type::Struct(ref variable) => write!(f, "{}", variable),
Type::Array(ref array, ref count) => write!(f, "[{}; {}]", array, count),
}
}
}
impl<F: Field + PrimeField> fmt::Display for StructField<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} : {}", self.ty, self.variable)
}
}
impl<F: Field + PrimeField> fmt::Debug for Struct<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "struct {} {{ \n", self.variable)?;
for field in self.fields.iter() {
write!(f, " {}\n", field)?;
}
write!(f, "}}")
}
}
impl<F: Field + PrimeField> fmt::Display for Parameter<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let visibility = if self.private { "private" } else { "public" };
write!(f, "{}: {} {}", self.variable, visibility, self.ty,)
}
}
impl<F: Field + PrimeField> fmt::Debug for Parameter<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Parameter(variable: {:?})", self.ty)
}
}
impl fmt::Debug for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<F: Field + PrimeField> fmt::Display for Function<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({}): ->({})\n{}",
self.parameters
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(","),
self.returns
.iter()
.map(|r| format!("{}", r))
.collect::<Vec<_>>()
.join(","),
self.statements
.iter()
.map(|x| format!("\t{}", x))
.collect::<Vec<_>>()
.join("\n"),
)
}
}
impl<F: Field + PrimeField> fmt::Debug for Function<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({}): -> ({})\n{}",
self.parameters
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(","),
self.returns
.iter()
.map(|r| format!("{}", r))
.collect::<Vec<_>>()
.join(","),
self.statements
.iter()
.map(|x| format!("\t{}", x))
.collect::<Vec<_>>()
.join("\n"),
)
}
}

610
src/program/types_from.rs Normal file
View File

@ -0,0 +1,610 @@
//! Logic to convert from an abstract syntax tree (ast) representation to a typed aleo program.
//!
//! @file types_from.rs
//! @author Collin Chin <collin@aleo.org>
//! @date 2020
use crate::ast;
use crate::program::{types, Import, PathString};
use snarkos_models::curves::{Field, PrimeField};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::path::Path;
/// pest ast -> types::Variable
impl<'ast, F: Field + PrimeField> From<ast::Variable<'ast>> for types::Variable<F> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Variable {
name: variable.value,
_field: PhantomData::<F>,
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::Variable<'ast>> for types::Expression<F> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Expression::Variable(types::Variable::from(variable))
}
}
/// pest ast - types::Integer
impl<'ast, F: Field + PrimeField> From<ast::U32<'ast>> for types::Expression<F> {
fn from(field: ast::U32<'ast>) -> Self {
types::Expression::Integer(types::Integer::U32(
field
.number
.value
.parse::<u32>()
.expect("unable to parse u32"),
))
}
}
impl<'ast, F: Field + PrimeField> From<ast::RangeOrExpression<'ast>>
for types::RangeOrExpression<F>
{
fn from(range_or_expression: ast::RangeOrExpression<'ast>) -> Self {
match range_or_expression {
ast::RangeOrExpression::Range(range) => {
let from = range
.from
.map(|from| match types::Expression::<F>::from(from.0) {
types::Expression::Integer(number) => number,
expression => {
unimplemented!("Range bounds should be integers, found {}", expression)
}
});
let to = range.to.map(|to| match types::Expression::<F>::from(to.0) {
types::Expression::Integer(number) => number,
expression => {
unimplemented!("Range bounds should be intgers, found {}", expression)
}
});
types::RangeOrExpression::Range(from, to)
}
ast::RangeOrExpression::Expression(expression) => {
types::RangeOrExpression::Expression(types::Expression::from(expression))
}
}
}
}
/// pest ast -> types::FieldExpression
impl<'ast, F: Field + PrimeField> From<ast::Field<'ast>> for types::Expression<F> {
fn from(field: ast::Field<'ast>) -> Self {
types::Expression::FieldElement(F::from_str(&field.number.value).unwrap_or_default())
}
}
/// pest ast -> types::Boolean
impl<'ast, F: Field + PrimeField> From<ast::Boolean<'ast>> for types::Expression<F> {
fn from(boolean: ast::Boolean<'ast>) -> Self {
types::Expression::Boolean(
boolean
.value
.parse::<bool>()
.expect("unable to parse boolean"),
)
}
}
/// pest ast -> types::Expression
impl<'ast, F: Field + PrimeField> From<ast::Value<'ast>> for types::Expression<F> {
fn from(value: ast::Value<'ast>) -> Self {
match value {
ast::Value::U32(num) => types::Expression::from(num),
ast::Value::Field(fe) => types::Expression::from(fe),
ast::Value::Boolean(bool) => types::Expression::from(bool),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::NotExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::NotExpression<'ast>) -> Self {
types::Expression::Not(Box::new(types::Expression::from(*expression.expression)))
}
}
impl<'ast, F: Field + PrimeField> From<ast::SpreadOrExpression<'ast>>
for types::SpreadOrExpression<F>
{
fn from(s_or_e: ast::SpreadOrExpression<'ast>) -> Self {
match s_or_e {
ast::SpreadOrExpression::Spread(spread) => {
types::SpreadOrExpression::Spread(types::Expression::from(spread.expression))
}
ast::SpreadOrExpression::Expression(expression) => {
types::SpreadOrExpression::Expression(types::Expression::from(expression))
}
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::BinaryExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::BinaryExpression<'ast>) -> Self {
match expression.operation {
// Boolean operations
ast::BinaryOperator::Or => types::Expression::Or(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::And => types::Expression::And(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Eq => types::Expression::Eq(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Neq => {
types::Expression::Not(Box::new(types::Expression::from(expression)))
}
ast::BinaryOperator::Geq => types::Expression::Geq(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Gt => types::Expression::Gt(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Leq => types::Expression::Leq(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Lt => types::Expression::Lt(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
// Number operations
ast::BinaryOperator::Add => types::Expression::Add(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Sub => types::Expression::Sub(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Mul => types::Expression::Mul(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Div => types::Expression::Div(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
ast::BinaryOperator::Pow => types::Expression::Pow(
Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)),
),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::TernaryExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::TernaryExpression<'ast>) -> Self {
types::Expression::IfElse(
Box::new(types::Expression::from(*expression.first)),
Box::new(types::Expression::from(*expression.second)),
Box::new(types::Expression::from(*expression.third)),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::PostfixExpression<'ast>) -> Self {
let variable = types::Expression::Variable(types::Variable::from(expression.variable));
// ast::PostFixExpression contains an array of "accesses": `a(34)[42]` is represented as `[a, [Call(34), Select(42)]]`, but Access call expressions
// are recursive, so it is `Select(Call(a, 34), 42)`. We apply this transformation here
// we start with the id, and we fold the array of accesses by wrapping the current value
expression
.accesses
.into_iter()
.fold(variable, |acc, access| match access {
ast::Access::Call(function) => match acc {
types::Expression::Variable(_) => types::Expression::FunctionCall(
Box::new(acc),
function
.expressions
.into_iter()
.map(|expression| types::Expression::from(expression))
.collect(),
),
expression => {
unimplemented!("only function names are callable, found \"{}\"", expression)
}
},
ast::Access::Member(struct_member) => types::Expression::StructMemberAccess(
Box::new(acc),
types::Variable::from(struct_member.variable),
),
ast::Access::Array(array) => types::Expression::ArrayAccess(
Box::new(acc),
Box::new(types::RangeOrExpression::from(array.expression)),
),
})
}
}
impl<'ast, F: Field + PrimeField> From<ast::ArrayInlineExpression<'ast>> for types::Expression<F> {
fn from(array: ast::ArrayInlineExpression<'ast>) -> Self {
types::Expression::Array(
array
.expressions
.into_iter()
.map(|s_or_e| Box::new(types::SpreadOrExpression::from(s_or_e)))
.collect(),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ArrayInitializerExpression<'ast>>
for types::Expression<F>
{
fn from(array: ast::ArrayInitializerExpression<'ast>) -> Self {
let count = types::Expression::<F>::get_count(array.count);
let expression = Box::new(types::SpreadOrExpression::from(*array.expression));
types::Expression::Array(vec![expression; count])
}
}
impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Expression<F> {
fn from(expression: ast::Expression<'ast>) -> Self {
match expression {
ast::Expression::Value(value) => types::Expression::from(value),
ast::Expression::Variable(variable) => types::Expression::from(variable),
ast::Expression::Not(expression) => types::Expression::from(expression),
ast::Expression::Binary(expression) => types::Expression::from(expression),
ast::Expression::Ternary(expression) => types::Expression::from(expression),
ast::Expression::ArrayInline(expression) => types::Expression::from(expression),
ast::Expression::ArrayInitializer(expression) => types::Expression::from(expression),
ast::Expression::StructInline(_expression) => {
unimplemented!("unknown type for inline struct expression")
}
ast::Expression::Postfix(expression) => types::Expression::from(expression),
// _ => unimplemented!(),
}
}
}
/// pest ast -> typed types::Expression
/// For defined types (ex: u32[4]) we manually construct the expression instead of implementing the From trait.
/// This saves us from having to resolve things at a later point in time.
impl<'ast, F: Field + PrimeField> types::Expression<F> {
fn get_count(count: ast::Value<'ast>) -> usize {
match count {
ast::Value::U32(f) => f
.number
.value
.parse::<usize>()
.expect("Unable to read array size"),
size => unimplemented!("Array size should be an integer {}", size),
}
}
fn from_struct(ty: ast::StructType<'ast>, expression: ast::Expression<'ast>) -> Self {
let declaration_struct = ty.variable.value;
match expression {
ast::Expression::StructInline(inline_struct) => {
if inline_struct.variable.value != declaration_struct {
unimplemented!("Declared struct type must match inline struct type")
}
let variable = types::Variable::from(inline_struct.variable);
let members = inline_struct
.members
.into_iter()
.map(|member| types::StructMember::from(member))
.collect::<Vec<types::StructMember<F>>>();
types::Expression::Struct(variable, members)
}
_ => unimplemented!("Struct declaration must be followed by inline struct"),
}
}
fn from_type(ty: ast::Type<'ast>, expression: ast::Expression<'ast>) -> Self {
match ty {
ast::Type::Basic(_ty) => Self::from(expression),
ast::Type::Array(_ty) => Self::from(expression),
ast::Type::Struct(ty) => Self::from_struct(ty, expression),
}
}
}
/// pest ast -> types::Assignee
impl<'ast, F: Field + PrimeField> From<ast::Variable<'ast>> for types::Assignee<F> {
fn from(variable: ast::Variable<'ast>) -> Self {
types::Assignee::Variable(types::Variable::from(variable))
}
}
impl<'ast, F: Field + PrimeField> From<ast::Assignee<'ast>> for types::Assignee<F> {
fn from(assignee: ast::Assignee<'ast>) -> Self {
let variable = types::Assignee::from(assignee.variable);
// we start with the id, and we fold the array of accesses by wrapping the current value
assignee
.accesses
.into_iter()
.fold(variable, |acc, access| match access {
ast::AssigneeAccess::Array(array) => types::Assignee::Array(
Box::new(acc),
types::RangeOrExpression::from(array.expression),
),
ast::AssigneeAccess::Member(struct_member) => types::Assignee::StructMember(
Box::new(acc),
types::Variable::from(struct_member.variable),
),
})
}
}
/// pest ast -> types::Statement
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::AssignStatement<'ast>) -> Self {
types::Statement::Definition(
types::Assignee::from(statement.assignee),
types::Expression::from(statement.expression),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
types::Statement::Definition(
types::Assignee::from(statement.variable),
types::Expression::from_type(statement.ty, statement.expression),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ReturnStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::ReturnStatement<'ast>) -> Self {
types::Statement::Return(
statement
.expressions
.into_iter()
.map(|expression| types::Expression::from(expression))
.collect(),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ForStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::ForStatement<'ast>) -> Self {
let from = match types::Expression::<F>::from(statement.start) {
types::Expression::Integer(number) => number,
expression => unimplemented!("Range bounds should be integers, found {}", expression),
};
let to = match types::Expression::<F>::from(statement.stop) {
types::Expression::Integer(number) => number,
expression => unimplemented!("Range bounds should be integers, found {}", expression),
};
types::Statement::For(
types::Variable::from(statement.index),
from,
to,
statement
.statements
.into_iter()
.map(|statement| types::Statement::from(statement))
.collect(),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::Statement<'ast>> for types::Statement<F> {
fn from(statement: ast::Statement<'ast>) -> Self {
match statement {
ast::Statement::Assign(statement) => types::Statement::from(statement),
ast::Statement::Definition(statement) => types::Statement::from(statement),
ast::Statement::Iteration(statement) => types::Statement::from(statement),
ast::Statement::Return(statement) => types::Statement::from(statement),
}
}
}
/// pest ast -> Explicit types::Type for defining struct members and function params
impl<'ast, F: Field + PrimeField> From<ast::BasicType<'ast>> for types::Type<F> {
fn from(basic_type: ast::BasicType<'ast>) -> Self {
match basic_type {
ast::BasicType::U32(_ty) => types::Type::U32,
ast::BasicType::Field(_ty) => types::Type::FieldElement,
ast::BasicType::Boolean(_ty) => types::Type::Boolean,
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::ArrayType<'ast>> for types::Type<F> {
fn from(array_type: ast::ArrayType<'ast>) -> Self {
let element_type = Box::new(types::Type::from(array_type.ty));
let count = types::Expression::<F>::get_count(array_type.count);
types::Type::Array(element_type, count)
}
}
impl<'ast, F: Field + PrimeField> From<ast::StructType<'ast>> for types::Type<F> {
fn from(struct_type: ast::StructType<'ast>) -> Self {
types::Type::Struct(types::Variable::from(struct_type.variable))
}
}
impl<'ast, F: Field + PrimeField> From<ast::Type<'ast>> for types::Type<F> {
fn from(ty: ast::Type<'ast>) -> Self {
match ty {
ast::Type::Basic(ty) => types::Type::from(ty),
ast::Type::Array(ty) => types::Type::from(ty),
ast::Type::Struct(ty) => types::Type::from(ty),
}
}
}
/// pest ast -> types::Struct
impl<'ast, F: Field + PrimeField> From<ast::InlineStructMember<'ast>> for types::StructMember<F> {
fn from(member: ast::InlineStructMember<'ast>) -> Self {
types::StructMember {
variable: types::Variable::from(member.variable),
expression: types::Expression::from(member.expression),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::StructField<'ast>> for types::StructField<F> {
fn from(struct_field: ast::StructField<'ast>) -> Self {
types::StructField {
variable: types::Variable::from(struct_field.variable),
ty: types::Type::from(struct_field.ty),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::Struct<'ast>> for types::Struct<F> {
fn from(struct_definition: ast::Struct<'ast>) -> Self {
let variable = types::Variable::from(struct_definition.variable);
let fields = struct_definition
.fields
.into_iter()
.map(|struct_field| types::StructField::from(struct_field))
.collect();
types::Struct { variable, fields }
}
}
/// pest ast -> function types::Parameters
impl<'ast, F: Field + PrimeField> From<ast::Parameter<'ast>> for types::Parameter<F> {
fn from(parameter: ast::Parameter<'ast>) -> Self {
let ty = types::Type::from(parameter.ty);
println!("type {}", ty);
let variable = types::Variable::from(parameter.variable);
if parameter.visibility.is_some() {
let private = match parameter.visibility.unwrap() {
ast::Visibility::Private(_) => true,
ast::Visibility::Public(_) => false,
};
types::Parameter {
private,
ty,
variable,
}
} else {
types::Parameter {
private: true,
ty,
variable,
}
}
}
}
/// pest ast -> types::Function
impl<'ast> From<ast::FunctionName<'ast>> for types::FunctionName {
fn from(name: ast::FunctionName<'ast>) -> Self {
types::FunctionName(name.value)
}
}
impl<'ast, F: Field + PrimeField> From<ast::Function<'ast>> for types::Function<F> {
fn from(function_definition: ast::Function<'ast>) -> Self {
let function_name = types::FunctionName::from(function_definition.function_name);
let parameters = function_definition
.parameters
.into_iter()
.map(|parameter| types::Parameter::from(parameter))
.collect();
let returns = function_definition
.returns
.into_iter()
.map(|return_type| types::Type::from(return_type))
.collect();
let statements = function_definition
.statements
.into_iter()
.map(|statement| types::Statement::from(statement))
.collect();
types::Function {
function_name,
parameters,
returns,
statements,
}
}
}
/// pest ast -> Import
impl<'ast> From<ast::Variable<'ast>> for PathString<'ast> {
fn from(import: ast::Variable<'ast>) -> Self {
import.span.as_str()
}
}
impl<'ast> From<ast::Import<'ast>> for Import<'ast> {
fn from(import: ast::Import<'ast>) -> Self {
match import {
ast::Import::Main(import) => Import::new(None, Path::new(import.source.span.as_str()))
.alias(import.alias.map(|alias| PathString::from(alias))),
ast::Import::From(import) => Import::new(
Some(PathString::from(import.symbol)),
Path::new(import.source.span.as_str()),
)
.alias(import.alias.map(|alias| PathString::from(alias))),
}
}
}
/// pest ast -> types::Program
impl<'ast, F: Field + PrimeField> From<ast::File<'ast>> for types::Program<'ast, F> {
fn from(file: ast::File<'ast>) -> Self {
// Compiled ast -> aleo program representation
let imports = file
.imports
.into_iter()
.map(|import| Import::from(import))
.collect::<Vec<Import>>();
let mut structs = HashMap::new();
let mut functions = HashMap::new();
file.structs.into_iter().for_each(|struct_def| {
structs.insert(
types::Variable::from(struct_def.variable.clone()),
types::Struct::from(struct_def),
);
});
file.functions.into_iter().for_each(|function_def| {
functions.insert(
types::FunctionName::from(function_def.function_name.clone()),
types::Function::from(function_def),
);
});
types::Program {
name: types::Variable {
name: "".into(),
_field: PhantomData::<F>,
},
imports,
structs,
functions,
}
}
}