Skip to content

Commit

Permalink
Merge branch 'main' into feat/basic-analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Philogy authored Oct 19, 2024
2 parents d4eb420 + ff69c52 commit a20a79c
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 97 deletions.
11 changes: 9 additions & 2 deletions crates/ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ use alloy_primitives::U256;
use chumsky::span::SimpleSpan;
use evm_glue::opcodes::Opcode;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Root<'src>(pub Box<[Definition<'src>]>);
#[derive(Debug, PartialEq, Eq)]
pub struct Root<'src>(pub Box<[RootSection<'src>]>);

#[derive(Debug, PartialEq, Eq)]
pub enum RootSection<'src> {
Definition(Definition<'src>),
Include(Spanned<&'src str>),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Definition<'src> {
Expand Down Expand Up @@ -61,6 +67,7 @@ pub enum ConstExpr {
FreeStoragePointer,
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MacroStatement<'src> {
LabelDefinition(Spanned<&'src str>),
Expand Down
63 changes: 48 additions & 15 deletions crates/ast/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub enum Token<'src> {
Dec(&'src str),
Hex(&'src str),
Bin(&'src str),
String(&'src str),

Error(char),
}
Expand All @@ -39,35 +40,57 @@ impl fmt::Display for Token<'_> {
| Token::Ident(s)
| Token::Dec(s)
| Token::Hex(s)
| Token::Bin(s) => write!(f, "{}", s),
| Token::Bin(s)
| Token::String(s) => write!(f, "{}", s),
Token::Punct(c) | Token::Error(c) => write!(f, "{}", c),
}
}
}

fn lexer<'src>(
) -> impl Parser<'src, &'src str, Vec<Spanned<Token<'src>>>, extra::Err<Rich<'src, char>>> {
let validate_end = any()
.or_not()
.rewind()
.validate(|c: Option<char>, e, emitter| {
if let Some(c) = c {
if !(c.is_whitespace() || "(){[]<>:=,/}".contains(c)) {
emitter.emit(Rich::custom(e.span(), "invalid token"));
}
}
});
let keyword = just("#")
.ignore_then(keyword("define").or(keyword("include")))
.ignore_then(choice((keyword("define"), keyword("include"))))
.then_ignore(validate_end)
.map(Token::Keyword);

let ident = text::ident().map(Token::Ident);
let ident = text::ident().then_ignore(validate_end).map(Token::Ident);

let punct = one_of("(){}[]<>:=,").map(Token::Punct);

let hex = just("0x")
.ignore_then(text::digits(16))
.to_slice()
.then_ignore(validate_end)
.map(Token::Hex);

let bin = just("0b")
.ignore_then(text::digits(2))
.then_ignore(validate_end)
.to_slice()
.map(Token::Bin);

let dec = text::digits(10).to_slice().map(Token::Dec);
let dec = text::digits(10)
.then_ignore(validate_end)
.to_slice()
.map(Token::Dec);

let string = just("\"")
.ignore_then(any().and_is(just("\"").not()).repeated().to_slice())
.then_ignore(just("\""))
.map(Token::String);

let token = choice((keyword, ident, punct, hex, bin, dec));
let token = choice((keyword, ident, punct, hex, bin, dec, string));

// comments
let single_line_comment = just("//")
Expand Down Expand Up @@ -102,11 +125,11 @@ mod tests {
};
}

// macro_rules! assert_err {
// ($input:expr, $expected:expr) => {
// assert_eq!(lexer().parse($input).into_result(), Err(vec![$expected]),);
// };
// }
macro_rules! assert_err {
($input:expr, $expected:expr) => {
assert_eq!(lexer().parse($input).into_result(), Err(vec![$expected]),);
};
}

#[test]
fn lex_keyword() {
Expand All @@ -125,10 +148,10 @@ mod tests {
(Token::Ident("foo"), SimpleSpan::new(0, 3)),
(Token::Ident("bar"), SimpleSpan::new(4, 7))
);
// assert_err!(
// "foo#define",
// Rich::custom(SimpleSpan::new(0, 10), "invalid token")
// );
assert_err!(
"foo#define",
Rich::custom(SimpleSpan::new(3, 3), "invalid token")
);
}

#[test]
Expand All @@ -155,7 +178,7 @@ mod tests {
fn lex_hex() {
assert_ok!("0x0", (Token::Hex("0x0"), SimpleSpan::new(0, 3)));
assert_ok!("0x123", (Token::Hex("0x123"), SimpleSpan::new(0, 5)));
// assert_err!("0x", SimpleSpan::new(2, 2));
assert_err!("0x0x", Rich::custom(SimpleSpan::new(3, 3), "invalid token"));
}

#[test]
Expand All @@ -169,4 +192,14 @@ mod tests {
assert_ok!("0b101", (Token::Bin("0b101"), SimpleSpan::new(0, 5)));
assert_ok!("0b0", (Token::Bin("0b0"), SimpleSpan::new(0, 3)));
}

#[test]
fn lex_string() {
assert_ok!("\"\"", (Token::String(""), SimpleSpan::new(0, 2)));
assert_ok!("\"foo\"", (Token::String("foo"), SimpleSpan::new(0, 5)));
assert_ok!(
"\"foo bar\"",
(Token::String("foo bar"), SimpleSpan::new(0, 9))
);
}
}
128 changes: 74 additions & 54 deletions crates/ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,21 @@ impl<'tokens, 'src: 'tokens, P, T> Parser<'tokens, 'src, T> for P where
}

fn root<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Root<'src>> {
definition()
root_section()
.repeated()
.collect::<Vec<_>>()
.map(|defs| ast::Root(defs.into_boxed_slice()))
}

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, include))
}

fn definition<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definition<'src>> {
just(Keyword("define")).ignore_then(choice((
r#macro(),
Expand All @@ -75,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 @@ -109,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 @@ -204,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 @@ -232,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 @@ -261,17 +262,20 @@ fn constant<'tokens, 'src: 'tokens>() -> impl Parser<'tokens, 'src, ast::Definit

just(Ident("constant"))
.ignore_then(ident())
.then_ignore(just(Punct('=')))
.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 Down Expand Up @@ -322,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 @@ -406,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 @@ -485,6 +486,25 @@ mod tests {
assert_err!(code(), vec![Hex("0x0")], "odd length");
}

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

assert_ok!(
root_section(),
vec![Keyword("include"), String("test")],
ast::RootSection::Include(("test", span))
);
assert_ok!(
root_section(),
vec![Keyword("define"), Ident("constant"), Ident("TEST"), Punct('='), Hex("0x1")],
ast::RootSection::Definition(ast::Definition::Constant {
name: ("TEST", span),
expr: (ast::ConstExpr::Value(uint!(1_U256)), span)
})
);
}

#[test]
fn parse_macro() {
let span: Span = SimpleSpan::new(0, 0);
Expand Down
Loading

0 comments on commit a20a79c

Please sign in to comment.