Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic PEG grammar for Stoffel Lang #42

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
652372a
Added examples directory
Mikerah Dec 24, 2021
5849c26
Basic honeybadgerswap examples in stoffelang
Mikerah Dec 24, 2021
bbc01a8
Changed initialized_pool() to constructor()
Mikerah Feb 6, 2022
2e57c41
Made first letter of functions that are exposed i.e. called by a user…
Mikerah Feb 6, 2022
8c02830
Chose option 2 for Trade function. Will need unsigned numbers.
Mikerah Feb 6, 2022
000bccc
Started pest grammar
Mikerah Mar 17, 2022
d650663
Basic handling of variables with types in stoffel lang grammar
Mikerah Mar 27, 2022
989425e
Some syntax changes to hbs1.stf
Mikerah May 17, 2022
b64ae4e
first pest grammar
Mikerah Jun 6, 2022
3a5e36a
Updated cargo.toml
Mikerah Jun 6, 2022
97c9fbc
Added lib.rs to parser folder in the compiler crate
Mikerah Jun 6, 2022
5f675af
Fixed whitespace issues
Mikerah Jun 8, 2022
a0027bd
Fixed program block parsing
Mikerah Jun 8, 2022
7c36df1
Added storage and amended types to include struct types
Mikerah Jun 9, 2022
f033952
removed constructor keyword. Will take into account after the parsing…
Mikerah Jun 9, 2022
17a97f7
cleaned up variables related grammar and fixed grammar for operations…
Mikerah Jun 9, 2022
5d29a3a
Fixed struct usage
Mikerah Jun 9, 2022
add531c
Fixed for loop rule in grammar
Mikerah Jun 9, 2022
ee97604
added shorthand operators to grammar
Mikerah Jun 9, 2022
a0d7499
fixed issues with expression within parentheses in the grammar
Mikerah Jun 9, 2022
c392d02
Modified HoneyBadgerSwap example to fit newer grammar
Mikerah Jun 9, 2022
07ddcec
Added mpc keyword to denote functions that are meant to be executed b…
Mikerah Jun 11, 2022
d459be8
Added placeholder errors for parser
Mikerah Jun 11, 2022
151bf08
Modified compiler crate to be a lib instead of an executable
Mikerah Jun 11, 2022
5dbce83
Reorganized compiler crate and added parser skeleton
Mikerah Jun 11, 2022
49f9c90
Applied clippy suggestions
Mikerah Jun 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
pest = "2.0"
pest_derive = "2.0"
14 changes: 14 additions & 0 deletions compiler/parser/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extern crate pest;
#[macro_use]
extern crate pest_derive;

use std::fs;
use pest::{Parser, iterators::Pairs, error::Error}

#[derive(Parser)]
#[grammar = "stoffel_lang.pest"]
pub struct StoffelLangParser;

pub fn parse(input: &str) -> Result<Pairs<Rule>, Error<Rule>> {
unimplemented!();
}
119 changes: 119 additions & 0 deletions compiler/parser/stoffel_lang.pest
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
WHITESPACE = _{ " " }
alpha = {'a'..'z' | 'A'..'Z'}
digit = {'0'..'9'}
number = {digit+}
ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC|"_")* }

// Keywords
for_keyword = {"for"}
while_keyword = {"while"}
if_keyword = {"if"}
else_keyword = {"else"}
struct_keyword = {"struct"}
variable_let_keyword = {"let"}
variable_storage_keyword = {"storage"}
constructor_keyword = {"constructor"}
function_keyword = {"fn"}
fn_returns = {"->"}
path_separator = {"::"}
use_keyword = {"use"}
true_keyword = {"true"}
false_keyword = {"false"}
function_returns = {"->"}
// types
type_sint_keyword = {"sint"}
type_int_keyword = {"int"}
type_uint_keyword = {"uint"}
type_sfix_keyword = {"sfix"}
type_fix_keyword = {"fix"}
type_sfloat_keyword = {"sfloat"}
type_float_keyword = {"float"}
type_regint_keyword = {"regint"}
type_sgf2n_keyword = {"sgf2n"}
type_gf2n_keyword = {"gf2n"}
type_bool_keyword = {"bool"}

// Comments
line_comment_open = {"//"}
block_comment_open = {"/*"}
block_comment_close = {"*/"}

arith_ops = {"/" | "*" | "+" | "-" | "**"}
comp_ops = {"==" | ">=" | "<=" | "!=" | ">" | "<"}
logical_ops = {"||" | "&&"}
inc_dec_ops = {"++" | "--"}
unary_ops = {"!"}
ops = {arith_ops | comp_ops | logical_ops | unary_ops}

