From 7f7ade50b59c2d812054a115abd48c75801395f3 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 12 Nov 2018 10:28:39 +0100 Subject: [PATCH 1/2] TypedIdentifier --- src/validator.rs | 83 ++++++++++++-------- src/yul.rs | 192 ++++++++++++++++++++--------------------------- 2 files changed, 135 insertions(+), 140 deletions(-) diff --git a/src/validator.rs b/src/validator.rs index fc4a54d..c6f1e7e 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -1,24 +1,25 @@ use yul::*; +pub type Result = std::result::Result; + pub trait Validator: Send + Sync { - fn validate(&self, typed: bool) -> Result<(), String>; + fn validate(&self, typed: bool) -> Result<()>; } impl Validator for Type { - fn validate(&self, typed: bool) -> Result<(), String> { - match *self { - // FIXME: validate that custom type name doesn't have space, etc. - Type::Custom(ref custom) => Ok(()), + fn validate(&self, typed: bool) -> Result<()> { + match self { + Type::Custom(custom) => custom.validate(typed), _ => Ok(()), } } } impl Validator for Literal { - fn validate(&self, typed: bool) -> Result<(), String> { + fn validate(&self, typed: bool) -> Result<()> { if typed { if self.yultype == None { - return Err("Type must be set".to_string()); + return Err("Type must be set"); } if let Some(yultype) = &self.yultype { yultype.validate(typed)?; @@ -30,21 +31,44 @@ impl Validator for Literal { } impl Validator for Identifier { - fn validate(&self, typed: bool) -> Result<(), String> { - if typed { - if self.yultype == None { - return Err("Type must be set".to_string()); + fn validate(&self, _: bool) -> Result<()> { + let mut bytes = self.0.bytes(); + + match bytes.next() { + None => return Err("Identifier label cannot be empty"), + Some(byte) => match byte { + b'a'...b'z' | + b'A'...b'Z' | + b'_' | b'$' => {}, + _ => return Err("Identifier label must start with a letter, `_`, or `$` character") } - if let Some(yultype) = &self.yultype { - yultype.validate(typed)?; + } + + for byte in bytes { + match byte { + b'a'...b'z' | + b'A'...b'Z' | + b'0'...b'9' | + b'_' | b'$' => {}, + _ => return Err("Identifier label can contain only letters, digits, `_`, or `$` characters") } } + Ok(()) } } +impl Validator for TypedIdentifier { + fn validate(&self, typed: bool) -> Result<()> { + if typed { + self.yultype.validate(typed)?; + } + self.identifier.validate(typed) + } +} + impl Validator for Block { - fn validate(&self, typed: bool) -> Result<(), String> { + fn validate(&self, typed: bool) -> Result<()> { for statement in &self.statements { statement.validate(typed)?; } @@ -53,7 +77,7 @@ impl Validator for Block { } impl Validator for Statement { - fn validate(&self, typed: bool) -> Result<(), String> { + fn validate(&self, typed: bool) -> Result<()> { match *self { Statement::Switch(ref switch) => switch.validate(typed), _ => Ok(()), @@ -62,11 +86,11 @@ impl Validator for Statement { } impl Validator for Case { - fn validate(&self, typed: bool) -> Result<(), String> { + fn validate(&self, typed: bool) -> Result<()> { if let Some(literal) = &self.literal { literal.validate(typed)?; if literal.literal.len() == 0 { - return Err("Case literal cannot be empty".to_string()); + return Err("Case literal cannot be empty"); } } Ok(()) @@ -74,7 +98,7 @@ impl Validator for Case { } impl Validator for Switch { - fn validate(&self, typed: bool) -> Result<(), String> { + fn validate(&self, typed: bool) -> Result<()> { for case in &self.cases { case.validate(typed)?; } @@ -88,13 +112,13 @@ mod tests { #[test] fn basic_type() { - assert!(!Type::Bool.validate(true).is_err(), ""); + assert!(Type::Bool.validate(true).is_ok(), ""); } #[test] fn custom_type() { assert!( - !Type::Custom("test".to_string()).validate(true).is_err(), + Type::Custom("test".into()).validate(true).is_ok(), "" ); } @@ -102,7 +126,7 @@ mod tests { #[test] fn invalid_custom_type() { assert!( - !Type::Custom("test invalid type".to_string()) + Type::Custom("test invalid type".into()) .validate(true) .is_err(), "" @@ -112,11 +136,11 @@ mod tests { #[test] fn untyped_literal() { assert!( - !Literal { + Literal { literal: "test".to_string(), yultype: None }.validate(false) - .is_err(), + .is_ok(), "" ); assert!( @@ -132,11 +156,11 @@ mod tests { #[test] fn typed_literal() { assert!( - !Literal { + Literal { literal: "test".to_string(), yultype: Some(Type::Bool) }.validate(true) - .is_err(), + .is_ok(), "" ); } @@ -159,12 +183,9 @@ mod tests { #[test] fn complex_example() { assert!( - !Block { + Block { statements: vec![Statement::Switch(Switch { - expression: Expression::Identifier(Identifier { - identifier: "shouldbebool".to_string(), - yultype: Some(Type::Bool), - }), + expression: Expression::Identifier("shouldbebool".into()), cases: vec![ Case { literal: Some(Literal { @@ -180,7 +201,7 @@ mod tests { ], })], }.validate(false) - .is_err(), + .is_ok(), "" ); } diff --git a/src/yul.rs b/src/yul.rs index 20a8c24..54a9c7a 100644 --- a/src/yul.rs +++ b/src/yul.rs @@ -13,7 +13,7 @@ pub enum Type { Int64, Int32, Int8, - Custom(String), + Custom(Identifier), } #[derive(Hash, Clone, PartialEq, Debug)] @@ -28,9 +28,18 @@ pub struct Literal { } #[derive(Hash, Clone, PartialEq, Debug)] -pub struct Identifier { - pub identifier: String, - pub yultype: Option, +pub struct Identifier(pub String); + +impl> From for Identifier { + fn from(string: S) -> Self { + Identifier(string.into()) + } +} + +#[derive(Hash, Clone, PartialEq, Debug)] +pub struct TypedIdentifier { + pub identifier: Identifier, + pub yultype: Type, } #[derive(Hash, Clone, PartialEq, Debug)] @@ -42,14 +51,14 @@ pub struct FunctionCall { #[derive(Hash, Clone, PartialEq, Debug)] pub struct FunctionDefinition { pub name: Identifier, - pub parameters: Vec, - pub returns: Vec, + pub parameters: Vec, + pub returns: Vec, pub block: Block, } #[derive(Hash, Clone, PartialEq, Debug)] pub struct VariableDeclaration { - pub identifiers: Vec, + pub identifiers: Vec, pub expression: Option, } @@ -127,10 +136,7 @@ impl fmt::Display for Type { impl Identifier { pub fn new(identifier: &str) -> Self { - Identifier { - identifier: identifier.to_string(), - yultype: None, - } + identifier.into() } } @@ -156,12 +162,13 @@ impl fmt::Display for Literal { impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", self.identifier)); - if let Some(yultype) = &self.yultype { - write!(f, ":{}", yultype) - } else { - write!(f, "") - } + write!(f, "{}", self.0) + } +} + +impl fmt::Display for TypedIdentifier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.identifier, self.yultype) } } @@ -363,7 +370,7 @@ mod tests { assert_eq!( Literal { literal: "testliteral".to_string(), - yultype: Some(Type::Custom("memptr".to_string())), + yultype: Some(Type::Custom("memptr".into())), }.to_string(), "testliteral:memptr" ); @@ -372,31 +379,28 @@ mod tests { #[test] fn identifier() { assert_eq!( - Identifier { - identifier: "testidentifier".to_string(), - yultype: None, - }.to_string(), + Identifier::new("testidentifier").to_string(), "testidentifier" ); } #[test] - fn identifier_typed() { + fn typed_identifier() { assert_eq!( - Identifier { - identifier: "testidentifier".to_string(), - yultype: Some(Type::Uint256), + TypedIdentifier { + identifier: "testidentifier".into(), + yultype: Type::Uint256, }.to_string(), "testidentifier:u256" ); } #[test] - fn identifierr_custom_typed() { + fn typed_identifier_custom() { assert_eq!( - Identifier { - identifier: "testidentifier".to_string(), - yultype: Some(Type::Custom("memptr".to_string())), + TypedIdentifier { + identifier: "testidentifier".into(), + yultype: Type::Custom("memptr".into()), }.to_string(), "testidentifier:memptr" ); @@ -406,15 +410,9 @@ mod tests { fn functioncall() { assert_eq!( FunctionCall { - identifier: Identifier { - identifier: "test".to_string(), - yultype: None, - }, + identifier: "test".into(), arguments: vec![ - Expression::Identifier(Identifier { - identifier: "test".to_string(), - yultype: None, - }), + Expression::Identifier("test".into()), Expression::Literal(Literal { literal: "literal".to_string(), yultype: None, @@ -471,10 +469,7 @@ mod tests { fn assignment_single() { assert_eq!( Assignment { - identifiers: vec![Identifier { - identifier: "a".to_string(), - yultype: None, - }], + identifiers: vec![Identifier::new("a")], expression: Expression::Literal(Literal { literal: "1".to_string(), yultype: None, @@ -489,18 +484,9 @@ mod tests { assert_eq!( Assignment { identifiers: vec![ - Identifier { - identifier: "a".to_string(), - yultype: None, - }, - Identifier { - identifier: "b".to_string(), - yultype: None, - }, - Identifier { - identifier: "c".to_string(), - yultype: None, - }, + Identifier::new("a"), + Identifier::new("b"), + Identifier::new("c"), ], expression: Expression::Literal(Literal { literal: "1".to_string(), @@ -515,13 +501,13 @@ mod tests { fn variabledeclaration_empty() { assert_eq!( VariableDeclaration { - identifiers: vec![Identifier { - identifier: "a".to_string(), - yultype: None, + identifiers: vec![TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint256, }], expression: None, }.to_string(), - "let a" + "let a:u256" ); } @@ -529,16 +515,16 @@ mod tests { fn variabledeclaration_single() { assert_eq!( VariableDeclaration { - identifiers: vec![Identifier { - identifier: "a".to_string(), - yultype: None, + identifiers: vec![TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint256, }], expression: Some(Expression::Literal(Literal { literal: "1".to_string(), yultype: None, })), }.to_string(), - "let a := 1" + "let a:u256 := 1" ); } @@ -547,17 +533,17 @@ mod tests { assert_eq!( VariableDeclaration { identifiers: vec![ - Identifier { - identifier: "a".to_string(), - yultype: None, + TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint256, }, - Identifier { - identifier: "b".to_string(), - yultype: None, + TypedIdentifier { + identifier: "b".into(), + yultype: Type::Uint64, }, - Identifier { - identifier: "c".to_string(), - yultype: None, + TypedIdentifier { + identifier: "c".into(), + yultype: Type::Uint8, }, ], expression: Some(Expression::Literal(Literal { @@ -565,7 +551,7 @@ mod tests { yultype: None, })), }.to_string(), - "let a, b, c := 1" + "let a:u256, b:u64, c:u8 := 1" ); } @@ -573,10 +559,7 @@ mod tests { fn functiondefinition_basic() { assert_eq!( FunctionDefinition { - name: Identifier { - identifier: "name".to_string(), - yultype: None, - }, + name: "name".into(), parameters: vec![], returns: vec![], block: Block { statements: vec![] }, @@ -589,18 +572,15 @@ mod tests { fn functiondefinition_single_arg() { assert_eq!( FunctionDefinition { - name: Identifier { - identifier: "name".to_string(), - yultype: None, - }, - parameters: vec![Identifier { - identifier: "a".to_string(), - yultype: None, + name: "name".into(), + parameters: vec![TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint8, }], returns: vec![], block: Block { statements: vec![] }, }.to_string(), - "function name(a) { }" + "function name(a:u8) { }" ); } @@ -608,18 +588,15 @@ mod tests { fn functiondefinition_single_ret() { assert_eq!( FunctionDefinition { - name: Identifier { - identifier: "name".to_string(), - yultype: None, - }, + name: "name".into(), parameters: vec![], - returns: vec![Identifier { - identifier: "a".to_string(), - yultype: None, + returns: vec![TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint8, }], block: Block { statements: vec![] }, }.to_string(), - "function name() -> a { }" + "function name() -> a:u8 { }" ); } @@ -627,33 +604,30 @@ mod tests { fn functiondefinition_multi() { assert_eq!( FunctionDefinition { - name: Identifier { - identifier: "name".to_string(), - yultype: None, - }, + name: "name".into(), parameters: vec![ - Identifier { - identifier: "a".to_string(), - yultype: None, + TypedIdentifier { + identifier: "a".into(), + yultype: Type::Uint8, }, - Identifier { - identifier: "b".to_string(), - yultype: None, + TypedIdentifier { + identifier: "b".into(), + yultype: Type::Uint8, }, ], returns: vec![ - Identifier { - identifier: "c".to_string(), - yultype: None, + TypedIdentifier { + identifier: "c".into(), + yultype: Type::Uint8, }, - Identifier { - identifier: "d".to_string(), - yultype: None, + TypedIdentifier { + identifier: "d".into(), + yultype: Type::Uint8, }, ], block: Block { statements: vec![] }, }.to_string(), - "function name(a, b) -> c, d { }" + "function name(a:u8, b:u8) -> c:u8, d:u8 { }" ); } From fd302a7fc5c5e9430d794b7e0c3317c255bfa63f Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 12 Nov 2018 10:36:49 +0100 Subject: [PATCH 2/2] Added a test --- src/validator.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/validator.rs b/src/validator.rs index c6f1e7e..698aa76 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -110,6 +110,21 @@ impl Validator for Switch { mod tests { use super::*; + #[test] + fn identifiers() { + assert!(Identifier::new("foo").validate(true).is_ok()); + assert!(Identifier::new("Foo").validate(true).is_ok()); + assert!(Identifier::new("_foo").validate(true).is_ok()); + assert!(Identifier::new("$foo").validate(true).is_ok()); + assert!(Identifier::new("f00").validate(true).is_ok()); + + assert!(Identifier::new("foo_bar").validate(true).is_ok()); + assert!(Identifier::new("FooBar").validate(true).is_ok()); + + assert!(Identifier::new("1foo").validate(true).is_err()); + assert!(Identifier::new("#foo").validate(true).is_err()); + } + #[test] fn basic_type() { assert!(Type::Bool.validate(true).is_ok(), "");