Skip to content

Commit

Permalink
Merge branch 'main' into fix/lex-escaped-strings
Browse files Browse the repository at this point in the history
  • Loading branch information
lmittmann committed Oct 18, 2024
2 parents 0780acb + ff69c52 commit 012134c
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 94 deletions.
10 changes: 8 additions & 2 deletions crates/ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub enum Definition<'src> {
Macro(Macro<'src>),
Constant {
name: Spanned<&'src str>,
value: U256,
expr: Spanned<ConstExpr>,
},
Jumptable(Jumptable<'src>),
Table {
Expand All @@ -37,6 +37,12 @@ pub struct Macro<'src> {
pub body: Box<[MacroStatement<'src>]>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum ConstExpr {
Value(U256),
FreeStoragePointer,
}

#[derive(Debug, PartialEq, Eq)]
pub enum MacroStatement<'src> {
LabelDefinition(Spanned<&'src str>),
Expand Down Expand Up @@ -69,7 +75,7 @@ pub enum Invoke<'src> {

#[derive(Debug, PartialEq, Eq)]
pub struct Jumptable<'src> {
pub name: (Span, &'src str),
pub name: Spanned<&'src str>,
pub size: u8,
pub labels: Box<[&'src str]>,
}
Expand Down
159 changes: 93 additions & 66 deletions crates/ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ fn root<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Root<'src>>
}

fn root_section<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::RootSection<'src>> {
let definition = definition().map(ast::RootSection::Definition);
let include = just(Keyword("include"))
.ignore_then(select! {String(s) => s}.map_with(|s, ex| (s, ex.span())))
.map(ast::RootSection::Include);

choice((definition().map(ast::RootSection::Definition), include))
choice((definition, include))
}

fn definition<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definition<'src>> {
Expand All @@ -83,28 +84,25 @@ fn definition<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Defin
}

fn r#macro<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definition<'src>> {
let macro_args = ident().separated_by(just(Punct(','))).collect::<Vec<_>>();
let macro_args = ident().separated_by(punct(',')).collect::<Vec<_>>();

just(Ident("macro"))
.ignore_then(ident())
.then_ignore(just(Punct('(')))
.then(macro_args)
.then_ignore(just(Punct(')')))
.then_ignore(just(Punct('=')))
.then(macro_args.delimited_by(punct('('), punct(')')))
.then_ignore(punct('='))
.then(
just(Ident("takes"))
.ignore_then(just(Punct('(')))
.ignore_then(dec())
.then_ignore(just(Punct(')')))
.ignore_then(dec().delimited_by(punct('('), punct(')')))
.then_ignore(just(Ident("returns")))
.then_ignore(just(Punct('(')))
.then(dec())
.then_ignore(just(Punct(')')))
.then(dec().delimited_by(punct('('), punct(')')))
.or_not(),
)
.then_ignore(just(Punct('{')))
.then(macro_statement().repeated().collect::<Vec<_>>())
.then_ignore(just(Punct('}')))
.then(
macro_statement()
.repeated()
.collect::<Vec<_>>()
.delimited_by(punct('{'), punct('}')),
)
.map(|(((name, args), takes_returns), body)| ast::Macro {
name,
args: args.into_boxed_slice(),
Expand All @@ -117,7 +115,7 @@ fn r#macro<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definiti
fn macro_statement<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::MacroStatement<'src>>
{
let label = ident()
.then_ignore(just(Punct(':')))
.then_ignore(punct(':'))
.map(ast::MacroStatement::LabelDefinition);
let instruction = instruction().map(ast::MacroStatement::Instruction);
let invoke = invoke().map(ast::MacroStatement::Invoke);
Expand Down Expand Up @@ -212,26 +210,21 @@ fn instruction<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Inst
ast::Instruction::LabelReference((ident, span))
}
});
let macro_arg_ref = just(Punct('<'))
.ignore_then(ident())
.then_ignore(just(Punct('>')))
let macro_arg_ref = ident()
.delimited_by(punct('<'), punct('>'))
.map(ast::Instruction::MacroArgReference);
let constant_ref = just(Punct('['))
.ignore_then(ident())
.then_ignore(just(Punct(']')))
let constant_ref = ident()
.delimited_by(punct('['), punct(']'))
.map(ast::Instruction::ConstantReference);

choice((push_auto, push, op, macro_arg_ref, constant_ref))
}

fn invoke<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Invoke<'src>> {
let invoke_macro_args = just(Punct('('))
.ignore_then(
instruction()
.separated_by(just(Punct(',')))
.collect::<Vec<_>>(),
)
.then_ignore(just(Punct(')')))
let invoke_macro_args = instruction()
.separated_by(punct(','))
.collect::<Vec<_>>()
.delimited_by(punct('('), punct(')'))
.map(|args| args.into_boxed_slice());

let invoke_macro = ident()
Expand All @@ -240,9 +233,9 @@ fn invoke<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Invoke<'s

let invoke_builtin = |name, constructor: fn((_, Span)) -> ast::Invoke<'src>| {
just(Ident(name))
.ignore_then(just(Punct('(')))
.ignore_then(punct('('))
.ignore_then(ident())
.then_ignore(just(Punct(')')))
.then_ignore(punct(')'))
.map(constructor)
};

Expand All @@ -259,19 +252,30 @@ fn invoke<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Invoke<'s
}