code_block = {"{" ~ (declarations | control_flow)* ~ "}"}

// base types
base_types = { type_fix_keyword | type_float_keyword |
type_gf2n_keyword | type_int_keyword |
type_regint_keyword | type_sfix_keyword |
type_sfloat_keyword | type_sgf2n_keyword |
type_sint_keyword | type_uint_keyword |
type_bool_keyword}

boolean_values = {true_keyword | false_keyword}

literal_value = {number | boolean_values}

expr_inner = { if_exp | code_block | literal_value | struct_component_access | array_index}

expr = {expr_inner ~ (ops ~ expr_inner)*}

call_item = {ident | "(" ~ expr ~ ")"}

// Arrays
array_length = { number+ }
array_expression = {"[" ~ array_elements ~ "]"}
array_elements = {literal_value ~ ("," ~ array_elements)*}
array_type = {"[" ~ base_types ~ ";" ~ number ~ "]"}
array_index = {call_item ~ "[" ~ expr ~"]" ~ ("[" ~ expr ~ "]")*}


// Structs
struct_name = {ident}
struct_component_name = @{(!digit ~ ident)+}
struct_component = {(struct_component_name ~ ":" ~ types ~ ("," ~ struct_component_name ~ ":" ~ types)* ~ ","?)?}
struct_block = { "{" ~ " " ~ struct_component? ~ " " ~ "}"}
struct_dec = {struct_keyword ~ struct_name ~ " " ~ struct_block}
struct_component_access = {subfield_path}
subfield_path = {(sub_subfield_path ~ ".")+ ~ call_item}
sub_subfield_path = {array_index|call_item}

composed_types = {array_type| map_dec | struct_dec}

types = {base_types | composed_types }

// Variables
variable_name = {ident}
variable_dec = {variable_let_keyword ~ variable_storage_keyword? ~ variable_name ~ ":" ~ (base_types | composed_types) ~ ( "=" ~ expr)?~ ";"}

// functions
function_name = {("constructor") | ident}
function_param_name = {ident}
function_args = { ((function_param_name ~ ":" ~ types) ~ ("," ~ (function_param_name ~ ":" ~ types))*)? }
function_signature = { function_keyword ~ function_name ~ "(" ~ function_args? ~ ")" ~ (function_returns ~ types)? }
function_dec = {function_signature ~ code_block}


// control flow
condition = {expr ~ (comp_ops | logical_ops) ~ expr}
if_exp = {if_keyword ~ expr ~ code_block ~ (else_keyword ~ (code_block|if_exp))?}
while_loop = {while_keyword ~ expr ~ code_block}
for_loop = {for_keyword ~ "(" ~ variable_dec? ~ ";" ~ condition ~ ";" ~ (expr ~ inc_dec_ops) ~ ")" ~ code_block}
control_flow = {condition | if_exp | while_loop | for_loop}

// Declarations
non_variable_declarations = {function_dec}
variable_declarations = {variable_dec}
var_reassignment = {variable_name ~ "=" ~ expr ~ ";"}
struct_field_reassignment = {struct_component_access ~ "=" ~ expr ~ ";"}
reassignment = {var_reassignment | struct_field_reassignment}
declarations = {non_variable_declarations | variable_declarations | reassignment}

program_dec = {"program" ~ ident ~ program_block}
program_block = { "{" ~ expr ~ "}" }
program = _{ SOI ~ program_dec ~ EOI}
9 changes: 9 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Examples

This folder contains examples of Stoffel applications that you can use to get an understanding of Stoffel Lang.

## Demo Applications
- HoneyBadgerSwap: A basic Uniswap-liked automated market maker. We provide two version of this: one in which we want the identities of the traders to be known but not the amounts that they trade and another in which we want both the identities and amounts traded hidden. Similarly, for the liquidity providers. In both scenarios, the assets are public.
- LMSR-based prediction market: Similar to the HoneyBadgerSwap example, except users bet on outcomes instead of trades that are determined by the logarithmic scoring rule by Hansen.
- An orderbook-based exchange: An exchange that uses an orderbook in order to keep track of buys and sell bids from traders and market makers.
- Auction: A basic double auction example based on the original Sugar Beet Auction
83 changes: 83 additions & 0 deletions examples/stoffel/hbs1.stf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// HBS where we care about the privacy of traders and LPs

