From 9163b837e490f788c6e51a23c6d836b027131c32 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Fri, 24 Jan 2020 18:31:00 +0800 Subject: [PATCH 01/35] Object added ContractDef -> Object --- src/yul.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/yul.rs b/src/yul.rs index 20a8c24..d184634 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -39,6 +39,12 @@ pub struct FunctionCall { pub arguments: Vec, } +#[derive(Hash, Clone, PartialEq, Debug)] +pub struct Object { + pub name: Identifier, + pub block: Block, +} + #[derive(Hash, Clone, PartialEq, Debug)] pub struct FunctionDefinition { pub name: Identifier, @@ -95,6 +101,7 @@ pub struct ForLoop { #[derive(Hash, Clone, PartialEq, Debug)] pub enum Statement { Block(Block), + Object(Object), FunctionDefinition(FunctionDefinition), VariableDeclaration(VariableDeclaration), Assignment(Assignment), @@ -181,6 +188,12 @@ impl fmt::Display for FunctionCall { } } +impl fmt::Display for Object { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "object \"{}\" {}", self.name, self.block) + } +} + impl fmt::Display for FunctionDefinition { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "function {}(", self.name)); @@ -307,6 +320,7 @@ impl fmt::Display for Statement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Statement::Block(ref block) => write!(f, "{}", block), + Statement::Object(ref contract) => write!(f, "{}", contract), Statement::FunctionDefinition(ref function) => write!(f, "{}", function), Statement::VariableDeclaration(ref variabledeclaration) => { write!(f, "{}", variabledeclaration) @@ -569,6 +583,21 @@ mod tests { ); } + #[test] + fn contractdefinition_basic() { + assert_eq!( + Object { + name: Identifier { + identifier: "Name".to_string(), + yultype: None, + }, + block: Block { statements: vec![] }, + }.to_string(), + "object \"Name\" { }" + ); + } + + #[test] fn functiondefinition_basic() { assert_eq!( From 704c531d08894b4604500e7e5605b5a2de13ff9b Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sat, 8 Feb 2020 11:53:37 +0800 Subject: [PATCH 02/35] Code added --- src/yul.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/yul.rs b/src/yul.rs index d184634..a6d3658 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -45,6 +45,11 @@ pub struct Object { pub block: Block, } +#[derive(Hash, Clone, PartialEq, Debug)] +pub struct Code { + pub block: Block, +} + #[derive(Hash, Clone, PartialEq, Debug)] pub struct FunctionDefinition { pub name: Identifier, @@ -102,6 +107,7 @@ pub struct ForLoop { pub enum Statement { Block(Block), Object(Object), + Code(Code), FunctionDefinition(FunctionDefinition), VariableDeclaration(VariableDeclaration), Assignment(Assignment), @@ -189,11 +195,17 @@ impl fmt::Display for FunctionCall { } impl fmt::Display for Object { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "object \"{}\" {}", self.name, self.block) } } +impl fmt::Display for Code { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "code {}", self.block) + } +} + impl fmt::Display for FunctionDefinition { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "function {}(", self.name)); @@ -320,7 +332,8 @@ impl fmt::Display for Statement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Statement::Block(ref block) => write!(f, "{}", block), - Statement::Object(ref contract) => write!(f, "{}", contract), + Statement::Object(ref object) => write!(f, "{}", object), + Statement::Code(ref code) => write!(f, "{}", code), Statement::FunctionDefinition(ref function) => write!(f, "{}", function), Statement::VariableDeclaration(ref variabledeclaration) => { write!(f, "{}", variabledeclaration) @@ -584,7 +597,7 @@ mod tests { } #[test] - fn contractdefinition_basic() { + fn object_basic() { assert_eq!( Object { name: Identifier { @@ -597,6 +610,15 @@ mod tests { ); } + #[test] + fn code_basic() { + assert_eq!( + Code { + block: Block { statements: vec![] }, + }.to_string(), + "code { }" + ); + } #[test] fn functiondefinition_basic() { From 628eb19092a453ee03059ce7ddf71c44ff068660 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sat, 8 Feb 2020 14:22:04 +0800 Subject: [PATCH 03/35] fix --- src/yul.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/yul.rs b/src/yul.rs index a6d3658..36eb847 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -42,7 +42,8 @@ pub struct FunctionCall { #[derive(Hash, Clone, PartialEq, Debug)] pub struct Object { pub name: Identifier, - pub block: Block, + pub code: Code, + pub objects: Vec, } #[derive(Hash, Clone, PartialEq, Debug)] @@ -196,7 +197,7 @@ impl fmt::Display for FunctionCall { impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "object \"{}\" {}", self.name, self.block) + write!(f, "object \"{}\" {{ {} }}", self.name, self.code) } } @@ -604,9 +605,10 @@ mod tests { identifier: "Name".to_string(), yultype: None, }, - block: Block { statements: vec![] }, + code: Code { block: Block { statements: vec![] } }, + objects: vec![] }.to_string(), - "object \"Name\" { }" + "object \"Name\" { code { } }" ); } From db1336ac92b85ae6d07af63918e0bfb1afb71f4a Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sun, 9 Feb 2020 23:35:02 +0800 Subject: [PATCH 04/35] Nested objects in to_string --- src/yul.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/yul.rs b/src/yul.rs index 36eb847..0a787fb 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -197,7 +197,7 @@ impl fmt::Display for FunctionCall { impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "object \"{}\" {{ {} }}", self.name, self.code) + write!(f, "object \"{}\" {{ {} {} }}", self.name, self.code, self.objects.iter().map(|o| o.to_string()).collect::>().join(" ")) } } @@ -606,9 +606,15 @@ mod tests { yultype: None, }, code: Code { block: Block { statements: vec![] } }, - objects: vec![] + objects: vec![ + Object { + name: Identifier { identifier: "Test".to_string(), yultype: None }, + code: Code { block: Block { statements: vec![] } }, + objects: vec![] + } + ] }.to_string(), - "object \"Name\" { code { } }" + "object \"Name\" { code { } object \"Test\" { code { } } }" ); } From e34a8cfc9a27025f71ec94ad37659762a7781616 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 24 Feb 2020 23:04:57 +0800 Subject: [PATCH 05/35] Shorthand macros --- src/lib.rs | 1 + src/shorthand.rs | 222 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 src/shorthand.rs diff --git a/src/lib.rs b/src/lib.rs index 7afad6a..d7727c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod yul; pub mod validator; +pub mod shorthand; diff --git a/src/shorthand.rs b/src/shorthand.rs new file mode 100644 index 0000000..3d28d85 --- /dev/null +++ b/src/shorthand.rs @@ -0,0 +1,222 @@ +use crate::yul; + +/// Creates a Yul literal. +macro_rules! literal { + {[$i:ident]} => {$i}; + {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; +} + +/// Creates a Yul literal expression. +macro_rules! literal_expression { + {$($tts:tt)*} => {yul::Expression::Literal(literal!($($tts)*))}; +} + +/// Creates a Yul identifier. +macro_rules! identifier { + {[$i:ident]} => {$i}; + {$l:ident} => {yul::Identifier { identifier: stringify!($l).to_string(), yultype: None }}; +} + +/// Creates a Yul identifier expression. +macro_rules! identifier_expression { + {$($tts:tt)*} => {yul::Expression::Identifier(identifier!($($tts)*))}; +} + +/// Creates a Yul function call. +macro_rules! function_call { + {[$i:ident]} => {$i}; + {$name:ident($($arg:tt),*)} => { + yul::FunctionCall { + identifier: identifier!{$name}, + arguments: { + let mut args = vec![]; + $( + args.push(expression!{$arg}); + )* + args + } + } + }; +} + +/// Creates a function call expression. +macro_rules! function_call_expression { + {$($tts:tt)*} => {yul::Expression::FunctionCall(function_call!($($tts)*))}; +} + +/// Creates a function call statement. +macro_rules! function_call_statement { + {$($tts:tt)*} => {yul::Statement::Expression( + yul::Expression::FunctionCall(function_call!($($tts)*)) + )}; +} + +/// Creates a Yul expression. +macro_rules! expression { + {[$i:ident]} => {$i}; + {$l:literal} => {literal_expression!{$l}}; + {$i:ident} => {identifier_expression!{$i}}; + {$($tts:tt)*} => {function_call_expression!($($tts)*)}; +} + +/// Creates a Yul variable declaration statement. +macro_rules! variable_declaration { + {let $name:ident := $($tts:tt)+} => { + yul::Statement::VariableDeclaration(yul::VariableDeclaration { + identifiers: vec![identifier!{$name}], + expression: Some(expression!{$($tts)*}) + }) + }; +} + +/// Creates a Yul assignment statement. +macro_rules! assignment { + {$name:ident := $($tts:tt)+} => { + yul::Statement::Assignment(yul::Assignment { + identifiers: vec![identifier!{$name}], + expression: expression!{$($tts)*} + }) + }; +} + +#[cfg(test)] +mod tests { + use crate::yul; + + #[test] + fn test_literal_string () { + assert_eq!( + literal!{"foo"}.to_string(), + r#""foo""# + ) + } + + #[test] + fn test_literal_num () { + assert_eq!( + literal!{42}.to_string(), + "42" + ) + } + + #[test] + fn test_literal_node () { + let foo = yul::Literal { literal: r#""bar""#.to_string(), yultype: None }; + assert_eq!( + literal!{[foo]}.to_string(), + r#""bar""# + ) + } + + #[test] + fn test_literal_expression_string () { + assert_eq!( + literal_expression!{"foo"}.to_string(), + r#""foo""# + ) + } + + #[test] + fn test_expression_literal () { + assert_eq!( + expression!{"foo"}.to_string(), + r#""foo""# + ) + } + + #[test] + fn test_expression_node () { + let node = literal_expression!("foobar"); + + assert_eq!( + expression!{[node]}.to_string(), + r#""foobar""# + ) + } + + #[test] + fn test_expression_identifier () { + assert_eq!( + expression!{foo}.to_string(), + "foo" + ) + } + + #[test] + fn test_function_call () { + assert_eq!( + function_call!{foo("string", bar, 42)}.to_string(), + r#"foo("string", bar, 42)"# + ) + } + + #[test] + fn test_function_call_node () { + let node = literal_expression!("foobar"); + + assert_eq!( + function_call!{foo("string", bar, 42, [node])}.to_string(), + r#"foo("string", bar, 42, "foobar")"# + ) + } + + #[test] + fn test_function_call_expression () { + assert_eq!( + function_call_expression!{foo("string", bar, 42)}.to_string(), + r#"foo("string", bar, 42)"# + ) + } + + #[test] + fn test_function_call_statement () { + assert_eq!( + function_call_statement!{foo("string", bar, 42)}.to_string(), + r#"foo("string", bar, 42)"# + ) + } + + #[test] + fn test_expression_function_call () { + let node = literal_expression!("foobar"); + + assert_eq!( + expression!{foo("string", bar, 42, [node])}.to_string(), + r#"foo("string", bar, 42, "foobar")"# + ) + } + + #[test] + fn test_variable_declaration () { + assert_eq!( + variable_declaration!{let foo := 42}.to_string(), + "let foo := 42" + ) + } + + #[test] + fn test_variable_declaration_function () { + assert_eq!( + variable_declaration!{let foo := foo("bar", 42)}.to_string(), + r#"let foo := foo("bar", 42)"# + ) + } + + #[test] + fn test_variable_declaration_function_nested_node () { + let food = function_call_expression!(food("taco", apple)); + + assert_eq!( + variable_declaration!{let foo := foo("bar", [food])}.to_string(), + r#"let foo := foo("bar", food("taco", apple))"# + ) + } + + #[test] + fn test_assignment () { + assert_eq!( + assignment!{foo := 42}.to_string(), + "foo := 42" + ) + } +} From be329f2296da8d485aa294a5f4a4f30cc1dd086d Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 24 Feb 2020 23:11:13 +0800 Subject: [PATCH 06/35] Macro export --- src/shorthand.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 3d28d85..fc9434e 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -1,28 +1,33 @@ use crate::yul; /// Creates a Yul literal. +#[macro_export] macro_rules! literal { {[$i:ident]} => {$i}; {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; } /// Creates a Yul literal expression. +#[macro_export] macro_rules! literal_expression { {$($tts:tt)*} => {yul::Expression::Literal(literal!($($tts)*))}; } /// Creates a Yul identifier. +#[macro_export] macro_rules! identifier { {[$i:ident]} => {$i}; {$l:ident} => {yul::Identifier { identifier: stringify!($l).to_string(), yultype: None }}; } /// Creates a Yul identifier expression. +#[macro_export] macro_rules! identifier_expression { {$($tts:tt)*} => {yul::Expression::Identifier(identifier!($($tts)*))}; } /// Creates a Yul function call. +#[macro_export] macro_rules! function_call { {[$i:ident]} => {$i}; {$name:ident($($arg:tt),*)} => { @@ -40,11 +45,13 @@ macro_rules! function_call { } /// Creates a function call expression. +#[macro_export] macro_rules! function_call_expression { {$($tts:tt)*} => {yul::Expression::FunctionCall(function_call!($($tts)*))}; } /// Creates a function call statement. +#[macro_export] macro_rules! function_call_statement { {$($tts:tt)*} => {yul::Statement::Expression( yul::Expression::FunctionCall(function_call!($($tts)*)) @@ -52,6 +59,7 @@ macro_rules! function_call_statement { } /// Creates a Yul expression. +#[macro_export] macro_rules! expression { {[$i:ident]} => {$i}; {$l:literal} => {literal_expression!{$l}}; @@ -60,6 +68,7 @@ macro_rules! expression { } /// Creates a Yul variable declaration statement. +#[macro_export] macro_rules! variable_declaration { {let $name:ident := $($tts:tt)+} => { yul::Statement::VariableDeclaration(yul::VariableDeclaration { @@ -70,6 +79,7 @@ macro_rules! variable_declaration { } /// Creates a Yul assignment statement. +#[macro_export] macro_rules! assignment { {$name:ident := $($tts:tt)+} => { yul::Statement::Assignment(yul::Assignment { From 4e7d092a31f7f13bd9832883d3ec6b757f0901b2 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 08:38:44 +0800 Subject: [PATCH 07/35] Nested function calls. --- src/shorthand.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index fc9434e..be69748 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -62,8 +62,10 @@ macro_rules! function_call_statement { #[macro_export] macro_rules! expression { {[$i:ident]} => {$i}; + {($($tts:tt)+)} => {expression!($($tts)*)}; {$l:literal} => {literal_expression!{$l}}; {$i:ident} => {identifier_expression!{$i}}; + // TODO: Match function call. {$($tts:tt)*} => {function_call_expression!($($tts)*)}; } @@ -222,6 +224,14 @@ mod tests { ) } + #[test] + fn test_variable_declaration_function_nested_raw () { + assert_eq!( + variable_declaration!{let foo := foo("bar", (food("taco", apple)))}.to_string(), + r#"let foo := foo("bar", food("taco", apple))"# + ) + } + #[test] fn test_assignment () { assert_eq!( From f35fa3a4be497a58773d239426a7e572a35b93c9 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 12:11:54 +0800 Subject: [PATCH 08/35] Fix --- src/shorthand.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index be69748..2695ffd 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -72,7 +72,7 @@ macro_rules! expression { /// Creates a Yul variable declaration statement. #[macro_export] macro_rules! variable_declaration { - {let $name:ident := $($tts:tt)+} => { + {let $name:tt := $($tts:tt)+} => { yul::Statement::VariableDeclaration(yul::VariableDeclaration { identifiers: vec![identifier!{$name}], expression: Some(expression!{$($tts)*}) @@ -83,7 +83,7 @@ macro_rules! variable_declaration { /// Creates a Yul assignment statement. #[macro_export] macro_rules! assignment { - {$name:ident := $($tts:tt)+} => { + {$name:tt := $($tts:tt)+} => { yul::Statement::Assignment(yul::Assignment { identifiers: vec![identifier!{$name}], expression: expression!{$($tts)*} @@ -224,6 +224,16 @@ mod tests { ) } + #[test] + fn test_variable_declaration_identifier_node () { + let foo = identifier!(foo); + + assert_eq!( + variable_declaration!{let [foo] := bar("baz")}.to_string(), + r#"let foo := bar("baz")"# + ) + } + #[test] fn test_variable_declaration_function_nested_raw () { assert_eq!( From 433bad6eba785768de3501a082630b3f614ab3ba Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 17:35:01 +0800 Subject: [PATCH 09/35] Function definitions --- src/shorthand.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 5 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 2695ffd..e97764b 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -61,12 +61,11 @@ macro_rules! function_call_statement { /// Creates a Yul expression. #[macro_export] macro_rules! expression { - {[$i:ident]} => {$i}; {($($tts:tt)+)} => {expression!($($tts)*)}; + {[$i:ident]} => {$i}; {$l:literal} => {literal_expression!{$l}}; {$i:ident} => {identifier_expression!{$i}}; - // TODO: Match function call. - {$($tts:tt)*} => {function_call_expression!($($tts)*)}; + {$name:ident($($arg:tt)*)} => {function_call_expression! {$name($($arg)+)}}; } /// Creates a Yul variable declaration statement. @@ -83,10 +82,50 @@ macro_rules! variable_declaration { /// Creates a Yul assignment statement. #[macro_export] macro_rules! assignment { - {$name:tt := $($tts:tt)+} => { + {$name:tt := $($expr:tt)+} => { yul::Statement::Assignment(yul::Assignment { identifiers: vec![identifier!{$name}], - expression: expression!{$($tts)*} + expression: expression!{$($expr)*} + }) + }; +} + +/// Creates a Yul statement. +macro_rules! statement { + {($($tts:tt)+)} => {statement!($($tts)*)}; + {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)+)}}; + {let $name:tt := $($expr:tt)+} => {variable_declaration!{let $name := $($expr)+}}; + {$name:tt := $($expr:tt)+} => {assignment!{$name := $($expr)+}}; +} + +/// Creates a Yul block from zero or more statements. +macro_rules! block { + {$($statement:tt)*} => { + yul::Block { + statements: { + let mut statements = vec![]; + $( + statements.push(statement!{$statement}); + )* + statements + } + } + }; +} + +macro_rules! function_definition { + {function $name:ident($($param:tt),*) -> $returns:ident {$($statement:tt)*}} => { + yul::Statement::FunctionDefinition(yul::FunctionDefinition { + name: identifier!{$name}, + parameters: { + let mut params = vec![]; + $( + params.push(identifier!{$param}); + )* + params + }, + returns: vec![identifier!{$returns}], + block: block!{$($statement)*}, }) }; } @@ -249,4 +288,63 @@ mod tests { "foo := 42" ) } + + #[test] + fn test_statement_function() { + let _42 = expression!{42}; + let biz = function_call_expression!{biz(bit, coin, [_42])}; + assert_eq!( + statement!{ + bar( + "ding", + dong, + [biz], + (farm(cow, "sheep")) + ) + }.to_string(), + r#"bar("ding", dong, biz(bit, coin, 42), farm(cow, "sheep"))"# + ) + } + + #[test] + fn test_statement_variable_declaration() { + assert_eq!( + statement!{let foo := bar("ding", dong)}.to_string(), + r#"let foo := bar("ding", dong)"# + ) + } + + #[test] + fn test_statement_assignment() { + assert_eq!( + statement!{foo := 42}.to_string(), + "foo := 42" + ) + } + + #[test] + fn test_block() { + assert_eq!( + block! { + (let foo := 42) + (bar(foo)) + }.to_string(), + "{ let foo := 42 bar(foo) }" + ) + } + + #[test] + fn function_definition() { + let bit = identifier!{bit}; + + assert_eq!( + function_definition! { + function foo([bit], coin) -> bar { + (let baz := add(bit, coin)) + (bar := hello_world(baz, "hi")) + } + }.to_string(), + r#"function foo(bit, coin) -> bar { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# + ) + } } From 3123203c709751e27d21af619e8ae8ba44933a6e Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 17:36:55 +0800 Subject: [PATCH 10/35] fmt shorthand --- src/shorthand.rs | 127 ++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 74 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index e97764b..3e48732 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -135,173 +135,153 @@ mod tests { use crate::yul; #[test] - fn test_literal_string () { - assert_eq!( - literal!{"foo"}.to_string(), - r#""foo""# - ) + fn test_literal_string() { + assert_eq!(literal! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_literal_num () { - assert_eq!( - literal!{42}.to_string(), - "42" - ) + fn test_literal_num() { + assert_eq!(literal! {42}.to_string(), "42") } #[test] - fn test_literal_node () { - let foo = yul::Literal { literal: r#""bar""#.to_string(), yultype: None }; - assert_eq!( - literal!{[foo]}.to_string(), - r#""bar""# - ) + fn test_literal_node() { + let foo = yul::Literal { + literal: r#""bar""#.to_string(), + yultype: None, + }; + assert_eq!(literal! {[foo]}.to_string(), r#""bar""#) } #[test] - fn test_literal_expression_string () { - assert_eq!( - literal_expression!{"foo"}.to_string(), - r#""foo""# - ) + fn test_literal_expression_string() { + assert_eq!(literal_expression! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_expression_literal () { - assert_eq!( - expression!{"foo"}.to_string(), - r#""foo""# - ) + fn test_expression_literal() { + assert_eq!(expression! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_expression_node () { + fn test_expression_node() { let node = literal_expression!("foobar"); - assert_eq!( - expression!{[node]}.to_string(), - r#""foobar""# - ) + assert_eq!(expression! {[node]}.to_string(), r#""foobar""#) } #[test] - fn test_expression_identifier () { - assert_eq!( - expression!{foo}.to_string(), - "foo" - ) + fn test_expression_identifier() { + assert_eq!(expression! {foo}.to_string(), "foo") } #[test] - fn test_function_call () { + fn test_function_call() { assert_eq!( - function_call!{foo("string", bar, 42)}.to_string(), + function_call! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# ) } - #[test] - fn test_function_call_node () { + #[test] + fn test_function_call_node() { let node = literal_expression!("foobar"); assert_eq!( - function_call!{foo("string", bar, 42, [node])}.to_string(), + function_call! {foo("string", bar, 42, [node])}.to_string(), r#"foo("string", bar, 42, "foobar")"# ) } #[test] - fn test_function_call_expression () { + fn test_function_call_expression() { assert_eq!( - function_call_expression!{foo("string", bar, 42)}.to_string(), + function_call_expression! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# ) } #[test] - fn test_function_call_statement () { + fn test_function_call_statement() { assert_eq!( - function_call_statement!{foo("string", bar, 42)}.to_string(), + function_call_statement! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# ) } #[test] - fn test_expression_function_call () { + fn test_expression_function_call() { let node = literal_expression!("foobar"); assert_eq!( - expression!{foo("string", bar, 42, [node])}.to_string(), + expression! {foo("string", bar, 42, [node])}.to_string(), r#"foo("string", bar, 42, "foobar")"# ) } #[test] - fn test_variable_declaration () { + fn test_variable_declaration() { assert_eq!( - variable_declaration!{let foo := 42}.to_string(), + variable_declaration! {let foo := 42}.to_string(), "let foo := 42" ) } #[test] - fn test_variable_declaration_function () { + fn test_variable_declaration_function() { assert_eq!( - variable_declaration!{let foo := foo("bar", 42)}.to_string(), + variable_declaration! {let foo := foo("bar", 42)}.to_string(), r#"let foo := foo("bar", 42)"# ) } #[test] - fn test_variable_declaration_function_nested_node () { + fn test_variable_declaration_function_nested_node() { let food = function_call_expression!(food("taco", apple)); assert_eq!( - variable_declaration!{let foo := foo("bar", [food])}.to_string(), + variable_declaration! {let foo := foo("bar", [food])}.to_string(), r#"let foo := foo("bar", food("taco", apple))"# ) } #[test] - fn test_variable_declaration_identifier_node () { + fn test_variable_declaration_identifier_node() { let foo = identifier!(foo); assert_eq!( - variable_declaration!{let [foo] := bar("baz")}.to_string(), + variable_declaration! {let [foo] := bar("baz")}.to_string(), r#"let foo := bar("baz")"# ) } #[test] - fn test_variable_declaration_function_nested_raw () { + fn test_variable_declaration_function_nested_raw() { assert_eq!( - variable_declaration!{let foo := foo("bar", (food("taco", apple)))}.to_string(), + variable_declaration! {let foo := foo("bar", (food("taco", apple)))}.to_string(), r#"let foo := foo("bar", food("taco", apple))"# ) } #[test] - fn test_assignment () { - assert_eq!( - assignment!{foo := 42}.to_string(), - "foo := 42" - ) + fn test_assignment() { + assert_eq!(assignment! {foo := 42}.to_string(), "foo := 42") } #[test] fn test_statement_function() { - let _42 = expression!{42}; - let biz = function_call_expression!{biz(bit, coin, [_42])}; + let _42 = expression! {42}; + let biz = function_call_expression! {biz(bit, coin, [_42])}; assert_eq!( - statement!{ + statement! { bar( "ding", dong, [biz], (farm(cow, "sheep")) ) - }.to_string(), + } + .to_string(), r#"bar("ding", dong, biz(bit, coin, 42), farm(cow, "sheep"))"# ) } @@ -309,17 +289,14 @@ mod tests { #[test] fn test_statement_variable_declaration() { assert_eq!( - statement!{let foo := bar("ding", dong)}.to_string(), + statement! {let foo := bar("ding", dong)}.to_string(), r#"let foo := bar("ding", dong)"# ) } #[test] fn test_statement_assignment() { - assert_eq!( - statement!{foo := 42}.to_string(), - "foo := 42" - ) + assert_eq!(statement! {foo := 42}.to_string(), "foo := 42") } #[test] @@ -328,14 +305,15 @@ mod tests { block! { (let foo := 42) (bar(foo)) - }.to_string(), + } + .to_string(), "{ let foo := 42 bar(foo) }" ) } #[test] fn function_definition() { - let bit = identifier!{bit}; + let bit = identifier! {bit}; assert_eq!( function_definition! { @@ -343,7 +321,8 @@ mod tests { (let baz := add(bit, coin)) (bar := hello_world(baz, "hi")) } - }.to_string(), + } + .to_string(), r#"function foo(bit, coin) -> bar { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# ) } From ac678686eaf9986207268a6c9141c8fa4e26aa93 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 20:37:23 +0800 Subject: [PATCH 11/35] macro export --- src/shorthand.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 3e48732..0e7d2fd 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -91,6 +91,7 @@ macro_rules! assignment { } /// Creates a Yul statement. +#[macro_export] macro_rules! statement { {($($tts:tt)+)} => {statement!($($tts)*)}; {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)+)}}; @@ -98,7 +99,8 @@ macro_rules! statement { {$name:tt := $($expr:tt)+} => {assignment!{$name := $($expr)+}}; } -/// Creates a Yul block from zero or more statements. +/// Creates a Yul block. +#[macro_export] macro_rules! block { {$($statement:tt)*} => { yul::Block { @@ -113,6 +115,8 @@ macro_rules! block { }; } +/// Creates a Yul function definition. +#[macro_export] macro_rules! function_definition { {function $name:ident($($param:tt),*) -> $returns:ident {$($statement:tt)*}} => { yul::Statement::FunctionDefinition(yul::FunctionDefinition { From e5a2f6943a358c00b50cfa8ec3e8123378ceea2c Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 25 Feb 2020 20:50:40 +0800 Subject: [PATCH 12/35] func no return --- src/shorthand.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 0e7d2fd..9fd1516 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -118,7 +118,7 @@ macro_rules! block { /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { - {function $name:ident($($param:tt),*) -> $returns:ident {$($statement:tt)*}} => { + {function $name:ident($($param:tt),*) $(-> $returns:ident)? {$($statement:tt)*}} => { yul::Statement::FunctionDefinition(yul::FunctionDefinition { name: identifier!{$name}, parameters: { @@ -128,7 +128,13 @@ macro_rules! function_definition { )* params }, - returns: vec![identifier!{$returns}], + returns: { + let mut returns = vec![]; + $( + returns.push(identifier!{$returns}); + )* + returns + }, block: block!{$($statement)*}, }) }; @@ -330,4 +336,21 @@ mod tests { r#"function foo(bit, coin) -> bar { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# ) } + + + #[test] + fn function_definition_no_return() { + let bit = identifier! {bit}; + + assert_eq!( + function_definition! { + function foo([bit], coin) { + (let baz := add(bit, coin)) + (bar := hello_world(baz, "hi")) + } + } + .to_string(), + r#"function foo(bit, coin) { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# + ) + } } From 65dc472298dead373f0bd7fcec0039f1753cddda Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Wed, 26 Feb 2020 13:17:55 +0800 Subject: [PATCH 13/35] shorthand switch --- src/shorthand.rs | 137 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 31 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 9fd1516..ac0b0e3 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -3,7 +3,7 @@ use crate::yul; /// Creates a Yul literal. #[macro_export] macro_rules! literal { - {[$i:ident]} => {$i}; + {[$($tts:tt)*]} => {$($tts)*}; {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; } @@ -16,7 +16,7 @@ macro_rules! literal_expression { /// Creates a Yul identifier. #[macro_export] macro_rules! identifier { - {[$i:ident]} => {$i}; + {[$($tts:tt)*]} => {$($tts)*}; {$l:ident} => {yul::Identifier { identifier: stringify!($l).to_string(), yultype: None }}; } @@ -29,7 +29,7 @@ macro_rules! identifier_expression { /// Creates a Yul function call. #[macro_export] macro_rules! function_call { - {[$i:ident]} => {$i}; + {[$($tts:tt)*]} => {$($tts)*}; {$name:ident($($arg:tt),*)} => { yul::FunctionCall { identifier: identifier!{$name}, @@ -62,7 +62,7 @@ macro_rules! function_call_statement { #[macro_export] macro_rules! expression { {($($tts:tt)+)} => {expression!($($tts)*)}; - {[$i:ident]} => {$i}; + {[$($tts:tt)*]} => {$($tts)*}; {$l:literal} => {literal_expression!{$l}}; {$i:ident} => {identifier_expression!{$i}}; {$name:ident($($arg:tt)*)} => {function_call_expression! {$name($($arg)+)}}; @@ -94,6 +94,7 @@ macro_rules! assignment { #[macro_export] macro_rules! statement { {($($tts:tt)+)} => {statement!($($tts)*)}; + {[$($tts:tt)*]} => {$($tts)*}; {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)+)}}; {let $name:tt := $($expr:tt)+} => {variable_declaration!{let $name := $($expr)+}}; {$name:tt := $($expr:tt)+} => {assignment!{$name := $($expr)+}}; @@ -102,6 +103,7 @@ macro_rules! statement { /// Creates a Yul block. #[macro_export] macro_rules! block { + {[$($tts:tt)*]} => {$($tts)*}; {$($statement:tt)*} => { yul::Block { statements: { @@ -140,22 +142,52 @@ macro_rules! function_definition { }; } +/// Creates a Yul switch statement. +#[macro_export] +macro_rules! switch { + {@case (case $literal:tt { $($statement:tt)* })} => { + yul::Case { + literal: Some(literal! {$literal}), + block: block! {$($statement)*} + } + }; + {@case (default { $($statement:tt)* })} => { + yul::Case { + literal: None, + block: block! {$($statement)*} + } + }; + + {switch $expression:tt $($case:tt)*} => { + yul::Statement::Switch(yul::Switch { + expression: expression! {$expression}, + cases: { + let mut cases = vec![]; + $( + cases.push(switch! {@case $case} ); + )* + cases + } + }) + }; +} + #[cfg(test)] mod tests { use crate::yul; #[test] - fn test_literal_string() { + fn literal_string() { assert_eq!(literal! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_literal_num() { + fn literal_num() { assert_eq!(literal! {42}.to_string(), "42") } #[test] - fn test_literal_node() { + fn literal_node() { let foo = yul::Literal { literal: r#""bar""#.to_string(), yultype: None, @@ -164,29 +196,29 @@ mod tests { } #[test] - fn test_literal_expression_string() { + fn literal_expression_string() { assert_eq!(literal_expression! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_expression_literal() { + fn expression_literal() { assert_eq!(expression! {"foo"}.to_string(), r#""foo""#) } #[test] - fn test_expression_node() { + fn expression_node() { let node = literal_expression!("foobar"); assert_eq!(expression! {[node]}.to_string(), r#""foobar""#) } #[test] - fn test_expression_identifier() { + fn expression_identifier() { assert_eq!(expression! {foo}.to_string(), "foo") } #[test] - fn test_function_call() { + fn function_call() { assert_eq!( function_call! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# @@ -194,7 +226,7 @@ mod tests { } #[test] - fn test_function_call_node() { + fn function_call_node() { let node = literal_expression!("foobar"); assert_eq!( @@ -204,7 +236,7 @@ mod tests { } #[test] - fn test_function_call_expression() { + fn function_call_expression() { assert_eq!( function_call_expression! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# @@ -212,7 +244,7 @@ mod tests { } #[test] - fn test_function_call_statement() { + fn function_call_statement() { assert_eq!( function_call_statement! {foo("string", bar, 42)}.to_string(), r#"foo("string", bar, 42)"# @@ -220,7 +252,7 @@ mod tests { } #[test] - fn test_expression_function_call() { + fn expression_function_call() { let node = literal_expression!("foobar"); assert_eq!( @@ -230,7 +262,7 @@ mod tests { } #[test] - fn test_variable_declaration() { + fn variable_declaration() { assert_eq!( variable_declaration! {let foo := 42}.to_string(), "let foo := 42" @@ -238,7 +270,7 @@ mod tests { } #[test] - fn test_variable_declaration_function() { + fn variable_declaration_function() { assert_eq!( variable_declaration! {let foo := foo("bar", 42)}.to_string(), r#"let foo := foo("bar", 42)"# @@ -246,7 +278,7 @@ mod tests { } #[test] - fn test_variable_declaration_function_nested_node() { + fn variable_declaration_function_nested_node() { let food = function_call_expression!(food("taco", apple)); assert_eq!( @@ -256,7 +288,7 @@ mod tests { } #[test] - fn test_variable_declaration_identifier_node() { + fn variable_declaration_identifier_node() { let foo = identifier!(foo); assert_eq!( @@ -266,7 +298,7 @@ mod tests { } #[test] - fn test_variable_declaration_function_nested_raw() { + fn variable_declaration_function_nested_raw() { assert_eq!( variable_declaration! {let foo := foo("bar", (food("taco", apple)))}.to_string(), r#"let foo := foo("bar", food("taco", apple))"# @@ -274,12 +306,12 @@ mod tests { } #[test] - fn test_assignment() { + fn assignment() { assert_eq!(assignment! {foo := 42}.to_string(), "foo := 42") } #[test] - fn test_statement_function() { + fn statement_function() { let _42 = expression! {42}; let biz = function_call_expression! {biz(bit, coin, [_42])}; assert_eq!( @@ -297,7 +329,7 @@ mod tests { } #[test] - fn test_statement_variable_declaration() { + fn statement_variable_declaration() { assert_eq!( statement! {let foo := bar("ding", dong)}.to_string(), r#"let foo := bar("ding", dong)"# @@ -305,12 +337,12 @@ mod tests { } #[test] - fn test_statement_assignment() { + fn statement_assignment() { assert_eq!(statement! {foo := 42}.to_string(), "foo := 42") } #[test] - fn test_block() { + fn block() { assert_eq!( block! { (let foo := 42) @@ -337,20 +369,63 @@ mod tests { ) } - #[test] fn function_definition_no_return() { let bit = identifier! {bit}; assert_eq!( function_definition! { - function foo([bit], coin) { + function foo([bit], [identifier! {coin}]) { (let baz := add(bit, coin)) (bar := hello_world(baz, "hi")) } - } - .to_string(), + }.to_string(), r#"function foo(bit, coin) { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# ) } -} + + #[test] + fn switch() { + let foo = expression! {foo(1, "s")}; + let bing = expression! {bing("bong")}; + let _42 = literal! {42}; + + assert_eq! { + switch! { + switch [foo] + (case 1 { + (bar(42)) + }) + (case [_42] { + (bar(420)) + (baz("block", chain)) + }) + (default { + (let bing := [bing]) + (bar(bing)) + }) + }.to_string(), + r#"switch foo(1, "s") case 1 { bar(42) } case 42 { bar(420) baz("block", chain) } default { let bing := bing("bong") bar(bing) } "# + } + #[test] + + fn switch_no_default() { + let foo = expression! {foo(1, "s")}; + let bing = expression! {bing("bong")}; + let _42 = literal! {42}; + + assert_eq! { + switch! { + switch [foo] + (case 1 { + (bar(42)) + }) + (case [_42] { + (bar(420)) + (baz("block", chain)) + }) + }.to_string(), + r#"switch foo(1, "s") case 1 { bar(42) } case 42 { bar(420) baz("block", chain) } "# + } + } } +} \ No newline at end of file From efdb5a94fc73c09a09eef82452ed1cdb7b5da174 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Wed, 26 Feb 2020 16:11:29 +0800 Subject: [PATCH 14/35] statement vector insertion --- src/shorthand.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index ac0b0e3..a6a4ce7 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -61,11 +61,11 @@ macro_rules! function_call_statement { /// Creates a Yul expression. #[macro_export] macro_rules! expression { - {($($tts:tt)+)} => {expression!($($tts)*)}; + {($($tts:tt)*)} => {expression!($($tts)*)}; {[$($tts:tt)*]} => {$($tts)*}; {$l:literal} => {literal_expression!{$l}}; {$i:ident} => {identifier_expression!{$i}}; - {$name:ident($($arg:tt)*)} => {function_call_expression! {$name($($arg)+)}}; + {$name:ident($($arg:tt)*)} => {function_call_expression! {$name($($arg)*)}}; } /// Creates a Yul variable declaration statement. @@ -93,23 +93,26 @@ macro_rules! assignment { /// Creates a Yul statement. #[macro_export] macro_rules! statement { - {($($tts:tt)+)} => {statement!($($tts)*)}; + {($($tts:tt)*)} => {statement! {$($tts)*}}; {[$($tts:tt)*]} => {$($tts)*}; - {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)+)}}; - {let $name:tt := $($expr:tt)+} => {variable_declaration!{let $name := $($expr)+}}; - {$name:tt := $($expr:tt)+} => {assignment!{$name := $($expr)+}}; + {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)*)}}; + {let $name:tt := $($expr:tt)*} => {variable_declaration!{let $name := $($expr)*}}; + {$name:tt := $($expr:tt)*} => {assignment!{$name := $($expr)*}}; } /// Creates a Yul block. #[macro_export] macro_rules! block { + {@statement [$statement_vec:tt...]} => { $statement_vec.clone() }; + {@statement $statement:tt} => {vec![statement! {$statement}]}; + {[$($tts:tt)*]} => {$($tts)*}; {$($statement:tt)*} => { yul::Block { statements: { let mut statements = vec![]; $( - statements.push(statement!{$statement}); + statements.append(&mut block! {@statement $statement}); )* statements } @@ -353,6 +356,24 @@ mod tests { ) } + #[test] + fn block_multi_insert() { + let statements = vec![ + statement! { let bar := 2 }, + statement! { blockchain(_3d) }, + ]; + + assert_eq!( + block! { + (let foo := 42) + [statements...] + (bar(foo)) + } + .to_string(), + "{ let foo := 42 let bar := 2 blockchain(_3d) bar(foo) }" + ) + } + #[test] fn function_definition() { let bit = identifier! {bit}; From 0e2cfafc7a22ab03eaef838c220faf93f8bbe469 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 27 Feb 2020 12:14:27 +0800 Subject: [PATCH 15/35] cleanup, statements, and more testing --- src/shorthand.rs | 187 ++++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 75 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index a6a4ce7..41ff3d9 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -3,43 +3,47 @@ use crate::yul; /// Creates a Yul literal. #[macro_export] macro_rules! literal { - {[$($tts:tt)*]} => {$($tts)*}; + {[$e:expr]} => {$e}; {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; } /// Creates a Yul literal expression. #[macro_export] macro_rules! literal_expression { - {$($tts:tt)*} => {yul::Expression::Literal(literal!($($tts)*))}; + {$($literal:tt)*} => {yul::Expression::Literal(literal!($($literal)*))}; } /// Creates a Yul identifier. #[macro_export] macro_rules! identifier { - {[$($tts:tt)*]} => {$($tts)*}; - {$l:ident} => {yul::Identifier { identifier: stringify!($l).to_string(), yultype: None }}; + {[$e:expr]} => {$e}; + {$i:ident} => {yul::Identifier { identifier: stringify!($i).to_string(), yultype: None }}; +} + +/// Creates a vec of Yul identifiers. +#[macro_export] +macro_rules! identifiers { + {$($identifier:tt)*} => {{ + let mut identifiers = vec![]; + $(identifiers.push(identifier! {$identifier});)* + identifiers + }}; } /// Creates a Yul identifier expression. #[macro_export] macro_rules! identifier_expression { - {$($tts:tt)*} => {yul::Expression::Identifier(identifier!($($tts)*))}; + {$($identifier:tt)*} => {yul::Expression::Identifier(identifier! {$($identifier)*})}; } /// Creates a Yul function call. #[macro_export] macro_rules! function_call { - {[$($tts:tt)*]} => {$($tts)*}; - {$name:ident($($arg:tt),*)} => { + {[$e:expr]} => {$e}; + {$name:tt($($args:tt),*)} => { yul::FunctionCall { - identifier: identifier!{$name}, - arguments: { - let mut args = vec![]; - $( - args.push(expression!{$arg}); - )* - args - } + identifier: identifier! {$name}, + arguments: expressions! {$($args)*} } }; } @@ -47,34 +51,46 @@ macro_rules! function_call { /// Creates a function call expression. #[macro_export] macro_rules! function_call_expression { - {$($tts:tt)*} => {yul::Expression::FunctionCall(function_call!($($tts)*))}; + {$($function_call:tt)*} => { + yul::Expression::FunctionCall(function_call! {$($function_call)*}) + }; } /// Creates a function call statement. #[macro_export] macro_rules! function_call_statement { - {$($tts:tt)*} => {yul::Statement::Expression( - yul::Expression::FunctionCall(function_call!($($tts)*)) - )}; + {$($function_call:tt)*} => { + yul::Statement::Expression(function_call_expression! {$($function_call)*}) + }; } /// Creates a Yul expression. #[macro_export] macro_rules! expression { - {($($tts:tt)*)} => {expression!($($tts)*)}; - {[$($tts:tt)*]} => {$($tts)*}; - {$l:literal} => {literal_expression!{$l}}; - {$i:ident} => {identifier_expression!{$i}}; - {$name:ident($($arg:tt)*)} => {function_call_expression! {$name($($arg)*)}}; + {[$e:expr]} => {$e}; + {($($expression:tt)*)} => {expression! {$($expression)*}}; + {$name:tt($($args:tt)*)} => {function_call_expression! {$name($($args)*)}}; + {$l:literal} => {literal_expression! {$l}}; + {$i:ident} => {identifier_expression! {$i}}; +} + +/// Creates a vec of Yul expressions. +#[macro_export] +macro_rules! expressions { + {$($expressions:tt)*} => {{ + let mut expressions = vec![]; + $(expressions.push(expression! {$expressions});)* + expressions + }}; } /// Creates a Yul variable declaration statement. #[macro_export] macro_rules! variable_declaration { - {let $name:tt := $($tts:tt)+} => { + {let $name:tt := $($expression:tt)*} => { yul::Statement::VariableDeclaration(yul::VariableDeclaration { - identifiers: vec![identifier!{$name}], - expression: Some(expression!{$($tts)*}) + identifiers: vec![identifier! {$name}], + expression: Some(expression! {$($expression)*}) }) }; } @@ -82,10 +98,10 @@ macro_rules! variable_declaration { /// Creates a Yul assignment statement. #[macro_export] macro_rules! assignment { - {$name:tt := $($expr:tt)+} => { + {$name:tt := $($expression:tt)+} => { yul::Statement::Assignment(yul::Assignment { - identifiers: vec![identifier!{$name}], - expression: expression!{$($expr)*} + identifiers: vec![identifier! {$name}], + expression: expression! {$($expression)*} }) }; } @@ -93,54 +109,50 @@ macro_rules! assignment { /// Creates a Yul statement. #[macro_export] macro_rules! statement { - {($($tts:tt)*)} => {statement! {$($tts)*}}; - {[$($tts:tt)*]} => {$($tts)*}; - {$name:ident($($arg:tt)*)} => {function_call_statement!{$name($($arg)*)}}; - {let $name:tt := $($expr:tt)*} => {variable_declaration!{let $name := $($expr)*}}; - {$name:tt := $($expr:tt)*} => {assignment!{$name := $($expr)*}}; + {[$e:expr]} => {$e}; + {($($statement:tt)*)} => {statement! {$($statement)*}}; + {$name:tt($($args:tt)*)} => {function_call_statement! {$name($($args)*)}}; + {let $name:tt := $($expression:tt)*} => {variable_declaration! {let $name := $($expression)*}}; + {$name:tt := $($expression:tt)*} => {assignment! {$name := $($expression)*}}; + {function $name:tt($($params:tt),*) $(-> $returns:ident)? {$($statements:tt)*}} => { + function $name($($params),*) $(-> $returns)? {$($statements)*} + } +} + +/// Creates a vec of Yul statements. +#[macro_export] +macro_rules! statements { + {@as_vec [$statements:tt...]} => { $statements.clone() }; + {@as_vec $($statement:tt)*} => {vec![statement! {$($statement)*}]}; + + {[$e:expr]} => {vec![$e]}; + {$($statement:tt)*} => {{ + let mut statements = vec![]; + $(statements.append(&mut statements! {@as_vec $statement});)* + statements + }}; } /// Creates a Yul block. #[macro_export] macro_rules! block { - {@statement [$statement_vec:tt...]} => { $statement_vec.clone() }; - {@statement $statement:tt} => {vec![statement! {$statement}]}; - - {[$($tts:tt)*]} => {$($tts)*}; - {$($statement:tt)*} => { - yul::Block { - statements: { - let mut statements = vec![]; - $( - statements.append(&mut block! {@statement $statement}); - )* - statements - } - } - }; + {[$e:expr]} => {$e}; + {$($statements:tt)*} => { yul::Block { statements: statements! {$($statements)*} }}; } /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { - {function $name:ident($($param:tt),*) $(-> $returns:ident)? {$($statement:tt)*}} => { + {function $name:tt($($params:tt),*) $(-> $returns:ident)? {$($statements:tt)*}} => { yul::Statement::FunctionDefinition(yul::FunctionDefinition { - name: identifier!{$name}, - parameters: { - let mut params = vec![]; - $( - params.push(identifier!{$param}); - )* - params - }, + name: identifier! {$name}, + parameters: identifiers! {$($params)*}, returns: { let mut returns = vec![]; - $( - returns.push(identifier!{$returns}); - )* + $(returns.push(identifier!{$returns});)* returns }, - block: block!{$($statement)*}, + block: block!{$($statements)*}, }) }; } @@ -148,27 +160,25 @@ macro_rules! function_definition { /// Creates a Yul switch statement. #[macro_export] macro_rules! switch { - {@case (case $literal:tt { $($statement:tt)* })} => { + {@case (case $literal:tt { $($statements:tt)* })} => { yul::Case { literal: Some(literal! {$literal}), - block: block! {$($statement)*} + block: block! {$($statements)*} } }; - {@case (default { $($statement:tt)* })} => { + {@case (default { $($statements:tt)* })} => { yul::Case { literal: None, - block: block! {$($statement)*} + block: block! {$($statements)*} } }; - {switch $expression:tt $($case:tt)*} => { + {switch $expression:tt $($cases:tt)*} => { yul::Statement::Switch(yul::Switch { expression: expression! {$expression}, cases: { let mut cases = vec![]; - $( - cases.push(switch! {@case $case} ); - )* + $(cases.push(switch! {@case $cases});)* cases } }) @@ -326,7 +336,7 @@ mod tests { (farm(cow, "sheep")) ) } - .to_string(), + .to_string(), r#"bar("ding", dong, biz(bit, coin, 42), farm(cow, "sheep"))"# ) } @@ -351,7 +361,7 @@ mod tests { (let foo := 42) (bar(foo)) } - .to_string(), + .to_string(), "{ let foo := 42 bar(foo) }" ) } @@ -385,7 +395,7 @@ mod tests { (bar := hello_world(baz, "hi")) } } - .to_string(), + .to_string(), r#"function foo(bit, coin) -> bar { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# ) } @@ -404,7 +414,7 @@ mod tests { r#"function foo(bit, coin) { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# ) } - + #[test] fn switch() { let foo = expression! {foo(1, "s")}; @@ -449,4 +459,31 @@ mod tests { r#"switch foo(1, "s") case 1 { bar(42) } case 42 { bar(420) baz("block", chain) } "# } } } + + #[test] + fn statements() { + let good_statements = statements! { + (let a := my_function("hello", (world("42")))) + (b := add(a, 2)) + }; + + let food = identifier! {food}; + let better_statements = statements! { + (let value := [food](1, 2)) + (let another_value := 42) + }; + + let best_statement = statement! { foo := "42 bar 42" }; + + assert_eq!( + statements! { + (let kung := foo) + [good_statements...] + (ip := man()) + [better_statements...] + [best_statement] + }.iter().map(|s| s.to_string()).collect::>().join(" "), + r#"let kung := foo let a := my_function("hello", world("42")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := "42 bar 42""# + ) + } } \ No newline at end of file From e3e390c0febcff5c41cadf91f411f41ecc98bf6e Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 27 Feb 2020 14:47:50 +0800 Subject: [PATCH 16/35] function statements --- src/shorthand.rs | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 41ff3d9..fc8c0b5 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -23,9 +23,12 @@ macro_rules! identifier { /// Creates a vec of Yul identifiers. #[macro_export] macro_rules! identifiers { - {$($identifier:tt)*} => {{ + {@as_vec [$identifiers:tt...]} => { $identifiers.clone() }; + {@as_vec $($identifier:tt)*} => {vec![identifier! {$($identifier)*}]}; + + {$($identifiers:tt)*} => {{ let mut identifiers = vec![]; - $(identifiers.push(identifier! {$identifier});)* + $(identifiers.append(&mut identifiers! {@as_vec $identifiers});)* identifiers }}; } @@ -114,7 +117,7 @@ macro_rules! statement { {$name:tt($($args:tt)*)} => {function_call_statement! {$name($($args)*)}}; {let $name:tt := $($expression:tt)*} => {variable_declaration! {let $name := $($expression)*}}; {$name:tt := $($expression:tt)*} => {assignment! {$name := $($expression)*}}; - {function $name:tt($($params:tt),*) $(-> $returns:ident)? {$($statements:tt)*}} => { + {function $name:tt($($params:tt),*) $(-> $returns:tt)? {$($statements:tt)*}} => { function $name($($params),*) $(-> $returns)? {$($statements)*} } } @@ -125,7 +128,6 @@ macro_rules! statements { {@as_vec [$statements:tt...]} => { $statements.clone() }; {@as_vec $($statement:tt)*} => {vec![statement! {$($statement)*}]}; - {[$e:expr]} => {vec![$e]}; {$($statement:tt)*} => {{ let mut statements = vec![]; $(statements.append(&mut statements! {@as_vec $statement});)* @@ -143,7 +145,7 @@ macro_rules! block { /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { - {function $name:tt($($params:tt),*) $(-> $returns:ident)? {$($statements:tt)*}} => { + {function $name:tt($($params:tt),*) $(-> $returns:tt)? {$($statements:tt)*}} => { yul::Statement::FunctionDefinition(yul::FunctionDefinition { name: identifier! {$name}, parameters: identifiers! {$($params)*}, @@ -486,4 +488,32 @@ mod tests { r#"let kung := foo let a := my_function("hello", world("42")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := "42 bar 42""# ) } + + #[test] + fn object_w_function_definitions() { + let one = identifier! {one}; + let foo_idents = identifiers! {test [one] two}; + let foo_func = function_definition! { + function foo([foo_idents...]) { + (log("hello_world")) + } + }; + + let bar_idents = identifiers! {three four}; + let bar_func = function_definition! { + function foo(two, [bar_idents...]) -> return_val { + (let a := test(two, three, four)) + (return_val := a) + } + }; + + assert_eq!( + block! { + (let a := b) + [foo_func] + [bar_func] + }.to_string(), + r#"{ let a := b function foo(test, one, two) { log("hello_world") } function foo(two, three, four) -> return_val { let a := test(two, three, four) return_val := a } }"# + ) + } } \ No newline at end of file From 0f8a5a4bd9373c02be56e37c412a3b381df80c9b Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 27 Feb 2020 18:59:11 +0800 Subject: [PATCH 17/35] squash this --- src/shorthand.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index fc8c0b5..0267e9b 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -138,7 +138,6 @@ macro_rules! statements { /// Creates a Yul block. #[macro_export] macro_rules! block { - {[$e:expr]} => {$e}; {$($statements:tt)*} => { yul::Block { statements: statements! {$($statements)*} }}; } From bd6dcfe82f5bbb39f0c9f81daf0121e5f3be7bbf Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 27 Feb 2020 22:23:16 +0800 Subject: [PATCH 18/35] statement switch --- src/shorthand.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 0267e9b..5b93306 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -119,7 +119,8 @@ macro_rules! statement { {$name:tt := $($expression:tt)*} => {assignment! {$name := $($expression)*}}; {function $name:tt($($params:tt),*) $(-> $returns:tt)? {$($statements:tt)*}} => { function $name($($params),*) $(-> $returns)? {$($statements)*} - } + }; + {switch $expression:tt $($cases:tt)*} => {switch! {switch $expression $($cases)*}}; } /// Creates a vec of Yul statements. @@ -515,4 +516,25 @@ mod tests { r#"{ let a := b function foo(test, one, two) { log("hello_world") } function foo(two, three, four) -> return_val { let a := test(two, three, four) return_val := a } }"# ) } + + #[test] + fn object_w_switch() { + + assert_eq!( + block! { + (let a := 40) + (let b := 2) + (switch (add(a,b)) + (case 42 { + (let c := 2) + }) + (case "3d" { + (foo(0)) + (bar("test")) + }) + ) + }.to_string(), + r#"{ let a := 40 let b := 2 switch add(a, b) case 42 { let c := 2 } case "3d" { foo(0) bar("test") } }"# + ) + } } \ No newline at end of file From 9b99282a941f12a5878048f7bc3e1c03ef803a04 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Fri, 28 Feb 2020 13:48:18 +0800 Subject: [PATCH 19/35] external case and cases macro --- src/shorthand.rs | 63 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 5b93306..d4f9ffd 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -159,30 +159,46 @@ macro_rules! function_definition { }; } -/// Creates a Yul switch statement. +/// Create a Yul case. #[macro_export] -macro_rules! switch { - {@case (case $literal:tt { $($statements:tt)* })} => { +macro_rules! case { + {[$e:expr]} => {$e}; + {($($case:tt)*)} => {case! {$($case)*}}; + {case $literal:tt { $($statements:tt)* }} => { yul::Case { literal: Some(literal! {$literal}), block: block! {$($statements)*} } }; - {@case (default { $($statements:tt)* })} => { + {default { $($statements:tt)* }} => { yul::Case { literal: None, block: block! {$($statements)*} } }; +} + +/// Creates a vec of Yul cases. +#[macro_export] +macro_rules! cases { + {@as_vec [$cases:tt...]} => { $cases.clone() }; + {@as_vec $($case:tt)*} => {vec![case! {$($case)*}]}; + + {$($case:tt)*} => {{ + let mut cases = vec![]; + $(cases.append(&mut cases! {@as_vec $case});)* + cases + }}; +} + +/// Creates a Yul switch statement. +#[macro_export] +macro_rules! switch { {switch $expression:tt $($cases:tt)*} => { yul::Statement::Switch(yul::Switch { expression: expression! {$expression}, - cases: { - let mut cases = vec![]; - $(cases.push(switch! {@case $cases});)* - cases - } + cases: cases! {$($cases)*} }) }; } @@ -519,7 +535,6 @@ mod tests { #[test] fn object_w_switch() { - assert_eq!( block! { (let a := 40) @@ -537,4 +552,32 @@ mod tests { r#"{ let a := 40 let b := 2 switch add(a, b) case 42 { let c := 2 } case "3d" { foo(0) bar("test") } }"# ) } + + #[test] + fn cases_in_switch() { + let case_foo = case! { + case "foo" { (test(42)) } + }; + + let case_bar = case! { + case "bar" { (hello_world(42)) (a := b) } + }; + + let cases = vec![case_foo, case_bar]; + + let default = case! { + default { + (c := 4) + } + }; + + assert_eq!( + switch! { + switch (cat("f",s)) + [cases...] + [default] + }.to_string(), + r#"switch cat("f", s) case "foo" { test(42) } case "bar" { hello_world(42) a := b } default { c := 4 } "# + ) + } } \ No newline at end of file From 67e6a08bf2bf1e3a5c7c5f64f1a3c0aea68f866b Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Fri, 6 Mar 2020 18:54:26 +0800 Subject: [PATCH 20/35] Expression expansion --- src/shorthand.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index d4f9ffd..d4fdc69 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -80,11 +80,14 @@ macro_rules! expression { /// Creates a vec of Yul expressions. #[macro_export] macro_rules! expressions { + {@as_vec [$expressions:tt...]} => { $expressions.clone() }; + {@as_vec $($expression:tt)*} => {vec![expression! {$($expression)*}]}; + {$($expressions:tt)*} => {{ - let mut expressions = vec![]; - $(expressions.push(expression! {$expressions});)* - expressions - }}; + let mut expressions = vec![]; + $(expressions.append(&mut expressions! {@as_vec $expressions});)* + expressions + }}; } /// Creates a Yul variable declaration statement. @@ -250,9 +253,11 @@ mod tests { #[test] fn function_call() { + let expressions = expressions! { bar "foo" (call()) }; + assert_eq!( - function_call! {foo("string", bar, 42)}.to_string(), - r#"foo("string", bar, 42)"# + function_call! {foo("string", bar, 42, [expressions...])}.to_string(), + r#"foo("string", bar, 42, bar, "foo", call())"# ) } From 19e27d8748b250c014234cc3ebdfaee63bad61da Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sat, 7 Mar 2020 12:49:24 +0800 Subject: [PATCH 21/35] If statements --- src/shorthand.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index d4fdc69..b589863 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -124,6 +124,7 @@ macro_rules! statement { function $name($($params),*) $(-> $returns)? {$($statements)*} }; {switch $expression:tt $($cases:tt)*} => {switch! {switch $expression $($cases)*}}; + {if $expression:tt { $($block:tt)* }} => {_if! {if $expression { $($block)* }}}; } /// Creates a vec of Yul statements. @@ -194,7 +195,6 @@ macro_rules! cases { }}; } - /// Creates a Yul switch statement. #[macro_export] macro_rules! switch { @@ -206,6 +206,17 @@ macro_rules! switch { }; } +/// Creates a Yul if statement +#[macro_export] +macro_rules! _if { + {if $expression:tt { $($block:tt)* }} => { + yul::Statement::If(yul::If { + expression: expression! {$expression}, + block: block! {$($block)*} + }) + } +} + #[cfg(test)] mod tests { use crate::yul; @@ -256,7 +267,7 @@ mod tests { let expressions = expressions! { bar "foo" (call()) }; assert_eq!( - function_call! {foo("string", bar, 42, [expressions...])}.to_string(), + function_call! { foo("string", bar, 42, [expressions...]) }.to_string(), r#"foo("string", bar, 42, bar, "foo", call())"# ) } @@ -505,8 +516,9 @@ mod tests { (ip := man()) [better_statements...] [best_statement] + (if 0 { (call(42)) }) }.iter().map(|s| s.to_string()).collect::>().join(" "), - r#"let kung := foo let a := my_function("hello", world("42")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := "42 bar 42""# + r#"let kung := foo let a := my_function("hello", world("42")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := "42 bar 42" if 0 { call(42) }"# ) } @@ -585,4 +597,12 @@ mod tests { r#"switch cat("f", s) case "foo" { test(42) } case "bar" { hello_world(42) a := b } default { c := 4 } "# ) } + + #[test] + fn _if() { + assert_eq!( + _if! { if (eq(foo, 0)) { (let a := b) } }.to_string(), + "if eq(foo, 0) { let a := b }" + ) + } } \ No newline at end of file From b9f55ea203296260a0c561906461c36ddab07086 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 16 Mar 2020 23:46:20 +0800 Subject: [PATCH 22/35] for loops --- src/shorthand.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index b589863..35cabc5 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -125,6 +125,9 @@ macro_rules! statement { }; {switch $expression:tt $($cases:tt)*} => {switch! {switch $expression $($cases)*}}; {if $expression:tt { $($block:tt)* }} => {_if! {if $expression { $($block)* }}}; + {for { $($pre:tt)* } $condition:tt { $($post:tt)* } { $($body:tt)* } } => { + for_loop! { for { $($pre)* } $condition { $($post)* } { $($body)* } } + } } /// Creates a vec of Yul statements. @@ -217,6 +220,19 @@ macro_rules! _if { } } +/// Creates a Yul for loop statement +#[macro_export] +macro_rules! for_loop { + {for { $($pre:tt)* } $condition:tt { $($post:tt)* } { $($body:tt)* } } => { + yul::Statement::ForLoop(yul::ForLoop { + pre: block! {$($pre)*}, + condition: expression! {$condition}, + post: block! {$($post)*}, + body: block! {$($body)*} + }) + } +} + #[cfg(test)] mod tests { use crate::yul; @@ -605,4 +621,30 @@ mod tests { "if eq(foo, 0) { let a := b }" ) } + + #[test] + fn for_loop() { + assert_eq!( + for_loop! { + for { (let i := 0) } (lt(i, exponent)) { (i := add(i, 1)) } + { + (result := mul(result, base)) + } + }.to_string(), + "for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) }" + ) + } + + #[test] + fn statement_for_loop() { + assert_eq!( + statement! { + for { (let i := 0) } (lt(i, exponent)) { (i := add(i, 1)) } + { + (result := mul(result, base)) + } + }.to_string(), + "for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) }" + ) + } } \ No newline at end of file From 0fb699702686c08c2e6717f802d76f69afc3c418 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 26 Mar 2020 22:54:56 +0800 Subject: [PATCH 23/35] expression to string for literals and identifiers --- src/shorthand.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 35cabc5..54e1311 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -4,6 +4,7 @@ use crate::yul; #[macro_export] macro_rules! literal { {[$e:expr]} => {$e}; + {($e:expr)} => {yul::Literal { literal: $e.to_string(), yultype: None }}; {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; } @@ -17,6 +18,7 @@ macro_rules! literal_expression { #[macro_export] macro_rules! identifier { {[$e:expr]} => {$e}; + {($e:expr)} => {yul::Identifier { identifier: $e.to_string(), yultype: None }}; {$i:ident} => {yul::Identifier { identifier: stringify!($i).to_string(), yultype: None }}; } @@ -647,4 +649,33 @@ mod tests { "for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) }" ) } + + #[test] + fn identifier_from_expression() { + let identifier = "test"; + + assert_eq!( + identifier! {(identifier)}.to_string(), + "test" + ); + + assert_eq!( + identifier_expression! {(identifier)}.to_string(), + "test" + ) + } + + #[test] + fn literal_from_expression() { + assert_eq!( + literal! {(1 + 1)}.to_string(), + "2" + ); + + let foo = r#""bar""#; + assert_eq!( + literal_expression! {(foo)}.to_string(), + r#""bar""# + ) + } } \ No newline at end of file From be4470b9e3634f2cc940aba962308f37a9071855 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Sat, 4 Apr 2020 12:32:41 +0800 Subject: [PATCH 24/35] Doc header --- src/shorthand.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 54e1311..b07086f 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -1,3 +1,18 @@ +//! This module contains a set of macros for shorthand Yul AST generation. +//! +//! ## Example Usage +//! +//! ```rust +//! let some_ident = identifier! { bar }; +//! +//! function_definition! { +//! function foo([some_ident], baz) -> foo { +//! (let val := add(bar, baz)) +//! (foo := some_func(val, "hi")) +//! } +//!} +//! ``` + use crate::yul; /// Creates a Yul literal. From 3bbf3c5c3e0597066dee517b830f23f42b47d751 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Wed, 20 May 2020 15:52:06 +0800 Subject: [PATCH 25/35] Leave statement added. --- src/shorthand.rs | 24 ++++++++++-------------- src/yul.rs | 2 ++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index b07086f..3314673 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -1,17 +1,4 @@ //! This module contains a set of macros for shorthand Yul AST generation. -//! -//! ## Example Usage -//! -//! ```rust -//! let some_ident = identifier! { bar }; -//! -//! function_definition! { -//! function foo([some_ident], baz) -> foo { -//! (let val := add(bar, baz)) -//! (foo := some_func(val, "hi")) -//! } -//!} -//! ``` use crate::yul; @@ -144,7 +131,8 @@ macro_rules! statement { {if $expression:tt { $($block:tt)* }} => {_if! {if $expression { $($block)* }}}; {for { $($pre:tt)* } $condition:tt { $($post:tt)* } { $($body:tt)* } } => { for_loop! { for { $($pre)* } $condition { $($post)* } { $($body)* } } - } + }; + {leave} => {yul::Statement::Leave}; } /// Creates a vec of Yul statements. @@ -693,4 +681,12 @@ mod tests { r#""bar""# ) } + + #[test] + fn leave() { + assert_eq!( + statement! { leave }.to_string(), + "leave" + ) + } } \ No newline at end of file diff --git a/src/yul.rs b/src/yul.rs index 0a787fb..0d5237d 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -118,6 +118,7 @@ pub enum Statement { ForLoop(ForLoop), Break, Continue, + Leave, } impl fmt::Display for Type { @@ -346,6 +347,7 @@ impl fmt::Display for Statement { Statement::ForLoop(ref forloop) => write!(f, "{}", forloop), Statement::Break => write!(f, "break"), Statement::Continue => write!(f, "continue"), + Statement::Leave => write!(f, "leave"), } } } From 65e1771e2c0124b628baae3c7fe3067bb345b0d5 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 14 Sep 2020 18:56:10 -0600 Subject: [PATCH 26/35] Added doc examples for shorthand macros. --- src/shorthand.rs | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 3314673..86a4bc9 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -3,6 +3,27 @@ use crate::yul; /// Creates a Yul literal. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use yultsur::*; +/// +/// let foo_var = "\"foo\""; +/// +/// // create a string literal with a string +/// let foo_1 = literal! { "foo" }; +/// // create a string literal from a variable using parens +/// let foo_2 = literal! { (foo_var) }; +/// // create a literal from an existing literal +/// let foo_3 = literal! { [foo_1.clone()] }; +/// +/// assert_eq!(foo_1.to_string(), "\"foo\""); +/// assert_eq!(foo_2.to_string(), "\"foo\""); +/// assert_eq!(foo_3.to_string(), "\"foo\""); +/// ``` #[macro_export] macro_rules! literal { {[$e:expr]} => {$e}; @@ -11,12 +32,49 @@ macro_rules! literal { } /// Creates a Yul literal expression. +/// +/// Same as `literal! { ... }`, except the literal is wrapped in a `yul::Expression`. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use yultsur::*; +/// +/// let foo = literal_expression! { "foo" }; +/// let forty_two = literal_expression! { 42 }; +/// +/// assert_eq!(foo.to_string(), "\"foo\""); +/// assert_eq!(forty_two.to_string(), "42"); +/// ``` #[macro_export] macro_rules! literal_expression { {$($literal:tt)*} => {yul::Expression::Literal(literal!($($literal)*))}; } /// Creates a Yul identifier. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use yultsur::*; +/// +/// let foo_var = "foo"; +/// +/// // create an identifier explicitly +/// let foo_1 = identifier! { foo }; +/// // create an identifier from a variable using parens +/// let foo_2 = identifier! { (foo_var) }; +/// // create an identifier from an existing literal +/// let foo_3 = identifier! { [foo_1.clone()] }; +/// +/// assert_eq!(foo_1.to_string(), "foo"); +/// assert_eq!(foo_2.to_string(), "foo"); +/// assert_eq!(foo_3.to_string(), "foo"); +/// ``` #[macro_export] macro_rules! identifier { {[$e:expr]} => {$e}; @@ -25,6 +83,23 @@ macro_rules! identifier { } /// Creates a vec of Yul identifiers. +/// +/// # Examples +/// +/// ``` +/// use yultsur::*; +/// +/// // create identifiers with +/// let identifiers_1 = identifiers! { foo bar }; +/// +/// // sandwich identifiers between other identifiers +/// let identifiers_2 = identifiers! { foo [identifiers_1...] bar }; +/// +/// assert_eq!( +/// identifiers_2.iter().map(|i| i.to_string()).collect::>(), +/// vec!["foo", "foo", "bar", "bar"], +/// ); +/// ``` #[macro_export] macro_rules! identifiers { {@as_vec [$identifiers:tt...]} => { $identifiers.clone() }; @@ -38,12 +113,34 @@ macro_rules! identifiers { } /// Creates a Yul identifier expression. +/// +/// Same as `identifier! { ... }`, except the returned values is wrapped in an expression. +/// +/// # Examples +/// +/// ``` +/// use yultsur::*; +/// +/// let bar = identifier_expression! { foo }; +/// assert_eq!(bar.to_string(), "foo") +/// ``` #[macro_export] macro_rules! identifier_expression { {$($identifier:tt)*} => {yul::Expression::Identifier(identifier! {$($identifier)*})}; } /// Creates a Yul function call. +/// +/// # Examples +/// +/// ``` +/// use yultsur::*; +/// +/// let args = expressions! { arg_1 arg_2 }; +/// let foo = function_call! { foo([args...]) }; +/// +/// assert_eq!(foo.to_string(), "foo(arg_1, arg_2)"); +/// ``` #[macro_export] macro_rules! function_call { {[$e:expr]} => {$e}; @@ -56,6 +153,8 @@ macro_rules! function_call { } /// Creates a function call expression. +/// +/// Same as `function_call { ... }`, except the returned values is wrapped in an expression. #[macro_export] macro_rules! function_call_expression { {$($function_call:tt)*} => { @@ -64,6 +163,8 @@ macro_rules! function_call_expression { } /// Creates a function call statement. +/// +/// Same as `function_call { ... }`, except the returned values is wrapped in a statement. #[macro_export] macro_rules! function_call_statement { {$($function_call:tt)*} => { From e53d9bba664ec2f30b4dc1810d9d31923f88097d Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 9 Nov 2020 17:39:48 -0700 Subject: [PATCH 27/35] continue and break in shorthand --- src/shorthand.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 86a4bc9..ac201b4 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -234,6 +234,8 @@ macro_rules! statement { for_loop! { for { $($pre)* } $condition { $($post)* } { $($body)* } } }; {leave} => {yul::Statement::Leave}; + {break} => {yul::Statement::Break}; + {continue} => {yul::Statement::Continue}; } /// Creates a vec of Yul statements. @@ -790,4 +792,20 @@ mod tests { "leave" ) } + + #[test] + fn _continue() { + assert_eq!( + statement! { continue }.to_string(), + "continue" + ) + } + + #[test] + fn _break() { + assert_eq!( + statement! { break }.to_string(), + "break" + ) + } } \ No newline at end of file From 3ac1958c1acc13cfcca1ed99c55a4fbfe5769e1d Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 16 Nov 2020 10:15:31 -0700 Subject: [PATCH 28/35] block statements --- src/shorthand.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index ac201b4..8f2b54e 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -257,6 +257,12 @@ macro_rules! block { {$($statements:tt)*} => { yul::Block { statements: statements! {$($statements)*} }}; } +/// Creates a Yul block statement. +#[macro_export] +macro_rules! block_statement { + {$($statements:tt)*} => { yul::Statement::Block(block! {$($statements)*}) }; +} + /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { @@ -524,6 +530,18 @@ mod tests { ) } + #[test] + fn block_statement() { + assert_eq!( + block_statement! { + (let foo := 42) + (bar(foo)) + } + .to_string(), + "{ let foo := 42 bar(foo) }" + ) + } + #[test] fn block_multi_insert() { let statements = vec![ From ab0c49099d06722e09bd1003a95ae56f1ffe3757 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 4 Jan 2021 10:38:35 -0700 Subject: [PATCH 29/35] code block shorthand --- src/shorthand.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/shorthand.rs b/src/shorthand.rs index 8f2b54e..9965fce 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -263,6 +263,18 @@ macro_rules! block_statement { {$($statements:tt)*} => { yul::Statement::Block(block! {$($statements)*}) }; } +/// Creates a Yul code block. +#[macro_export] +macro_rules! code { + {$($statements:tt)*} => { yul::Code { block: block! {$($statements)*} } }; +} + +/// Creates a Yul code block statement. +#[macro_export] +macro_rules! code_statement { + {$($statements:tt)*} => { yul::Statement::Code(code! {$($statements)*}) }; +} + /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { @@ -542,6 +554,31 @@ mod tests { ) } + + #[test] + fn code() { + assert_eq!( + code! { + (let foo := 42) + (bar(foo)) + } + .to_string(), + "code { let foo := 42 bar(foo) }" + ) + } + + #[test] + fn code_statement() { + assert_eq!( + code_statement! { + (let foo := 42) + (bar(foo)) + } + .to_string(), + "code { let foo := 42 bar(foo) }" + ) + } + #[test] fn block_multi_insert() { let statements = vec![ From c2fcba489ca2ce04b51e4f254ccd9a3a06b81000 Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Fri, 15 Jan 2021 12:29:03 +0100 Subject: [PATCH 30/35] Support data objects --- src/yul.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/yul.rs b/src/yul.rs index 0d5237d..658bd0b 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -44,6 +44,13 @@ pub struct Object { pub name: Identifier, pub code: Code, pub objects: Vec, + pub data: Vec, +} + +#[derive(Hash, Clone, PartialEq, Debug)] +pub struct Data { + pub name: String, + pub value: String, } #[derive(Hash, Clone, PartialEq, Debug)] @@ -198,7 +205,13 @@ impl fmt::Display for FunctionCall { impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "object \"{}\" {{ {} {} }}", self.name, self.code, self.objects.iter().map(|o| o.to_string()).collect::>().join(" ")) + write!(f, "object \"{}\" {{ {} {} {} }}", self.name, self.code, self.objects.iter().map(|o| o.to_string()).collect::>().join(" "), self.data.iter().map(|o| o.to_string()).collect::>().join(" ")) + } +} + +impl fmt::Display for Data { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "data \"{}\" \"{}\"", self.name, self.value) } } @@ -612,14 +625,51 @@ mod tests { Object { name: Identifier { identifier: "Test".to_string(), yultype: None }, code: Code { block: Block { statements: vec![] } }, - objects: vec![] + objects: vec![], + data: vec![], + } + ], + data: vec![] + }.to_string(), + "object \"Name\" { code { } object \"Test\" { code { } } }" + ); + } + + #[test] + fn object_with_data() { + assert_eq!( + Object { + name: Identifier { + identifier: "Name".to_string(), + yultype: None, + }, + code: Code { block: Block { statements: vec![] } }, + objects: vec![ + Object { + name: Identifier { identifier: "Test".to_string(), yultype: None }, + code: Code { block: Block { statements: vec![] } }, + objects: vec![], + data: vec![Data {name: "Data1".to_string(), value: "Value1".to_string() }], } - ] + ], + data: vec![Data {name: "Data2".to_string(), value: "Value2".to_string() }], + }.to_string(), + "object \"Name\" { code { } object \"Test\" { code { } data \"Data1\" \"Value1\" } data \"Data2\" \"Value2\" }" + ); + } + + #[test] + fn object_data_string() { + assert_eq!( + Data { + name: "Name".to_string(), + value: "Value".to_string() }.to_string(), - "object \"Name\" { code { } object \"Test\" { code { } } }" + "data \"Name\" \"Value\"" ); } + #[test] fn code_basic() { assert_eq!( From 6a7c162339cf615b2b2289d84b8bcfe57ffebf55 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Thu, 11 Feb 2021 17:02:52 -0700 Subject: [PATCH 31/35] Fix 'literal too large error' for hex literals --- src/shorthand.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 9965fce..0232200 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -28,7 +28,7 @@ use crate::yul; macro_rules! literal { {[$e:expr]} => {$e}; {($e:expr)} => {yul::Literal { literal: $e.to_string(), yultype: None }}; - {$l:literal} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; + {$l:tt} => {yul::Literal { literal: stringify!($l).to_string(), yultype: None }}; } /// Creates a Yul literal expression. @@ -178,8 +178,8 @@ macro_rules! expression { {[$e:expr]} => {$e}; {($($expression:tt)*)} => {expression! {$($expression)*}}; {$name:tt($($args:tt)*)} => {function_call_expression! {$name($($args)*)}}; - {$l:literal} => {literal_expression! {$l}}; {$i:ident} => {identifier_expression! {$i}}; + {$l:tt} => {literal_expression! {$l}}; } /// Creates a vec of Yul expressions. @@ -363,6 +363,15 @@ macro_rules! for_loop { mod tests { use crate::yul; + #[test] + fn literal_hex() { + assert_eq!( + literal! { 0x4200000000000000000000000000000000000000000000000000000000420026 } + .to_string(), + "0x4200000000000000000000000000000000000000000000000000000000420026" + ) + } + #[test] fn literal_string() { assert_eq!(literal! {"foo"}.to_string(), r#""foo""#) @@ -499,6 +508,15 @@ mod tests { assert_eq!(assignment! {foo := 42}.to_string(), "foo := 42") } + #[test] + fn large_assignment() { + assert_eq!( + assignment! { foo := 0x4200000000000000000000000000000000000000000000000000000000420026 } + .to_string(), + "foo := 0x4200000000000000000000000000000000000000000000000000000000420026" + ) + } + #[test] fn statement_function() { let _42 = expression! {42}; @@ -554,7 +572,6 @@ mod tests { ) } - #[test] fn code() { assert_eq!( From 3f5543e864bf4efecff9f0d02cd6e8f44a5134ac Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Tue, 8 Jun 2021 14:42:21 -0600 Subject: [PATCH 32/35] Multiple return and target values. --- src/shorthand.rs | 61 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 0232200..0b311e1 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -198,9 +198,9 @@ macro_rules! expressions { /// Creates a Yul variable declaration statement. #[macro_export] macro_rules! variable_declaration { - {let $name:tt := $($expression:tt)*} => { + {let $($names:tt),* := $($expression:tt)*} => { yul::Statement::VariableDeclaration(yul::VariableDeclaration { - identifiers: vec![identifier! {$name}], + identifiers: identifiers! {$($names)*}, expression: Some(expression! {$($expression)*}) }) }; @@ -209,9 +209,9 @@ macro_rules! variable_declaration { /// Creates a Yul assignment statement. #[macro_export] macro_rules! assignment { - {$name:tt := $($expression:tt)+} => { + {$($names:tt),* := $($expression:tt)+} => { yul::Statement::Assignment(yul::Assignment { - identifiers: vec![identifier! {$name}], + identifiers: identifiers! {$($names)*}, expression: expression! {$($expression)*} }) }; @@ -278,15 +278,11 @@ macro_rules! code_statement { /// Creates a Yul function definition. #[macro_export] macro_rules! function_definition { - {function $name:tt($($params:tt),*) $(-> $returns:tt)? {$($statements:tt)*}} => { + {function $name:tt($($params:tt),*) $(-> $($returns:tt),*)? {$($statements:tt)*}} => { yul::Statement::FunctionDefinition(yul::FunctionDefinition { name: identifier! {$name}, parameters: identifiers! {$($params)*}, - returns: { - let mut returns = vec![]; - $(returns.push(identifier!{$returns});)* - returns - }, + returns: identifiers! {$($($returns)*)?}, block: block!{$($statements)*}, }) }; @@ -467,6 +463,14 @@ mod tests { ) } + #[test] + fn variable_declaration_multiple_targets() { + assert_eq!( + variable_declaration! {let foo, bar := 42}.to_string(), + "let foo, bar := 42" + ) + } + #[test] fn variable_declaration_function() { assert_eq!( @@ -508,6 +512,11 @@ mod tests { assert_eq!(assignment! {foo := 42}.to_string(), "foo := 42") } + #[test] + fn assignment_multiple_targets() { + assert_eq!(assignment! {foo, bar := 42}.to_string(), "foo, bar := 42") + } + #[test] fn large_assignment() { assert_eq!( @@ -630,6 +639,38 @@ mod tests { ) } + #[test] + fn function_definition_multiple_returns() { + let bit = identifier! {bit}; + + assert_eq!( + function_definition! { + function foo([bit], coin) -> bar, baz { + (let baz := add(bit, coin)) + (bar := hello_world(baz, "hi")) + } + } + .to_string(), + r#"function foo(bit, coin) -> bar, baz { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# + ) + } + + #[test] + fn function_definition_multiple_returns_inserted() { + let returns = identifiers! { bar baz }; + + assert_eq!( + function_definition! { + function foo(bit, coin) -> [returns...] { + (let baz := add(bit, coin)) + (bar := hello_world(baz, "hi")) + } + } + .to_string(), + r#"function foo(bit, coin) -> bar, baz { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# + ) + } + #[test] fn function_definition_no_return() { let bit = identifier! {bit}; From eafe76a06c0075d0755584875796f647fb724c91 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Wed, 9 Jun 2021 19:42:45 -0600 Subject: [PATCH 33/35] Display writes code with indents and line breaks. --- Cargo.toml | 3 + src/lib.rs | 2 + src/shorthand.rs | 144 +++++++++++++++++++++++++++++++++++++---------- src/yul.rs | 124 +++++++++++++++++++++------------------- 4 files changed, 186 insertions(+), 87 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8516e9e..cb95639 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ license = "GPL-3.0" readme = "README.md" description = "Yultsur (or Yülçür) is a toolkit for Yul." keywords = ["yul", "ethereum", "solidity"] + +[dependencies] +indenter = "0.3" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d7727c2..0e078b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +extern crate indenter; + pub mod yul; pub mod validator; pub mod shorthand; diff --git a/src/shorthand.rs b/src/shorthand.rs index 0b311e1..7a69dd4 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -1,7 +1,5 @@ //! This module contains a set of macros for shorthand Yul AST generation. -use crate::yul; - /// Creates a Yul literal. /// /// # Examples @@ -565,7 +563,10 @@ mod tests { (bar(foo)) } .to_string(), - "{ let foo := 42 bar(foo) }" +r#"{ + let foo := 42 + bar(foo) +}"# ) } @@ -577,7 +578,7 @@ mod tests { (bar(foo)) } .to_string(), - "{ let foo := 42 bar(foo) }" + "{\n let foo := 42\n bar(foo)\n}" ) } @@ -589,7 +590,7 @@ mod tests { (bar(foo)) } .to_string(), - "code { let foo := 42 bar(foo) }" + "code {\n let foo := 42\n bar(foo)\n}" ) } @@ -601,7 +602,7 @@ mod tests { (bar(foo)) } .to_string(), - "code { let foo := 42 bar(foo) }" + "code {\n let foo := 42\n bar(foo)\n}" ) } @@ -619,7 +620,12 @@ mod tests { (bar(foo)) } .to_string(), - "{ let foo := 42 let bar := 2 blockchain(_3d) bar(foo) }" +r#"{ + let foo := 42 + let bar := 2 + blockchain(_3d) + bar(foo) +}"# ) } @@ -635,7 +641,10 @@ mod tests { } } .to_string(), - r#"function foo(bit, coin) -> bar { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# +r#"function foo(bit, coin) -> bar { + let baz := add(bit, coin) + bar := hello_world(baz, "hi") +}"# ) } @@ -651,7 +660,10 @@ mod tests { } } .to_string(), - r#"function foo(bit, coin) -> bar, baz { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# +r#"function foo(bit, coin) -> bar, baz { + let baz := add(bit, coin) + bar := hello_world(baz, "hi") +}"# ) } @@ -667,7 +679,10 @@ mod tests { } } .to_string(), - r#"function foo(bit, coin) -> bar, baz { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# +r#"function foo(bit, coin) -> bar, baz { + let baz := add(bit, coin) + bar := hello_world(baz, "hi") +}"# ) } @@ -682,7 +697,10 @@ mod tests { (bar := hello_world(baz, "hi")) } }.to_string(), - r#"function foo(bit, coin) { let baz := add(bit, coin) bar := hello_world(baz, "hi") }"# +r#"function foo(bit, coin) { + let baz := add(bit, coin) + bar := hello_world(baz, "hi") +}"# ) } @@ -707,17 +725,28 @@ mod tests { (bar(bing)) }) }.to_string(), - r#"switch foo(1, "s") case 1 { bar(42) } case 42 { bar(420) baz("block", chain) } default { let bing := bing("bong") bar(bing) } "# +r#"switch foo(1, "s") +case 1 { + bar(42) +} +case 42 { + bar(420) + baz("block", chain) +} +default { + let bing := bing("bong") + bar(bing) +}"# } - #[test] + } - fn switch_no_default() { - let foo = expression! {foo(1, "s")}; - let bing = expression! {bing("bong")}; - let _42 = literal! {42}; + #[test] + fn switch_no_default() { + let foo = expression! {foo(1, "s")}; + let _42 = literal! {42}; - assert_eq! { - switch! { + assert_eq! { + switch! { switch [foo] (case 1 { (bar(42)) @@ -727,9 +756,16 @@ mod tests { (baz("block", chain)) }) }.to_string(), - r#"switch foo(1, "s") case 1 { bar(42) } case 42 { bar(420) baz("block", chain) } "# - } - } } +r#"switch foo(1, "s") +case 1 { + bar(42) +} +case 42 { + bar(420) + baz("block", chain) +}"# + } + } #[test] fn statements() { @@ -755,7 +791,7 @@ mod tests { [best_statement] (if 0 { (call(42)) }) }.iter().map(|s| s.to_string()).collect::>().join(" "), - r#"let kung := foo let a := my_function("hello", world("42")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := "42 bar 42" if 0 { call(42) }"# + "let kung := foo let a := my_function(\"hello\", world(\"42\")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := \"42 bar 42\" if 0 {\n call(42)\n}" ) } @@ -783,7 +819,16 @@ mod tests { [foo_func] [bar_func] }.to_string(), - r#"{ let a := b function foo(test, one, two) { log("hello_world") } function foo(two, three, four) -> return_val { let a := test(two, three, four) return_val := a } }"# +r#"{ + let a := b + function foo(test, one, two) { + log("hello_world") + } + function foo(two, three, four) -> return_val { + let a := test(two, three, four) + return_val := a + } +}"# ) } @@ -803,7 +848,18 @@ mod tests { }) ) }.to_string(), - r#"{ let a := 40 let b := 2 switch add(a, b) case 42 { let c := 2 } case "3d" { foo(0) bar("test") } }"# +r#"{ + let a := 40 + let b := 2 + switch add(a, b) + case 42 { + let c := 2 + } + case "3d" { + foo(0) + bar("test") + } +}"# ) } @@ -831,7 +887,17 @@ mod tests { [cases...] [default] }.to_string(), - r#"switch cat("f", s) case "foo" { test(42) } case "bar" { hello_world(42) a := b } default { c := 4 } "# +r#"switch cat("f", s) +case "foo" { + test(42) +} +case "bar" { + hello_world(42) + a := b +} +default { + c := 4 +}"# ) } @@ -839,7 +905,9 @@ mod tests { fn _if() { assert_eq!( _if! { if (eq(foo, 0)) { (let a := b) } }.to_string(), - "if eq(foo, 0) { let a := b }" +r#"if eq(foo, 0) { + let a := b +}"# ) } @@ -852,7 +920,16 @@ mod tests { (result := mul(result, base)) } }.to_string(), - "for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) }" +r#"for { + let i := 0 +} +lt(i, exponent) +{ + i := add(i, 1) +} +{ + result := mul(result, base) +}"# ) } @@ -865,7 +942,16 @@ mod tests { (result := mul(result, base)) } }.to_string(), - "for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) }" +r#"for { + let i := 0 +} +lt(i, exponent) +{ + i := add(i, 1) +} +{ + result := mul(result, base) +}"# ) } diff --git a/src/yul.rs b/src/yul.rs index 658bd0b..49e928d 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -1,4 +1,6 @@ use std::fmt; +use std::fmt::Write; +use indenter::indented; #[derive(Hash, Clone, PartialEq, Debug)] pub enum Type { @@ -147,27 +149,9 @@ impl fmt::Display for Type { } } -impl Identifier { - pub fn new(identifier: &str) -> Self { - Identifier { - identifier: identifier.to_string(), - yultype: None, - } - } -} - -impl Literal { - pub fn new(literal: &str) -> Self { - Literal { - literal: literal.to_string(), - yultype: None, - } - } -} - impl fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", self.literal)); + write!(f, "{}", self.literal)?; if let Some(yultype) = &self.yultype { write!(f, ":{}", yultype) } else { @@ -178,7 +162,7 @@ impl fmt::Display for Literal { impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", self.identifier)); + write!(f, "{}", self.identifier)?; if let Some(yultype) = &self.yultype { write!(f, ":{}", yultype) } else { @@ -189,8 +173,8 @@ impl fmt::Display for Identifier { impl fmt::Display for FunctionCall { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}(", self.identifier)); - try!(write!( + write!(f, "{}(", self.identifier)?; + write!( f, "{}", self.arguments @@ -198,14 +182,22 @@ impl fmt::Display for FunctionCall { .map(|argument| format!("{}", argument)) .collect::>() .join(", ") - )); + )?; write!(f, ")") } } impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "object \"{}\" {{ {} {} {} }}", self.name, self.code, self.objects.iter().map(|o| o.to_string()).collect::>().join(" "), self.data.iter().map(|o| o.to_string()).collect::>().join(" ")) + write!(f, "object \"{}\" {{\n", self.name)?; + write!(indented(f), "{}\n", self.code)?; + for object in self.objects.iter() { + write!(indented(f), "{}\n", object)?; + } + for data in self.data.iter() { + write!(indented(f), "{}\n", data)?; + } + write!(f, "}}") } } @@ -223,8 +215,8 @@ impl fmt::Display for Code { impl fmt::Display for FunctionDefinition { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "function {}(", self.name)); - try!(write!( + write!(f, "function {}(", self.name)?; + write!( f, "{}", self.parameters @@ -232,11 +224,11 @@ impl fmt::Display for FunctionDefinition { .map(|identifier| format!("{}", identifier)) .collect::>() .join(", ") - )); - try!(write!(f, ")")); + )?; + write!(f, ")")?; if self.returns.len() > 0 { - try!(write!(f, " -> ")); - try!(write!( + write!(f, " -> ")?; + write!( f, "{}", self.returns @@ -244,7 +236,7 @@ impl fmt::Display for FunctionDefinition { .map(|identifier| format!("{}", identifier)) .collect::>() .join(", ") - )); + )?; } write!(f, " {}", self.block) } @@ -256,8 +248,8 @@ impl fmt::Display for VariableDeclaration { if self.identifiers.len() == 0 { panic!("VariableDeclaration must have identifiers") } - try!(write!(f, "let ")); - try!(write!( + write!(f, "let ")?; + write!( f, "{}", self.identifiers @@ -265,7 +257,7 @@ impl fmt::Display for VariableDeclaration { .map(|identifier| format!("{}", identifier)) .collect::>() .join(", ") - )); + )?; if let Some(expression) = &self.expression { write!(f, " := {}", expression) } else { @@ -280,7 +272,7 @@ impl fmt::Display for Assignment { if self.identifiers.len() == 0 { panic!("Assignment must have identifiers") } - try!(write!( + write!( f, "{}", self.identifiers @@ -288,7 +280,7 @@ impl fmt::Display for Assignment { .map(|identifier| format!("{}", identifier)) .collect::>() .join(", ") - )); + )?; write!(f, " := {}", self.expression) } } @@ -325,9 +317,9 @@ impl fmt::Display for Case { impl fmt::Display for Switch { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "switch {} ", self.expression)); + write!(f, "switch {}", self.expression)?; for case in &self.cases { - try!(write!(f, "{} ", case)); + write!(f, "\n{}", case)?; } write!(f, "") } @@ -337,7 +329,7 @@ impl fmt::Display for ForLoop { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "for {} {} {} {}", + "for {}\n{}\n{}\n{}", self.pre, self.condition, self.post, self.body ) } @@ -367,11 +359,11 @@ impl fmt::Display for Statement { impl fmt::Display for Block { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{{")); - for (_, statement) in self.statements.iter().enumerate() { - try!(write!(f, " {}", statement)); + write!(f, "{{\n")?; + for statement in self.statements.iter() { + write!(indented(f), "{}\n", statement)?; } - write!(f, " }}") + write!(f, "}}") } } @@ -478,13 +470,13 @@ mod tests { }), block: Block { statements: vec![] }, }.to_string(), - "if literal { }" + "if literal {\n}" ); } #[test] fn block_empty() { - assert_eq!(Block { statements: vec![] }.to_string(), "{ }"); + assert_eq!(Block { statements: vec![] }.to_string(), "{\n}"); } #[test] @@ -493,7 +485,7 @@ mod tests { Block { statements: vec![Statement::Block(Block { statements: vec![] })], }.to_string(), - "{ { } }" + "{\n {\n }\n}" ); } @@ -506,7 +498,7 @@ mod tests { yultype: None, }))], }.to_string(), - "{ literal }" + "{\n literal\n}" ); } @@ -631,7 +623,14 @@ mod tests { ], data: vec![] }.to_string(), - "object \"Name\" { code { } object \"Test\" { code { } } }" +r#"object "Name" { + code { + } + object "Test" { + code { + } + } +}"# ); } @@ -654,7 +653,16 @@ mod tests { ], data: vec![Data {name: "Data2".to_string(), value: "Value2".to_string() }], }.to_string(), - "object \"Name\" { code { } object \"Test\" { code { } data \"Data1\" \"Value1\" } data \"Data2\" \"Value2\" }" +r#"object "Name" { + code { + } + object "Test" { + code { + } + data "Data1" "Value1" + } + data "Data2" "Value2" +}"# ); } @@ -676,7 +684,7 @@ mod tests { Code { block: Block { statements: vec![] }, }.to_string(), - "code { }" + "code {\n}" ); } @@ -692,7 +700,7 @@ mod tests { returns: vec![], block: Block { statements: vec![] }, }.to_string(), - "function name() { }" + "function name() {\n}" ); } @@ -711,7 +719,7 @@ mod tests { returns: vec![], block: Block { statements: vec![] }, }.to_string(), - "function name(a) { }" + "function name(a) {\n}" ); } @@ -730,7 +738,7 @@ mod tests { }], block: Block { statements: vec![] }, }.to_string(), - "function name() -> a { }" + "function name() -> a {\n}" ); } @@ -764,7 +772,7 @@ mod tests { ], block: Block { statements: vec![] }, }.to_string(), - "function name(a, b) -> c, d { }" + "function name(a, b) -> c, d {\n}" ); } @@ -778,7 +786,7 @@ mod tests { }), block: Block { statements: vec![] }, }.to_string(), - "case literal { }" + "case literal {\n}" ); } @@ -789,7 +797,7 @@ mod tests { literal: None, block: Block { statements: vec![] }, }.to_string(), - "default { }" + "default {\n}" ); } @@ -815,7 +823,7 @@ mod tests { }, ], }.to_string(), - "switch 3 case 1 { } default { } " + "switch 3\ncase 1 {\n}\ndefault {\n}" ); } @@ -831,7 +839,7 @@ mod tests { post: Block { statements: vec![] }, body: Block { statements: vec![] }, }.to_string(), - "for { } 1 { } { }" + "for {\n}\n1\n{\n}\n{\n}" ); } } From 5d4f35a67258e1ba0761722cc535a1580a292aef Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Mon, 28 Jun 2021 18:58:20 -0600 Subject: [PATCH 34/35] Small code blocks and for loops are printed more neatly. --- src/shorthand.rs | 52 ++++++++++++------------------------------- src/yul.rs | 57 ++++++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 66 deletions(-) diff --git a/src/shorthand.rs b/src/shorthand.rs index 7a69dd4..c59f528 100644 --- a/src/shorthand.rs +++ b/src/shorthand.rs @@ -726,9 +726,7 @@ r#"function foo(bit, coin) { }) }.to_string(), r#"switch foo(1, "s") -case 1 { - bar(42) -} +case 1 { bar(42) } case 42 { bar(420) baz("block", chain) @@ -757,9 +755,7 @@ default { }) }.to_string(), r#"switch foo(1, "s") -case 1 { - bar(42) -} +case 1 { bar(42) } case 42 { bar(420) baz("block", chain) @@ -791,7 +787,7 @@ case 42 { [best_statement] (if 0 { (call(42)) }) }.iter().map(|s| s.to_string()).collect::>().join(" "), - "let kung := foo let a := my_function(\"hello\", world(\"42\")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := \"42 bar 42\" if 0 {\n call(42)\n}" + "let kung := foo let a := my_function(\"hello\", world(\"42\")) b := add(a, 2) ip := man() let value := food(1, 2) let another_value := 42 foo := \"42 bar 42\" if 0 { call(42) }" ) } @@ -821,9 +817,7 @@ case 42 { }.to_string(), r#"{ let a := b - function foo(test, one, two) { - log("hello_world") - } + function foo(test, one, two) { log("hello_world") } function foo(two, three, four) -> return_val { let a := test(two, three, four) return_val := a @@ -852,9 +846,7 @@ r#"{ let a := 40 let b := 2 switch add(a, b) - case 42 { - let c := 2 - } + case 42 { let c := 2 } case "3d" { foo(0) bar("test") @@ -888,16 +880,12 @@ r#"{ [default] }.to_string(), r#"switch cat("f", s) -case "foo" { - test(42) -} +case "foo" { test(42) } case "bar" { hello_world(42) a := b } -default { - c := 4 -}"# +default { c := 4 }"# ) } @@ -905,9 +893,7 @@ default { fn _if() { assert_eq!( _if! { if (eq(foo, 0)) { (let a := b) } }.to_string(), -r#"if eq(foo, 0) { - let a := b -}"# + "if eq(foo, 0) { let a := b }" ) } @@ -918,17 +904,12 @@ r#"if eq(foo, 0) { for { (let i := 0) } (lt(i, exponent)) { (i := add(i, 1)) } { (result := mul(result, base)) + (call()) } }.to_string(), -r#"for { - let i := 0 -} -lt(i, exponent) -{ - i := add(i, 1) -} -{ +r#"for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) + call() }"# ) } @@ -940,17 +921,12 @@ lt(i, exponent) for { (let i := 0) } (lt(i, exponent)) { (i := add(i, 1)) } { (result := mul(result, base)) + (call()) } }.to_string(), -r#"for { - let i := 0 -} -lt(i, exponent) -{ - i := add(i, 1) -} -{ +r#"for { let i := 0 } lt(i, exponent) { i := add(i, 1) } { result := mul(result, base) + call() }"# ) } diff --git a/src/yul.rs b/src/yul.rs index 49e928d..e82eb69 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -329,7 +329,7 @@ impl fmt::Display for ForLoop { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "for {}\n{}\n{}\n{}", + "for {} {} {} {}", self.pre, self.condition, self.post, self.body ) } @@ -359,11 +359,17 @@ impl fmt::Display for Statement { impl fmt::Display for Block { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{\n")?; - for statement in self.statements.iter() { - write!(indented(f), "{}\n", statement)?; + if self.statements.len() == 0 { + write!(f, "{{ }}") + }else if self.statements.len() == 1 { + write!(f, "{{ {} }}", self.statements[0]) + } else { + write!(f, "{{\n")?; + for statement in self.statements.iter() { + write!(indented(f), "{}\n", statement)?; + } + write!(f, "}}") } - write!(f, "}}") } } @@ -470,13 +476,13 @@ mod tests { }), block: Block { statements: vec![] }, }.to_string(), - "if literal {\n}" + "if literal { }" ); } #[test] fn block_empty() { - assert_eq!(Block { statements: vec![] }.to_string(), "{\n}"); + assert_eq!(Block { statements: vec![] }.to_string(), "{ }"); } #[test] @@ -485,7 +491,7 @@ mod tests { Block { statements: vec![Statement::Block(Block { statements: vec![] })], }.to_string(), - "{\n {\n }\n}" + "{ { } }" ); } @@ -494,11 +500,11 @@ mod tests { assert_eq!( Block { statements: vec![Statement::Expression(Expression::Literal(Literal { - literal: "literal".to_string(), + literal: "42".to_string(), yultype: None, }))], }.to_string(), - "{\n literal\n}" + "{ 42 }" ); } @@ -624,11 +630,9 @@ mod tests { data: vec![] }.to_string(), r#"object "Name" { - code { - } + code { } object "Test" { - code { - } + code { } } }"# ); @@ -654,11 +658,9 @@ r#"object "Name" { data: vec![Data {name: "Data2".to_string(), value: "Value2".to_string() }], }.to_string(), r#"object "Name" { - code { - } + code { } object "Test" { - code { - } + code { } data "Data1" "Value1" } data "Data2" "Value2" @@ -677,14 +679,13 @@ r#"object "Name" { ); } - #[test] fn code_basic() { assert_eq!( Code { block: Block { statements: vec![] }, }.to_string(), - "code {\n}" + "code { }" ); } @@ -700,7 +701,7 @@ r#"object "Name" { returns: vec![], block: Block { statements: vec![] }, }.to_string(), - "function name() {\n}" + "function name() { }" ); } @@ -719,7 +720,7 @@ r#"object "Name" { returns: vec![], block: Block { statements: vec![] }, }.to_string(), - "function name(a) {\n}" + "function name(a) { }" ); } @@ -738,7 +739,7 @@ r#"object "Name" { }], block: Block { statements: vec![] }, }.to_string(), - "function name() -> a {\n}" + "function name() -> a { }" ); } @@ -772,7 +773,7 @@ r#"object "Name" { ], block: Block { statements: vec![] }, }.to_string(), - "function name(a, b) -> c, d {\n}" + "function name(a, b) -> c, d { }" ); } @@ -786,7 +787,7 @@ r#"object "Name" { }), block: Block { statements: vec![] }, }.to_string(), - "case literal {\n}" + "case literal { }" ); } @@ -797,7 +798,7 @@ r#"object "Name" { literal: None, block: Block { statements: vec![] }, }.to_string(), - "default {\n}" + "default { }" ); } @@ -823,7 +824,7 @@ r#"object "Name" { }, ], }.to_string(), - "switch 3\ncase 1 {\n}\ndefault {\n}" + "switch 3\ncase 1 { }\ndefault { }" ); } @@ -839,7 +840,7 @@ r#"object "Name" { post: Block { statements: vec![] }, body: Block { statements: vec![] }, }.to_string(), - "for {\n}\n1\n{\n}\n{\n}" + "for { } 1 { } { }" ); } } From ae854702e90b2c70e5c19961167f2b6f2aff32d2 Mon Sep 17 00:00:00 2001 From: Grant Wuerker Date: Fri, 2 Jul 2021 13:57:36 -0600 Subject: [PATCH 35/35] Ord trait on AST elements. --- src/yul.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/yul.rs b/src/yul.rs index e82eb69..21501b0 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -2,7 +2,7 @@ use std::fmt; use std::fmt::Write; use indenter::indented; -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub enum Type { Bool, Uint256, @@ -18,30 +18,30 @@ pub enum Type { Custom(String), } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Block { pub statements: Vec, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Literal { pub literal: String, pub yultype: Option, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Identifier { pub identifier: String, pub yultype: Option, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct FunctionCall { pub identifier: Identifier, pub arguments: Vec, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Object { pub name: Identifier, pub code: Code, @@ -49,18 +49,18 @@ pub struct Object { pub data: Vec, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Data { pub name: String, pub value: String, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Code { pub block: Block, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct FunctionDefinition { pub name: Identifier, pub parameters: Vec, @@ -68,44 +68,44 @@ pub struct FunctionDefinition { pub block: Block, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct VariableDeclaration { pub identifiers: Vec, pub expression: Option, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Assignment { pub identifiers: Vec, pub expression: Expression, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub enum Expression { Literal(Literal), Identifier(Identifier), FunctionCall(FunctionCall), } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct If { pub expression: Expression, pub block: Block, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Case { pub literal: Option, pub block: Block, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct Switch { pub expression: Expression, pub cases: Vec, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub struct ForLoop { pub pre: Block, pub condition: Expression, @@ -113,7 +113,7 @@ pub struct ForLoop { pub body: Block, } -#[derive(Hash, Clone, PartialEq, Debug)] +#[derive(Hash, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)] pub enum Statement { Block(Block), Object(Object),