mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-10-26 07:00:35 +03:00
commit
7500f9a5fd
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -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"
|
||||
|
32
Cargo.toml
32
Cargo.toml
@ -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
265
README.md
@ -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`.
|
@ -1,5 +0,0 @@
|
||||
65279,1179403647,1463895090
|
||||
3.1415927,2.7182817,1.618034
|
||||
-40,-273.15
|
||||
13,42
|
||||
65537
|
|
4
simple.leo
Normal file
4
simple.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() -> (u32) {
|
||||
a = 1 + 1
|
||||
return a
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
x = 5 + 3
|
||||
y = x * (x * 2)
|
4
simple_import.leo
Normal file
4
simple_import.leo
Normal file
@ -0,0 +1,4 @@
|
||||
struct Point {
|
||||
u32 x
|
||||
u32 y
|
||||
}
|
963
src/ast.rs
Normal file
963
src/ast.rs
Normal 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>,
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
field = { (ASCII_DIGIT | "." | "-")+ }
|
||||
record = { field ~ ("," ~ field)* }
|
||||
file = { SOI ~ (record ~ ("\r\n" | "\n"))* ~ EOI }
|
@ -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);
|
||||
}
|
@ -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
178
src/leo.pest
Normal 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
11
src/lib.rs
Normal 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;
|
499
src/main.rs
499
src/main.rs
@ -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>(¶ms.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, ¶ms, 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);
|
||||
}
|
||||
|
154
src/program/constraints/boolean.rs
Normal file
154
src/program/constraints/boolean.rs
Normal 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, ¶meter.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, ¶meter.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))
|
||||
}
|
||||
}
|
231
src/program/constraints/constraints.rs
Normal file
231
src/program/constraints/constraints.rs
Normal 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(),
|
||||
¶meter.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(),
|
||||
¶meter.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(),
|
||||
¶meter.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);
|
||||
}
|
||||
}
|
404
src/program/constraints/expression.rs
Normal file
404
src/program/constraints/expression.rs
Normal 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!(),
|
||||
}
|
||||
}
|
||||
}
|
118
src/program/constraints/field_element.rs
Normal file
118
src/program/constraints/field_element.rs
Normal 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, ¶meter.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, ¶meter.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))
|
||||
}
|
||||
}
|
166
src/program/constraints/integer.rs
Normal file
166
src/program/constraints/integer.rs
Normal 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, ¶meter.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, ¶meter.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(),
|
||||
)
|
||||
}
|
||||
}
|
29
src/program/constraints/mod.rs
Normal file
29
src/program/constraints/mod.rs
Normal 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::*;
|
79
src/program/constraints/resolved_program.rs
Normal file
79
src/program/constraints/resolved_program.rs
Normal 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)
|
||||
}
|
||||
}
|
92
src/program/constraints/resolved_value.rs
Normal file
92
src/program/constraints/resolved_value.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
198
src/program/constraints/statement.rs
Normal file
198
src/program/constraints/statement.rs
Normal 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
75
src/program/imports.rs
Normal 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
20
src/program/mod.rs
Normal 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
185
src/program/types.rs
Normal 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
|
||||
}
|
||||
}
|
284
src/program/types_display.rs
Normal file
284
src/program/types_display.rs
Normal 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
610
src/program/types_from.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user