program HoneyBadgerSwap {
struct Pool {
assetAID: int,
assetBID: int,
amountA: sint,
amountB: sint,
k: sint
}

struct UserData {
userID: sint,
assets: Map<int, sint>
}

struct LPData {
lpID: sint,
shares: sint,
userData: UserData
}

storage sint totalShares;
storage Pool pool;

storage Map<sint, LPData> lpData;

fn constructor(assetAID: sint, amountA: sint, assetBID: sint, amountB: sint) {
sint k = amountA * amountB;
pool = Pool {assetAID: assetAID, amountA: amountA, assetBID:assetBID, amountB: amountB, k: k};
}

// Will need to create an unsigned integer type for the language
pub fn Trade(amountA: sint, amountB: sint) -> bool {
// But since we don't have an unsigned type (yet), we need to do a check which is expensive
if amountA < 0 or amount B < 0 {
return false;
}

sint newAmountA = pools[0].amountA - amountA;
sint priceAssetB = pools[0].k / (pools[0].amountA - newAmountA)
sint newAmountB = pools[0].k / priceAssetB

userData[userID].assets[assetA] -= amountA;
userData[userID].assets[assetB] += amountB;

pools[0].amountA = newAmountA;
pools[0].amountB = newAmountB;
}

// Obviously, needs way more input validation. Will be added when we have more details
// about the language for a complete, realistic example.
pub fn AddLiquidity(lpID: sint, amountA: sint, maxAmountB: sint) -> sint {
sint tokenBAmount = (amountA * pool.amountB) / pool.amountA;
sint sharesMinted = (amountA * totalShares) / pool.amountA;

lpData[lpID].shares += sharesMinted;
totalShares += sharesMinted;

lpData[lpID].userData.amountA += amountA;
lpData[lpID].userData.amountB += amountB;

pool.amountA += amountA;
pool.amountB += tokenBAmount;

}

// Again, missing some basic functionality but does what we need to do for basic ideation on syntax
pub fn RemoveLiquidity(lpID: sint, shares: sint) {
sint amountA = (shares * pool.amountA) / totalShares;
sint amountB = (shares * pool.amountB) / totalShares;

lpData[lpID].shares -= shares;
totalShares -= shares;

lpData[lpID].userData.amountA -= amountA;
lpData[lpID].userData.amountB -= amountB;

pool.amountA -= amountA;
pool.amountB -= amountB;
}

}
81 changes: 81 additions & 0 deletions examples/stoffel/hbs2.stf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// HBS where we only care about trader amounts

program HoneyBadgerSwap {
struct Pool {
assetAID: int,
assetBID: int,
amountA: sint,
amountB: sint
}

struct UserData {
userID: int,
assets: Map<int, sint>
}

struct LPData {
lpID: sint,
shares: sint,
userData: UserData
}

storage Pool pool;

storage Map<int, UserData> userData;
storage Map<int, LPData> lpData;

fn constructor(assetAID: sint, amountA: sint, assetBID: sint, amountB: sint) {
sint k = amountA * amountB;
pool = Pool {assetAID: assetAID, amountA: amountA, assetBID:assetBID, amountB: amountB, k: k};
}

// Will need to create an unsigned integer type for the language
pub fn Trade(userID: int, amountA: sint, amountB: sint) -> bool {
if amountA < 0 or amount B < 0 {
return false;
}

sint newAmountA = pools[0].amountA - amountA;
sint priceAssetB = pools[0].k / (pools[0].amountA - newAmountA)
sint newAmountB = pools[0].k / priceAssetB

userData[userID].assets[assetA] -= amountA;
userData[userID].assets[assetB] += amountB;

pools[0].amountA = newAmountA;
pools[0].amountB = newAmountB;
}


// Obviously, needs way more input validation. Will be added when we have more details
// about the language for a complete, realistic example.
pub fn AddLiquidity(lpID: int, amountA: sint, maxAmountB: sint) -> sint {
sint tokenBAmount = (amountA * pool.amountB) / pool.amountA;
sint sharesMinted = (amountA * totalShares) / pool.amountA;

lpData[lpID].shares += sharesMinted;
totalShares += sharesMinted;

lpData[lpID].userData.amountA += amountA;
lpData[lpID].userData.amountB += amountB;

pool.amountA += amountA;
pool.amountB += tokenBAmount;

}

// Again, missing some basic functionality but does what we need to do for basic ideation on syntax
pub fn RemoveLiquidity(lpID: int, shares: sint) {
sint amountA = (shares * pool.amountA) / totalShares;
sint amountB = (shares * pool.amountB) / totalShares;

lpData[lpID].shares -= shares;
totalShares -= shares;

lpData[lpID].userData.amountA -= amountA;
lpData[lpID].userData.amountB -= amountB;

pool.amountA -= amountA;
pool.amountB -= amountB;
}
}