Skip to content

Commit

Permalink
Support yul ast (#268)
Browse files Browse the repository at this point in the history
* feat: support for basic yul types

* feat: support parsing of yul-like evm opcodes

* feat(syn-solidity): parse yul function calls

* feat(syn-solidity): support parsing yul for loops

* feat(syn-solidity): support parsing yul switch statements

* feat(syn-solidity): move walrus token to own file, support parsing assign stmts

* feat(syn-solidity): parse yul stmts

* feat(syn-solidity): parse hex num, yul lits, + cleanup

* feat(syn-solidity): support yul path + bug fixes

* cleanup + parsing bug fixes

* fix small nits

* Update crates/syn-solidity/src/yul/type/function.rs

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>

* fix: correct doc link for yulAssignment

* fix: expose private struct vars

* fix: replace redudant YulPath enum with wrapper struct

* fix: ignore tokens in fmt::Debug, convert YulAssign & YulDecl into structs

* fix: replace blob import with individual imports

* fix: ensure switch has atleast one case or default

* fix: remove YulLit and replace with crate::Lit

---------

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
  • Loading branch information
mouseless0x and DaniPopes authored Sep 18, 2023
1 parent be55894 commit 2967dd9
Show file tree
Hide file tree
Showing 19 changed files with 1,317 additions and 17 deletions.
82 changes: 82 additions & 0 deletions crates/syn-solidity/src/kw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,86 @@ custom_keywords!(
new,
revert,
unchecked,

// Yul other
switch,
case,
default,
leave,

// Yul-EVM-builtin opcodes
stop,
add,
sub,
mul,
div,
sdiv,
//mod,
smod,
exp,
not,
lt,
gt,
slt,
sgt,
eq,
iszero,
and,
or,
xor,
byte,
shl,
shr,
sar,
addmod,
mulmod,
signextend,
keccak256,
pop,
mload,
mstore,
mstore8,
sload,
sstore,
msize,
gas,
address,
balance,
selfbalance,
caller,
callvalue,
calldataload,
calldatasize,
calldatacopy,
extcodesize,
extcodecopy,
returndatasize,
returndatacopy,
extcodehash,
create,
create2,
call,
callcode,
delegatecall,
staticcall,
//return,
//revert,
selfdestruct,
invalid,
log0,
log1,
log2,
log3,
log4,
chainid,
origin,
gasprice,
blockhash,
coinbase,
timestamp,
number,
difficulty,
prevrandao,
gaslimit,
basefee,
);
6 changes: 5 additions & 1 deletion crates/syn-solidity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ pub mod visit_mut;
pub use visit_mut::VisitMut;

mod yul;
pub use yul::YulBlock;
pub use yul::{
WalrusToken, YulBlock, YulCaseBranch, YulEVMBuiltIn, YulExpr, YulFnCall, YulFor,
YulFunctionDef, YulIdent, YulIf, YulPath, YulReturns, YulStmt, YulSwitch, YulSwitchDefault,
YulVarAssign, YulVarDecl,
};

/// Parse a Solidity [`proc_macro::TokenStream`] into a [`File`].
pub fn parse(input: proc_macro::TokenStream) -> Result<File> {
Expand Down
104 changes: 104 additions & 0 deletions crates/syn-solidity/src/yul/expr/fn_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use crate::{utils::DebugPunctuated, Spanned, YulEVMBuiltIn, YulExpr, YulIdent};

use proc_macro2::Span;
use std::fmt;
use syn::{
parenthesized,
parse::{discouraged::Speculative, Parse, ParseStream, Result},
punctuated::Punctuated,
token::Paren,
Token,
};

/// Yul function call.
///
/// Solidity Reference:
/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulFunctionCall>
#[derive(Clone)]
pub struct YulFnCall {
pub function_type: FnType,
pub paren_token: Paren,
pub arguments: Punctuated<YulExpr, Token![,]>,
}

impl Parse for YulFnCall {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let content;
Ok(Self {
function_type: input.parse()?,
paren_token: parenthesized!(content in input),
arguments: content.parse_terminated(YulExpr::parse, Token![,])?,
})
}
}

impl Spanned for YulFnCall {
fn span(&self) -> Span {
let span = self.function_type.span();
span.join(self.arguments.span()).unwrap_or(span)
}

fn set_span(&mut self, span: Span) {
self.function_type.set_span(span);
self.paren_token = Paren(span);
self.arguments.set_span(span);
}
}

impl fmt::Debug for YulFnCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("YulFnCall")
.field("function_type", &self.function_type)
.field("arguments", DebugPunctuated::new(&self.arguments))
.finish()
}
}

/// What type of function is called.
#[derive(Clone)]
pub enum FnType {
/// When calling a self defined function
Custom(YulIdent),

/// When calling a built in evm opcode
EVMOpcode(YulEVMBuiltIn),
}

impl Parse for FnType {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let speculative_parse = input.fork();

if let Ok(evm_builtin) = speculative_parse.parse::<YulEVMBuiltIn>() {
input.advance_to(&speculative_parse);
Ok(Self::EVMOpcode(evm_builtin))
} else {
Ok(Self::Custom(input.parse::<YulIdent>()?))
}
}
}

impl Spanned for FnType {
fn span(&self) -> Span {
match self {
Self::Custom(custom) => custom.span(),
Self::EVMOpcode(opcode) => opcode.span(),
}
}

fn set_span(&mut self, span: Span) {
match self {
Self::Custom(custom) => custom.set_span(span),
Self::EVMOpcode(opcode) => opcode.set_span(span),
}
}
}

impl fmt::Debug for FnType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("FnType::")?;
match self {
Self::Custom(custom) => custom.fmt(f),
Self::EVMOpcode(opcode) => opcode.fmt(f),
}
}
}
69 changes: 69 additions & 0 deletions crates/syn-solidity/src/yul/expr/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use crate::{Lit, Spanned, YulPath};

use std::fmt;

use proc_macro2::Span;
use syn::{
parse::{discouraged::Speculative, Parse, ParseStream, Result},
token::Paren,
};

mod fn_call;
pub use fn_call::YulFnCall;

/// A Yul expression.
///
/// Solidity Reference:
/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulExpression>
#[derive(Clone)]
pub enum YulExpr {
Path(YulPath),
Call(YulFnCall),
Literal(Lit),
}

impl Parse for YulExpr {
fn parse(input: ParseStream<'_>) -> Result<Self> {
if input.peek2(Paren) {
return input.parse().map(Self::Call)
}

let speculative_parse = input.fork();

if let Ok(lit) = speculative_parse.parse::<Lit>() {
input.advance_to(&speculative_parse);
return Ok(Self::Literal(lit))
}

input.parse().map(Self::Path)
}
}

impl Spanned for YulExpr {
fn span(&self) -> Span {
match self {
Self::Path(path) => path.span(),
Self::Call(call) => call.span(),
Self::Literal(lit) => lit.span(),
}
}

fn set_span(&mut self, span: Span) {
match self {
Self::Path(path) => path.set_span(span),
Self::Call(call) => call.set_span(span),
Self::Literal(lit) => lit.set_span(span),
}
}
}

impl fmt::Debug for YulExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("YulExpr::")?;
match self {
Self::Path(path) => path.fmt(f),
Self::Call(call) => call.fmt(f),
Self::Literal(lit) => lit.fmt(f),
}
}
}
Loading

0 comments on commit 2967dd9

Please sign in to comment.