From d15072d45bc5b52bdabc1c558980f9522c58e080 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sat, 15 Sep 2018 08:15:24 +0200 Subject: [PATCH 1/2] Yul's grammar --- Cargo.toml | 4 +++ examples/example1.yul | 5 +++ src/main.rs | 46 ++++++++++++++++++++++++++ src/yul.pest | 75 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 examples/example1.yul create mode 100644 src/main.rs create mode 100644 src/yul.pest diff --git a/Cargo.toml b/Cargo.toml index 8516e9e..3c50df0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,7 @@ license = "GPL-3.0" readme = "README.md" description = "Yultsur (or Yülçür) is a toolkit for Yul." keywords = ["yul", "ethereum", "solidity"] + +[dependencies] +pest = "^1.0" +pest_derive = "^1.0" diff --git a/examples/example1.yul b/examples/example1.yul new file mode 100644 index 0000000..9481fb1 --- /dev/null +++ b/examples/example1.yul @@ -0,0 +1,5 @@ +{ + function power() -> result:u256 + { + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..af3de5e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,46 @@ +extern crate pest; +#[macro_use] +extern crate pest_derive; + +use pest::Parser; + +#[derive(Parser)] +#[grammar = "yul.pest"] +struct BlockParser; + +use std::fs::File; +use std::io::prelude::*; + +fn file_to_string(path: &str) -> String { + let mut file = File::open(path).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + content +} + +fn main() { + smoke(); +} + +fn smoke() { + let source = file_to_string("examples/example1.yul"); + let pairs = BlockParser::parse(Rule::block, &source).unwrap_or_else(|e| panic!("{}", e)); + + for pair in pairs { + + let span = pair.clone().into_span(); + println!("Rule: {:?}", pair.as_rule()); + println!("Span: {:?}", span); + println!("Text: {}", span.as_str()); + + for inner_pair in pair.into_inner() { + let inner_span = inner_pair.clone().into_span(); + match inner_pair.as_rule() { + Rule::block => println!("Block: {}", inner_span.as_str()), + Rule::statement => println!("Statement: {}", inner_span.as_str()), + Rule::function_definition => println!("FunctionDefinition: {}", inner_span.as_str()), + _ => unreachable!() + }; + } + } +} diff --git a/src/yul.pest b/src/yul.pest new file mode 100644 index 0000000..532a18c --- /dev/null +++ b/src/yul.pest @@ -0,0 +1,75 @@ +block = { soi? ~ "{" ~ (statement)* ~ "}" ~ eoi? } + +statement = { + (block + | function_definition + | variable_declaration + | assignment + | expression + | switch + | for_loop + | if_statement + | break_continue) +} + +function_definition = { "function" ~ identifier ~ "(" ~ (typed_identifier_list)? ~ ")" ~ ("->" ~ typed_identifier_list)? ~ block } + +variable_declaration = { "let" ~ typed_identifier_list ~ (":=" ~ expression)? } + +assignment = { identifier_list ~ ":=" ~ expression } + +expression = { function_call | identifier | literal } + +if_statement = { "if" ~ expression ~ block } + +switch = { "switch" ~ expression ~ ((case)+ ~ (default)? | default) } + +case = { "case" ~ literal ~ block } + +default = { "default" ~ block } + +for_loop = { "for" ~ block ~ expression ~ block ~ block } + +break_continue = { "break" | "continue" } + +function_call = { identifier ~ "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" } + +identifier = @ { alpha ~ (alpha | digit)* } + +identifier_list = { identifier ~ ("," ~ identifier)* } + +type_name = { identifier | builtin_typename } + +builtin_typename = { "bool" | "u8" | "u32" | "u64" | "u128" | "u256" | "s8" | "s32" | "s64" | "s128" | "s256" } // FIXME: this can probably be simplified + +typed_identifier_list = { identifier ~ ":" ~ type_name ~ ("," ~ identifier ~ ":" ~ type_name)* } + +literal = { + (number_literal + | string_literal + | hex_literal + | true_literal + | false_literal) + ~ ":" + ~ type_name +} + +number_literal = { hex_number | decimal_number } + +hex_literal = { "hex" ~ ("\"" ~ (('0'..'9' | 'a'..'f' | 'A'..'F'){2})* ~ "\"" | "\'" ~ (('0'..'9' | 'a'..'f' | 'A'..'F'){2})* ~ "\'") } // FIXME: can be refactored into "hex_byte" + +string_literal = { "\"" ~ ((!("\\" | "\n" | "\r" | "\"") ~ any) | "\\" ~ (!( "\n" ) ~ any))* ~ "\"" } + +true_literal = { "true" } + +false_literal = { "false" } + +hex_number = @ { "0x" ~ ('0'..'9' | 'a'..'f' | 'A'..'F')+ } // TODO: this may need to be flagged atomic to avoid something like "0x deadbeef" + +decimal_number = @ { digit+ } + +alpha = _ { 'a'..'z' | 'A'..'Z' } + +digit = _ { '0'..'9' } + +whitespace = _ { " " | "\t" | "\n" } From c6029d92c3918ebac15d54ee07baca1c858eaacb Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 18 Sep 2018 12:25:16 +0200 Subject: [PATCH 2/2] Empty block test using AST --- examples/empty_block.yul | 1 + ...mple1.yul => power_function_signature.yul} | 0 src/lib.rs | 5 ++ src/main.rs | 46 ---------------- src/yul.pest | 22 +++++--- src/yul_parser.rs | 54 +++++++++++++++++++ 6 files changed, 75 insertions(+), 53 deletions(-) create mode 100644 examples/empty_block.yul rename examples/{example1.yul => power_function_signature.yul} (100%) delete mode 100644 src/main.rs create mode 100644 src/yul_parser.rs diff --git a/examples/empty_block.yul b/examples/empty_block.yul new file mode 100644 index 0000000..6f31cf5 --- /dev/null +++ b/examples/empty_block.yul @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/examples/example1.yul b/examples/power_function_signature.yul similarity index 100% rename from examples/example1.yul rename to examples/power_function_signature.yul diff --git a/src/lib.rs b/src/lib.rs index 7afad6a..d1414ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,7 @@ +extern crate pest; +#[macro_use] +extern crate pest_derive; + pub mod yul; pub mod validator; +pub mod yul_parser; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index af3de5e..0000000 --- a/src/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate pest; -#[macro_use] -extern crate pest_derive; - -use pest::Parser; - -#[derive(Parser)] -#[grammar = "yul.pest"] -struct BlockParser; - -use std::fs::File; -use std::io::prelude::*; - -fn file_to_string(path: &str) -> String { - let mut file = File::open(path).unwrap(); - let mut content = String::new(); - file.read_to_string(&mut content).unwrap(); - content -} - -fn main() { - smoke(); -} - -fn smoke() { - let source = file_to_string("examples/example1.yul"); - let pairs = BlockParser::parse(Rule::block, &source).unwrap_or_else(|e| panic!("{}", e)); - - for pair in pairs { - - let span = pair.clone().into_span(); - println!("Rule: {:?}", pair.as_rule()); - println!("Span: {:?}", span); - println!("Text: {}", span.as_str()); - - for inner_pair in pair.into_inner() { - let inner_span = inner_pair.clone().into_span(); - match inner_pair.as_rule() { - Rule::block => println!("Block: {}", inner_span.as_str()), - Rule::statement => println!("Statement: {}", inner_span.as_str()), - Rule::function_definition => println!("FunctionDefinition: {}", inner_span.as_str()), - _ => unreachable!() - }; - } - } -} diff --git a/src/yul.pest b/src/yul.pest index 532a18c..3a90fd6 100644 --- a/src/yul.pest +++ b/src/yul.pest @@ -5,14 +5,15 @@ statement = { | function_definition | variable_declaration | assignment + | if_statement + | for_loop | expression | switch - | for_loop - | if_statement - | break_continue) + | break_statement + | continue_statement) } -function_definition = { "function" ~ identifier ~ "(" ~ (typed_identifier_list)? ~ ")" ~ ("->" ~ typed_identifier_list)? ~ block } +function_definition = { "function" ~ identifier ~ "(" ~ (typed_parameter_list)? ~ ")" ~ ("->" ~ typed_identifier_list)? ~ block } variable_declaration = { "let" ~ typed_identifier_list ~ (":=" ~ expression)? } @@ -30,7 +31,9 @@ default = { "default" ~ block } for_loop = { "for" ~ block ~ expression ~ block ~ block } -break_continue = { "break" | "continue" } +break_statement = { "break" } + +continue_statement = { "continue" } function_call = { identifier ~ "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" } @@ -38,11 +41,16 @@ identifier = @ { alpha ~ (alpha | digit)* } identifier_list = { identifier ~ ("," ~ identifier)* } -type_name = { identifier | builtin_typename } +type_name = { builtin_typename | identifier } builtin_typename = { "bool" | "u8" | "u32" | "u64" | "u128" | "u256" | "s8" | "s32" | "s64" | "s128" | "s256" } // FIXME: this can probably be simplified -typed_identifier_list = { identifier ~ ":" ~ type_name ~ ("," ~ identifier ~ ":" ~ type_name)* } +typed_parameter_list = { typed_identifier ~ ("," ~ typed_identifier)* } +typed_identifier_list = { typed_identifier ~ ("," ~ typed_identifier)* } +untyped_parameter_list = { identifier ~ ("," ~ identifier)* } +untyped_identifier_list = { identifier ~ ("," ~ identifier)* } + +typed_identifier = { identifier ~ ":" ~ type_name } literal = { (number_literal diff --git a/src/yul_parser.rs b/src/yul_parser.rs new file mode 100644 index 0000000..8ac28cd --- /dev/null +++ b/src/yul_parser.rs @@ -0,0 +1,54 @@ +use yul::*; + +use pest::Parser; +use pest::iterators::Pair; + +#[derive(Parser)] +#[grammar = "yul.pest"] +struct BlockParser; + +use std::fs::File; +use std::io::prelude::*; + +fn file_to_string(path: &str) -> String { + let mut file = File::open(path).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + content +} + +impl Block { + fn from(pair: Pair) -> Block { + let mut statements: Vec = vec![]; + for p in pair.into_inner() { + match p.as_rule() { + Rule::statement => { + //statements.push(Statement::from(p)); + } + c => panic!("{:?}", c), + } + } + Block { statements } + } +} + +pub fn parse_block(source: &str) -> Block { + let mut pairs = BlockParser::parse(Rule::block, &source).unwrap(); + Block::from(pairs.next().unwrap()) +} + +#[cfg(test)] +mod tests { +use super::*; + + #[test] + fn empty_block() { + let source = file_to_string("examples/empty_block.yul"); + let block = parse_block(&source); + assert_eq!( + source, + block.to_string() + ); + } + +}