fn constant<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definition<'src>> {
let const_expr = choice((
word().map(|(value, span)| (ast::ConstExpr::Value(value), span)),
just(Ident("FREE_STORAGE_POINTER"))
.ignore_then(just(Punct('(')))
.ignore_then(just(Punct(')')))
.map_with(|_, ex| (ast::ConstExpr::FreeStoragePointer, ex.span())),
));

just(Ident("constant"))
.ignore_then(ident())
.then_ignore(just(Punct('=')))
.then(word())
.map(|(name, (value, _))| ast::Definition::Constant { name, value })
.then_ignore(punct('='))
.then(const_expr)
.map(|(name, expr)| ast::Definition::Constant { name, expr })
}

fn table<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definition<'src>> {
just(Ident("table"))
.ignore_then(ident())
.then_ignore(just(Punct('{')))
.then(code().repeated().collect::<Vec<_>>())
.then_ignore(just(Punct('}')))
.then(
code()
.repeated()
.collect::<Vec<_>>()
.delimited_by(punct('{'), punct('}')),
)
.map(|(name, code)| ast::Definition::Table {
name,
data: code
Expand All @@ -287,13 +291,18 @@ fn sol_function<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Def
.ignore_then(ident())
.then(sol_type_list())
.then(
choice((just(Ident("public")), just(Ident("external"))))
.then_ignore(choice((just(Ident("view")), just(Ident("pure")))).or_not())
.then_ignore(choice((just(Ident("payable")), just(Ident("nonpayable")))).or_not())
.or_not()
.ignore_then(just(Ident("returns")))
.ignore_then(sol_type_list())
.or_not(),
choice((
just(Ident("public")),
just(Ident("external")),
just(Ident("payable")),
just(Ident("nonpayable")),
))
.or_not()
.then_ignore(choice((just(Ident("view")), just(Ident("pure")))).or_not())
.or_not()
.ignore_then(just(Ident("returns")))
.ignore_then(sol_type_list())
.or_not(),
)
.map(|((name, args), rets)| {
let rets = rets.unwrap_or(Box::new([]));
Expand All @@ -317,41 +326,34 @@ fn sol_error<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Defini

fn sol_type_list<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, Box<[Spanned<DynSolType>]>>
{
just(Punct('('))
.ignore_then(
sol_type()
.separated_by(just(Punct(',')))
.collect::<Vec<_>>(),
)
.then_ignore(just(Punct(')')))
sol_type()
.separated_by(punct(','))
.collect::<Vec<_>>()
.delimited_by(punct('('), punct(')'))
.map(|args| args.into_boxed_slice())
}

fn sol_type<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, Spanned<DynSolType>> {
recursive(|sol_raw_type| {
let sol_raw_primitive_type = ident().map(|(typ, _)| typ.to_string()).boxed();
let sol_raw_primitive_type = ident().map(|(typ, _)| typ.to_string());

let sol_raw_tuple_type = just(Punct('('))
.ignore_then(
sol_raw_type
.separated_by(just(Punct(',')))
.collect::<Vec<_>>(),
)
.then_ignore(just(Punct(')')))
let sol_raw_tuple_type = sol_raw_type
.separated_by(punct(','))
.collect::<Vec<_>>()
.delimited_by(punct('('), punct(')'))
.map(|types| {
let mut result = "(".to_string();
let types = types.into_iter().collect::<Vec<_>>().join(",");
result.push_str(&types);
result.push(')');
result
})
.boxed();
});

choice((sol_raw_primitive_type, sol_raw_tuple_type))
.then(
just(Punct('['))
punct('[')
.ignore_then(dec().or_not())
.then_ignore(just(Punct(']')))
.then_ignore(punct(']'))
.or_not(),
)
.then_ignore(ident().or_not())
Expand Down Expand Up @@ -401,6 +403,10 @@ fn code<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, Vec<u8>> {
.map(|code| code.to_vec())
}

fn punct<'tokens, 'src: 'tokens>(c: char) -> impl Parser<'tokens, 'src, Token<'src>> {
just(Punct(c))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -494,7 +500,7 @@ mod tests {
vec![Keyword("define"), Ident("constant"), Ident("TEST"), Punct('='), Hex("0x1")],
ast::RootSection::Definition(ast::Definition::Constant {
name: ("TEST", span),
value: uint!(1_U256)
expr: (ast::ConstExpr::Value(uint!(1_U256)), span)
})
);
}
Expand Down Expand Up @@ -614,15 +620,36 @@ mod tests {
}

#[test]
fn parse_constant() {
fn parse_constant_value() {
let span: Span = SimpleSpan::new(0, 0);

assert_ok!(
constant(),
vec![Ident("constant"), Ident("TEST"), Punct('='), Hex("0x1")],
ast::Definition::Constant {
name: ("TEST", span),
value: uint!(1_U256)
expr: (ast::ConstExpr::Value(uint!(1_U256)), span)
}
);
}

#[test]
fn parse_constant_storage_pointer() {
let span: Span = SimpleSpan::new(0, 0);

assert_ok!(
constant(),
vec![
Ident("constant"),
Ident("VAR_LOCATION"),
Punct('='),
Ident("FREE_STORAGE_POINTER"),
Punct('('),
Punct(')')
],
ast::Definition::Constant {
name: ("VAR_LOCATION", span),
expr: (ast::ConstExpr::FreeStoragePointer, span)
}
);
}
Expand Down
Loading

0 comments on commit 012134c

Please sign in to comment.