Skip to content

Commit

Permalink
parse complete program
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Feb 2, 2024
1 parent 1efd032 commit 112f90a
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 11 deletions.
3 changes: 3 additions & 0 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ pub struct ModelDeclaration {
pub name: Identifier,
/// Fields of the model.
pub fields: Vec<Param>,
/// A parent model from which fields are inherited.
pub parent: Option<Identifier>,
/// Model logical bounds.
pub st_block: Option<StBlock>,
}
Expand Down Expand Up @@ -301,6 +303,7 @@ pub enum Expression {
Variable(Identifier),

Number(UnaryExpression<String>),
Boolean(UnaryExpression<bool>),
Float(UnaryExpression<String>),
String(UnaryExpression<String>),
Char(UnaryExpression<char>),
Expand Down
33 changes: 26 additions & 7 deletions crates/parser/src/folidity.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ EnumDeclaration: ast::EnumDeclaration = {
}

ModelDeclaration: ast::ModelDeclaration = {
<start:@L> "model" <i:Identifier> <params:Params> <st:StBlock?> <end:@R> => {
ast::ModelDeclaration::new(start, end, i, params, st)
<start:@L> "model" <i:Identifier> <parent:(":" <Identifier>)?> <params:Params> <st:StBlock?> <end:@R> => {
ast::ModelDeclaration::new(start, end, i, params, parent, st)
}
}

FromState: (ast::Identifier, Option<ast::Identifier>) = {
"from" <s:Identifier> <si:Identifier?> => {
(s, si)
"from" "(" <s:Identifier> <si:Identifier> ")" => {
(s, Some(si))
},
"from" <s:Identifier> => {
(s, None)
}
}

Expand All @@ -58,7 +61,7 @@ StateDeclaration: ast::StateDeclaration = {
ast::StateDeclaration::new(start, end, i, Some(ast::StateBody::Raw(params)), from, st)
},

<start:@L> "state" <i:Identifier> ";"<end:@R> => {
<start:@L> "state" <i:Identifier> <end:@R> => {
ast::StateDeclaration::new(start, end, i, None, None, None)
},
}
Expand Down Expand Up @@ -104,7 +107,7 @@ AccessAttr: ast::AccessAttribute = {


View: ast::FunctionVisibility = {
<start:@L> "view" "(" <param:StateParam> ")" <end:@R> => {
<start:@L> "view" <param:StateParam> <end:@R> => {
ast::FunctionVisibility::View(
ast::ViewState::new(start, end, param)
)
Expand Down Expand Up @@ -452,6 +455,19 @@ Term: ast::Expression = {
ast::UnaryExpression::new(start, end, val.to_string())
)
},

<start:@L> "true" <end:@R> => {
ast::Expression::Boolean(
ast::UnaryExpression::new(start, end, true)
)
},

<start:@L> "false" <end:@R> => {
ast::Expression::Boolean(
ast::UnaryExpression::new(start, end, false)
)
},

<start:@L> <val:float> <end:@R> => {
ast::Expression::Float(
ast::UnaryExpression::new(start, end, val.to_string())
Expand Down Expand Up @@ -501,7 +517,10 @@ Type: ast::Type = {
MappingRelation: ast::MappingRelation = {
<start:@L> <inj:">"?> "-" <partial:"/"?> ">" <surj:">"?> <end:@R> => {
ast::MappingRelation::new(start, end, inj.is_some(), partial.is_some(), surj.is_some())
}
},
<start:@L> <inj:">"?> "->" <end:@R> => {
ast::MappingRelation::new(start, end, false, false, false)
},
}

Mapping: ast::Mapping = {
Expand Down
8 changes: 8 additions & 0 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ pub type Span = Range<usize>;

lalrpop_mod!(pub folidity);

/// Parses a Solidity file into a concrete syntax tree.
/// # Returns
///
/// - A root of the syntax tree [`Source`]
///
/// # Errors
///
/// - A list of [`Report`] diagnostic error
pub fn parse(src: &str) -> Result<Source, Vec<Report>> {
let mut lexer_errors = Vec::new();
let tokens = Lexer::new(src, &mut lexer_errors);
Expand Down
191 changes: 187 additions & 4 deletions crates/parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use crate::{
ast::{
self, AccessAttribute, BinaryExpression, Declaration, EnumDeclaration, Expression,
FuncReturnType, FunctionCall, FunctionDeclaration, FunctionVisibility, Identifier, IfElse,
List, Mapping, MappingRelation, MemberAccess, Param, Set, Source, StBlock,
StateDeclaration, Statement, StatementBlock, StructDeclaration, StructInit, TypeVariant,
UnaryExpression, Variable,
List, Mapping, MappingRelation, MemberAccess, ModelDeclaration, Param, Set, Source,
StBlock, StateDeclaration, Statement, StatementBlock, StructDeclaration, StructInit,
TypeVariant, UnaryExpression, Variable,
},
lexer::{Lexer, Token},
parse,
Expand Down Expand Up @@ -98,7 +98,7 @@ fn test_simple_func() -> Result<(), String> {
}

const FACTORIAL_SRC: &str = r#"
state EmptyState;
state EmptyState
fn (out: int) calculate(value: int)
st [
value > 0,
Expand Down Expand Up @@ -525,6 +525,9 @@ fn () structs() {
let { one, reset } = MyStruct : { ..obj };
let a_enum = MyEnum.A;
}
model MyModel: ParentModel {
}
"#;

#[test]
Expand Down Expand Up @@ -685,9 +688,189 @@ fn test_structs_enums() -> Result<(), String> {
],
}),
})),
Declaration::ModelDeclaration(Box::new(ModelDeclaration {
loc: 210..240,
name: Identifier {
loc: 216..223,
name: "MyModel".to_string(),
},
fields: vec![],
parent: Some(Identifier {
loc: 225..236,
name: "ParentModel".to_string(),
}),
st_block: None,
})),
],
};

assert_eq!(tree, parsed, "Invalid tree: {:#?}", parsed);
Ok(())
}

const COMPLETE_SRC: &str = r#"
# This is a comment
enum Choice {
None,
Yay,
Nay
}
# This is
# a multiline comment
model BeginModel {
start_block: int,
end_block: int,
voters: set<Address>,
proposal: String,
max_size: int
} st [
start_block > (current_block + 10),
end_block > (start_block + 10),
voters.balance > 1000,
max_size > 0,
voter.size <= max_size
]
model VotingModel: BeginModel {
commits: mapping<address >-/> hex>
} st [
commits.key in voters
]
model RevealModel {
proposal: string,
end_block: int,
commit: mapping<hex -> Choice>
} st [
end_block > (current_block + 15),
yays >= 0,
nays >= 0,
(yays + nays) <= commits.size
]
model ExecuteModel {
proposal: string,
passed: bool
}
state BeginState(BeginModel)
state VotingState(VotingModel)
state RevealState(RevealModel) from (VotingState vst)
st current_block > vst.end_block
state ExecuteState(ExecuteModel) from RevealState st [
current_block > RevealModel.end_block
]
state ExecuteState {
proposal: String,
passed: bool
} from (RevealState rst) st [
current_block > rst.end_block
]
@init
@(any)
fn () init(proposal: String,
start_block: int,
max_size: int,
end_block: int)
when () -> BeginState
{
move BeginState : {
proposal,
start_block,
end_block,
max_size
};
}
@(any)
fn () join() when (BeginState s) -> BeginState {
let caller = caller();
let { voters, params } = s;
voters = voters + caller;
move BeginState : {
voters
| ..params
};
}
@(voters)
fn () start_voting() when (BeginState s) -> VotingState {
commits = Set();
move VotingState : {
commits
| ..s
};
}
@(voters)
fn () commit(h: hex) when (VotingState s) -> VotingState {
let caller = caller();
let { commits, params } = s;
commits = commits :> add(caller, h);
move VotingState : {
commits
| ..params
};
}
@(any)
fn () start_reveal() when (VotingState s) -> RevealState {
let { end_block, proposal, commits, params } = s;
move RevealState : {
endblock + 10,
proposal,
# we need to add lambda to grammar later
# commits :> map(|c| (c.value, Choice::None))
0,
0
};
}
@(any)
fn () execute() when (RevealState s) -> ExecuteState {
let votes = s.commits.values;
# add lambda later
# let yay = votes :> filter(|v| v == Choice::Yay).sum();
let mut passed = false;
if votes.size / yay > 0.5 {
passed = true;
move ExecuteState : {
ss.proposal,
passed
};
} else {
move ExecuteState : {
s.proposal,
passed
};
}
}
view(BeginState s) fn list<Address> get_voters() {
return s.voters;
}
"#;

#[test]
fn parse_complete_program() {
let res = parse(COMPLETE_SRC);
match res {
Ok(_) => {}
Err(errs) => {
panic!("{:#?}", errs)
}
}
}

0 comments on commit 112f90a

Please sign in to comment.