From 7fa0c9e829e4d1e218464a50de44283ef1522053 Mon Sep 17 00:00:00 2001 From: 0xrusowsky <0xrusowsky@proton.me> Date: Tue, 26 Dec 2023 20:43:48 +0100 Subject: [PATCH 1/5] chore: error when defining a label more than once --- huff_parser/src/lib.rs | 19 +++++++++++++++++++ huff_parser/tests/labels.rs | 35 ++++++++++++++++++++++++++++++++++- huff_utils/src/error.rs | 10 ++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index 48e88c0e..bc9dab54 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -13,6 +13,7 @@ use huff_utils::{ types::*, }; use regex::Regex; +use std::collections::HashSet; /// The Parser #[derive(Debug, Clone)] @@ -185,6 +186,22 @@ impl Parser { std::mem::discriminant(&self.current_token.kind) == std::mem::discriminant(&kind) } + /// Checks whether the input label is unique. + /// If so, it will be added to the label set. Otherwise, an error will be returned. + fn check_label(&self, label: &str, label_set: &mut HashSet) -> Result<(), ParserError> { + if label_set.contains(label) { + tracing::error!(target: "parser", "DUPLICATED LABEL NAME: {}", label); + return Err(ParserError { + kind: ParserErrorKind::DuplicateLabel(label.to_string()), + hint: Some(format!("Duplicated label name: \"{label}\"")), + spans: AstSpan(self.spans.clone()), + }) + } else { + label_set.insert(label.to_string()); + Ok(()) + } + } + /// Consumes the next token. pub fn consume(&mut self) { self.spans.push(self.current_token.span.clone()); @@ -539,6 +556,7 @@ impl Parser { /// Only HEX, OPCODES, labels, builtins, and MACRO calls should be authorized. pub fn parse_body(&mut self) -> Result, ParserError> { let mut statements: Vec = Vec::new(); + let mut labels: HashSet = HashSet::new(); self.match_kind(TokenKind::OpenBrace)?; tracing::info!(target: "parser", "PARSING MACRO BODY"); while !self.check(TokenKind::CloseBrace) { @@ -634,6 +652,7 @@ impl Parser { TokenKind::Label(l) => { let mut curr_spans = vec![self.current_token.span.clone()]; self.consume(); + self.check_label(&l, &mut labels)?; let inner_statements: Vec = self.parse_label()?; inner_statements.iter().for_each(|a| curr_spans.extend_from_slice(&a.span.0)); tracing::info!(target: "parser", "PARSED LABEL \"{}\" INSIDE MACRO WITH {} STATEMENTS.", l, inner_statements.len()); diff --git a/huff_parser/tests/labels.rs b/huff_parser/tests/labels.rs index 0ddbe05e..053bfed0 100644 --- a/huff_parser/tests/labels.rs +++ b/huff_parser/tests/labels.rs @@ -1,6 +1,10 @@ use huff_lexer::*; use huff_parser::*; -use huff_utils::{evm::Opcode, prelude::*}; +use huff_utils::{ + evm::Opcode, + error::ParserErrorKind, + prelude::* +}; #[test] fn multiline_labels() { @@ -433,3 +437,32 @@ pub fn builtins_under_labels() { assert_eq!(s.span, md_expected.statements[i].span); } } + + +#[test] +fn duplicated_labels() { + let source = r#" + #define macro HELLO_WORLD() = takes(3) returns(0) { + 0x00 mstore + 0x01 0x02 add + dup_label: + HELLO() + 0x00 0x00 revert + cool_label: + HELLO() + 0x00 0x00 return + dup_label: + HELLO() + 0x00 0x00 return + } + "#; + let flattened_source = FullFileSource { source, file: None, spans: vec![] }; + let lexer = Lexer::new(flattened_source.source); + let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::>(); + let mut parser = Parser::new(tokens, None); + + // Grab the first macro + let parse_result = parser.parse(); + assert!(parse_result.is_err()); + assert_eq!(parse_result.unwrap_err().kind, ParserErrorKind::DuplicateLabel("dup_label".to_string())); +} \ No newline at end of file diff --git a/huff_utils/src/error.rs b/huff_utils/src/error.rs index 9ecb2631..22838676 100644 --- a/huff_utils/src/error.rs +++ b/huff_utils/src/error.rs @@ -63,6 +63,8 @@ pub enum ParserErrorKind { InvalidDecoratorFlag(String), /// Invalid decorator flag argument InvalidDecoratorFlagArg(TokenKind), + /// Duplicate label + DuplicateLabel(String), } /// A Lexing Error @@ -488,6 +490,14 @@ impl fmt::Display for CompilerError { pe.spans.error(pe.hint.as_ref()) ) } + ParserErrorKind::DuplicateLabel(label) => { + write!( + f, + "\nError: Duplicate label: \"{}\" \n{}\n", + label, + pe.spans.error(pe.hint.as_ref()) + ) + } }, CompilerError::PathBufRead(os_str) => { write!( From e30f16893ea30a8d10b90e6ca663c9264dcb8147 Mon Sep 17 00:00:00 2001 From: 0xrusowsky <0xrusowsky@proton.me> Date: Tue, 26 Dec 2023 20:54:41 +0100 Subject: [PATCH 2/5] style: linting to pass fmt and clippy checks --- huff_codegen/src/lib.rs | 2 +- huff_core/benches/huff_benchmark.rs | 10 +++++----- huff_core/tests/erc20.rs | 2 +- huff_core/tests/erc721.rs | 2 +- huff_lexer/tests/tables.rs | 6 +++--- huff_parser/src/lib.rs | 6 +++--- huff_parser/tests/labels.rs | 14 ++++++-------- huff_utils/src/abi.rs | 4 ++-- huff_utils/src/ast.rs | 13 ++++++------- 9 files changed, 28 insertions(+), 31 deletions(-) diff --git a/huff_codegen/src/lib.rs b/huff_codegen/src/lib.rs index aca743ab..0ce89a8d 100644 --- a/huff_codegen/src/lib.rs +++ b/huff_codegen/src/lib.rs @@ -300,7 +300,7 @@ impl Codegen { let circular_codesize_invocations = circular_codesize_invocations.unwrap_or(&mut ccsi); // Loop through all intermediate bytecode representations generated from the AST - for (_ir_bytes_index, ir_byte) in ir_bytes.iter().enumerate() { + for ir_byte in ir_bytes.iter() { let starting_offset = offset; match &ir_byte.ty { IRByteType::Bytes(b) => { diff --git a/huff_core/benches/huff_benchmark.rs b/huff_core/benches/huff_benchmark.rs index e001cf71..394cc2f0 100644 --- a/huff_core/benches/huff_benchmark.rs +++ b/huff_core/benches/huff_benchmark.rs @@ -17,7 +17,7 @@ fn lex_erc20_from_source_benchmark(c: &mut Criterion) { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider) .unwrap(); @@ -48,7 +48,7 @@ fn parse_erc20_benchmark(c: &mut Criterion) { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider) .unwrap(); @@ -84,7 +84,7 @@ fn codegen_erc20_benchmark(c: &mut Criterion) { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider) .unwrap(); @@ -133,7 +133,7 @@ fn erc20_compilation_benchmark(c: &mut Criterion) { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps( Arc::clone(file_source), &files::Remapper::new("./"), @@ -180,7 +180,7 @@ fn erc721_compilation_benchmark(c: &mut Criterion) { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps( Arc::clone(file_source), &files::Remapper::new("./"), diff --git a/huff_core/tests/erc20.rs b/huff_core/tests/erc20.rs index 3c1144fe..84ed1c3b 100644 --- a/huff_core/tests/erc20.rs +++ b/huff_core/tests/erc20.rs @@ -18,7 +18,7 @@ fn test_erc20_compile() { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider) .unwrap(); diff --git a/huff_core/tests/erc721.rs b/huff_core/tests/erc721.rs index 3460cec3..29ee3961 100644 --- a/huff_core/tests/erc721.rs +++ b/huff_core/tests/erc721.rs @@ -18,7 +18,7 @@ fn test_erc721_compile() { .collect(); // Recurse file deps + generate flattened source - let file_source = file_sources.get(0).unwrap(); + let file_source = file_sources.first().unwrap(); let recursed_file_source = Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider) .unwrap(); diff --git a/huff_lexer/tests/tables.rs b/huff_lexer/tests/tables.rs index feabae28..1ff07d74 100644 --- a/huff_lexer/tests/tables.rs +++ b/huff_lexer/tests/tables.rs @@ -12,7 +12,7 @@ fn parses_jump_table() { .filter(|x| !matches!(x.kind, TokenKind::Whitespace)) .collect::>(); - assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define); + assert_eq!(tokens.first().unwrap().kind, TokenKind::Define); assert_eq!(tokens.get(1).unwrap().kind, TokenKind::JumpTable); assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("JUMP_TABLE"))); assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen); @@ -30,7 +30,7 @@ fn parses_packed_jump_table() { .filter(|x| !matches!(x.kind, TokenKind::Whitespace)) .collect::>(); - assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define); + assert_eq!(tokens.first().unwrap().kind, TokenKind::Define); assert_eq!(tokens.get(1).unwrap().kind, TokenKind::JumpTablePacked); assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("JUMP_TABLE_PACKED"))); assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen); @@ -48,7 +48,7 @@ fn parses_code_table() { .filter(|x| !matches!(x.kind, TokenKind::Whitespace)) .collect::>(); - assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define); + assert_eq!(tokens.first().unwrap().kind, TokenKind::Define); assert_eq!(tokens.get(1).unwrap().kind, TokenKind::CodeTable); assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("CODE_TABLE"))); assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen); diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index bc9dab54..78358bee 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -35,7 +35,7 @@ pub struct Parser { impl Parser { /// Public associated function that instantiates a Parser. pub fn new(tokens: Vec, base: Option) -> Self { - let initial_token = tokens.get(0).unwrap().clone(); + let initial_token = tokens.first().unwrap().clone(); let remapper = files::Remapper::new("./"); Self { tokens, cursor: 0, current_token: initial_token, base, spans: vec![], remapper } } @@ -44,7 +44,7 @@ impl Parser { /// /// PANICS if the tokens vec is empty! pub fn reset(&mut self) { - self.current_token = self.tokens.get(0).unwrap().clone(); + self.current_token = self.tokens.first().unwrap().clone(); self.cursor = 0; } @@ -191,7 +191,7 @@ impl Parser { fn check_label(&self, label: &str, label_set: &mut HashSet) -> Result<(), ParserError> { if label_set.contains(label) { tracing::error!(target: "parser", "DUPLICATED LABEL NAME: {}", label); - return Err(ParserError { + Err(ParserError { kind: ParserErrorKind::DuplicateLabel(label.to_string()), hint: Some(format!("Duplicated label name: \"{label}\"")), spans: AstSpan(self.spans.clone()), diff --git a/huff_parser/tests/labels.rs b/huff_parser/tests/labels.rs index 053bfed0..99cd7166 100644 --- a/huff_parser/tests/labels.rs +++ b/huff_parser/tests/labels.rs @@ -1,10 +1,6 @@ use huff_lexer::*; use huff_parser::*; -use huff_utils::{ - evm::Opcode, - error::ParserErrorKind, - prelude::* -}; +use huff_utils::{error::ParserErrorKind, evm::Opcode, prelude::*}; #[test] fn multiline_labels() { @@ -438,7 +434,6 @@ pub fn builtins_under_labels() { } } - #[test] fn duplicated_labels() { let source = r#" @@ -464,5 +459,8 @@ fn duplicated_labels() { // Grab the first macro let parse_result = parser.parse(); assert!(parse_result.is_err()); - assert_eq!(parse_result.unwrap_err().kind, ParserErrorKind::DuplicateLabel("dup_label".to_string())); -} \ No newline at end of file + assert_eq!( + parse_result.unwrap_err().kind, + ParserErrorKind::DuplicateLabel("dup_label".to_string()) + ); +} diff --git a/huff_utils/src/abi.rs b/huff_utils/src/abi.rs index 2f6cb67f..eef2384a 100644 --- a/huff_utils/src/abi.rs +++ b/huff_utils/src/abi.rs @@ -78,7 +78,7 @@ impl From for Abi { .filter(|m| m.name.to_lowercase() == "constructor") .cloned() .collect::>() - .get(0) + .first() .map(|func| Constructor { inputs: func .inputs @@ -97,7 +97,7 @@ impl From for Abi { .filter(|m| m.name == "CONSTRUCTOR") .cloned() .collect::>() - .get(0) + .first() .map(|func| Constructor { inputs: func .parameters diff --git a/huff_utils/src/ast.rs b/huff_utils/src/ast.rs index 72cf8fe6..b71c197d 100644 --- a/huff_utils/src/ast.rs +++ b/huff_utils/src/ast.rs @@ -33,8 +33,7 @@ impl AstSpan { pub fn error(&self, hint: Option<&String>) -> String { let file_to_source_map = self.0.iter().fold(BTreeMap::>::new(), |mut m, s| { - let file_name = - s.file.as_ref().map(|f2| f2.path.clone()).unwrap_or_else(|| "".to_string()); + let file_name = s.file.as_ref().map(|f2| f2.path.clone()).unwrap_or_default(); let mut new_vec: Vec<&Span> = m.get(&file_name).cloned().unwrap_or_default(); new_vec.push(s); m.insert(file_name, new_vec); @@ -167,7 +166,7 @@ impl Contract { .iter() .filter(|pointer| pointer.0.eq(&c.name)) .collect::>() - .get(0) + .first() { Some(p) => { *c = ConstantDefinition { @@ -248,7 +247,7 @@ impl Contract { .iter() .filter(|md| md.name.eq(&mi.macro_name)) .collect::>() - .get(0) + .first() { Some(&md) => { if md.name.eq("CONSTRUCTOR") { @@ -278,7 +277,7 @@ impl Contract { .iter() .filter(|md| md.name.eq(name)) .collect::>() - .get(0) + .first() { Some(&md) => { if md.name.eq("CONSTRUCTOR") { @@ -335,7 +334,7 @@ impl Contract { .iter() .filter(|pointer| pointer.0.eq(const_name)) .collect::>() - .get(0) + .first() .is_none() { tracing::debug!(target: "ast", "No storage pointer already set for \"{}\"!", const_name); @@ -347,7 +346,7 @@ impl Contract { .iter() .filter(|c| c.name.eq(const_name)) .collect::>() - .get(0) + .first() { Some(c) => { let new_value = match c.value { From 7c1c6da73b59580acea7123ec3e31612e1ba491b Mon Sep 17 00:00:00 2001 From: 0xrusowsky <0xrusowsky@proton.me> Date: Tue, 26 Dec 2023 22:54:27 +0100 Subject: [PATCH 3/5] fix: shift check from macro to contract scope --- huff_parser/src/lib.rs | 22 +++++++++++++++------- huff_parser/tests/labels.rs | 19 +++++++------------ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index 78358bee..2e9e5b95 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -60,6 +60,9 @@ impl Parser { // Initialize an empty Contract let mut contract = Contract::default(); + // Initialize and empty label set + let mut labels: HashSet = HashSet::new(); + // Iterate over tokens and construct the Contract aka AST while !self.check(TokenKind::Eof) { // Reset our spans @@ -71,7 +74,7 @@ impl Parser { } // Check for a decorator above a test macro else if self.check(TokenKind::Pound) { - let m = self.parse_macro()?; + let m = self.parse_macro(&mut labels)?; tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name); contract.macros.push(m); } @@ -103,7 +106,7 @@ impl Parser { contract.errors.push(e); } TokenKind::Macro | TokenKind::Fn | TokenKind::Test => { - let m = self.parse_macro()?; + let m = self.parse_macro(&mut labels)?; tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name); contract.macros.push(m); } @@ -504,7 +507,10 @@ impl Parser { /// Parses a macro. /// /// It should parse the following : macro MACRO_NAME(args...) = takes (x) returns (n) {...} - pub fn parse_macro(&mut self) -> Result { + pub fn parse_macro( + &mut self, + label_set: &mut HashSet, + ) -> Result { let mut decorator: Option = None; if self.check(TokenKind::Pound) { decorator = Some(self.parse_decorator()?); @@ -536,7 +542,7 @@ impl Parser { let macro_returns = self.match_kind(TokenKind::Returns).map_or(Ok(0), |_| self.parse_single_arg())?; - let macro_statements: Vec = self.parse_body()?; + let macro_statements: Vec = self.parse_body(label_set)?; Ok(MacroDefinition::new( macro_name, @@ -554,9 +560,11 @@ impl Parser { /// Parse the body of a macro. /// /// Only HEX, OPCODES, labels, builtins, and MACRO calls should be authorized. - pub fn parse_body(&mut self) -> Result, ParserError> { + pub fn parse_body( + &mut self, + label_set: &mut HashSet, + ) -> Result, ParserError> { let mut statements: Vec = Vec::new(); - let mut labels: HashSet = HashSet::new(); self.match_kind(TokenKind::OpenBrace)?; tracing::info!(target: "parser", "PARSING MACRO BODY"); while !self.check(TokenKind::CloseBrace) { @@ -652,7 +660,7 @@ impl Parser { TokenKind::Label(l) => { let mut curr_spans = vec![self.current_token.span.clone()]; self.consume(); - self.check_label(&l, &mut labels)?; + self.check_label(&l, label_set)?; let inner_statements: Vec = self.parse_label()?; inner_statements.iter().for_each(|a| curr_spans.extend_from_slice(&a.span.0)); tracing::info!(target: "parser", "PARSED LABEL \"{}\" INSIDE MACRO WITH {} STATEMENTS.", l, inner_statements.len()); diff --git a/huff_parser/tests/labels.rs b/huff_parser/tests/labels.rs index 99cd7166..aecfa38c 100644 --- a/huff_parser/tests/labels.rs +++ b/huff_parser/tests/labels.rs @@ -437,18 +437,13 @@ pub fn builtins_under_labels() { #[test] fn duplicated_labels() { let source = r#" - #define macro HELLO_WORLD() = takes(3) returns(0) { - 0x00 mstore - 0x01 0x02 add - dup_label: - HELLO() - 0x00 0x00 revert - cool_label: - HELLO() - 0x00 0x00 return - dup_label: - HELLO() - 0x00 0x00 return + #define macro MAIN() = takes(0) returns(0) { + cool_label jump + cool_label jump + + cool_label: 0x00 + dup_label: 0x00 + dup_label: 0x00 } "#; let flattened_source = FullFileSource { source, file: None, spans: vec![] }; From f308204f4bcd019357b0bc6230a7fa8e7e51c4c3 Mon Sep 17 00:00:00 2001 From: 0xrusowsky <0xrusowsky@proton.me> Date: Tue, 26 Dec 2023 23:26:53 +0100 Subject: [PATCH 4/5] style: linting --- huff_parser/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index 6a05db7f..b6b80749 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -192,7 +192,11 @@ impl Parser { /// Checks whether the input label is unique. /// If so, it will be added to the label set. Otherwise, an error will be returned. - fn check_duplicate_label(&self, label: &str, label_set: &mut HashSet) -> Result<(), ParserError> { + fn check_duplicate_label( + &self, + label: &str, + label_set: &mut HashSet, + ) -> Result<(), ParserError> { if label_set.contains(label) { tracing::error!(target: "parser", "DUPLICATED LABEL NAME: {}", label); Err(ParserError { From 50ad38f8c575738d75a5299bedd7815efd1c9bfd Mon Sep 17 00:00:00 2001 From: 0xrusowsky <0xrusowsky@proton.me> Date: Tue, 16 Jan 2024 21:02:30 +0100 Subject: [PATCH 5/5] chore: cache labels in the `Contract` struct --- huff_codegen/README.md | 2 ++ huff_codegen/tests/abigen.rs | 2 ++ huff_parser/README.md | 1 + huff_parser/src/lib.rs | 34 ++++++++++++---------------------- huff_utils/src/abi.rs | 1 + huff_utils/src/ast.rs | 2 ++ 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/huff_codegen/README.md b/huff_codegen/README.md index 7c5d2b8c..e51c8093 100644 --- a/huff_codegen/README.md +++ b/huff_codegen/README.md @@ -94,6 +94,7 @@ let contract = Contract { functions: vec![], events: vec![], tables: vec![], + labels: vec![], }; // Generate the main bytecode @@ -149,6 +150,7 @@ let contract = Contract { functions: vec![], events: vec![], tables: vec![], + labels: vec![], }; // Generate the constructor bytecode diff --git a/huff_codegen/tests/abigen.rs b/huff_codegen/tests/abigen.rs index 5118b5f3..c6a8f9be 100644 --- a/huff_codegen/tests/abigen.rs +++ b/huff_codegen/tests/abigen.rs @@ -28,6 +28,7 @@ fn constructs_valid_abi() { functions: vec![], events: vec![], tables: vec![], + labels: vec![], }; // Generate the abi from the contract @@ -68,6 +69,7 @@ fn missing_constructor_fails() { functions: vec![], events: vec![], tables: vec![], + labels: vec![], }; // Generate the abi from the contract diff --git a/huff_parser/README.md b/huff_parser/README.md index 9d8e488a..d0d8fc38 100644 --- a/huff_parser/README.md +++ b/huff_parser/README.md @@ -58,6 +58,7 @@ let expected_contract = Contract { functions: vec![], events: vec![], tables: vec![], + labels: vec![] }; assert_eq!(unwrapped_contract.macros, expected_contract.macros); ``` \ No newline at end of file diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs index b6b80749..379dd737 100644 --- a/huff_parser/src/lib.rs +++ b/huff_parser/src/lib.rs @@ -13,7 +13,6 @@ use huff_utils::{ types::*, }; use regex::Regex; -use std::collections::HashSet; /// The Parser #[derive(Debug, Clone)] @@ -60,9 +59,6 @@ impl Parser { // Initialize an empty Contract let mut contract = Contract::default(); - // Initialize and empty label set - let mut labels: HashSet = HashSet::new(); - // Iterate over tokens and construct the Contract aka AST while !self.check(TokenKind::Eof) { // Reset our spans @@ -74,7 +70,7 @@ impl Parser { } // Check for a decorator above a test macro else if self.check(TokenKind::Pound) { - let m = self.parse_macro(&mut labels)?; + let m = self.parse_macro(&mut contract)?; tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name); contract.macros.push(m); } @@ -106,7 +102,7 @@ impl Parser { contract.errors.push(e); } TokenKind::Macro | TokenKind::Fn | TokenKind::Test => { - let m = self.parse_macro(&mut labels)?; + let m = self.parse_macro(&mut contract)?; tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name); self.check_duplicate_macro(&contract, &m)?; contract.macros.push(m); @@ -191,21 +187,21 @@ impl Parser { } /// Checks whether the input label is unique. - /// If so, it will be added to the label set. Otherwise, an error will be returned. + /// If so, it will be added to the contract. Otherwise, an error will be returned. fn check_duplicate_label( &self, - label: &str, - label_set: &mut HashSet, + contract: &mut Contract, + label: String, ) -> Result<(), ParserError> { - if label_set.contains(label) { + if contract.labels.binary_search_by(|_label| _label.cmp(&label)).is_ok() { tracing::error!(target: "parser", "DUPLICATED LABEL NAME: {}", label); Err(ParserError { - kind: ParserErrorKind::DuplicateLabel(label.to_string()), + kind: ParserErrorKind::DuplicateLabel(label.clone()), hint: Some(format!("Duplicated label name: \"{label}\"")), spans: AstSpan(self.spans.clone()), }) } else { - label_set.insert(label.to_string()); + contract.labels.push(label); Ok(()) } } @@ -530,10 +526,7 @@ impl Parser { /// Parses a macro. /// /// It should parse the following : macro MACRO_NAME(args...) = takes (x) returns (n) {...} - pub fn parse_macro( - &mut self, - label_set: &mut HashSet, - ) -> Result { + pub fn parse_macro(&mut self, contract: &mut Contract) -> Result { let mut decorator: Option = None; if self.check(TokenKind::Pound) { decorator = Some(self.parse_decorator()?); @@ -565,7 +558,7 @@ impl Parser { let macro_returns = self.match_kind(TokenKind::Returns).map_or(Ok(0), |_| self.parse_single_arg())?; - let macro_statements: Vec = self.parse_body(label_set)?; + let macro_statements: Vec = self.parse_body(contract)?; Ok(MacroDefinition::new( macro_name, @@ -583,10 +576,7 @@ impl Parser { /// Parse the body of a macro. /// /// Only HEX, OPCODES, labels, builtins, and MACRO calls should be authorized. - pub fn parse_body( - &mut self, - label_set: &mut HashSet, - ) -> Result, ParserError> { + pub fn parse_body(&mut self, contract: &mut Contract) -> Result, ParserError> { let mut statements: Vec = Vec::new(); self.match_kind(TokenKind::OpenBrace)?; tracing::info!(target: "parser", "PARSING MACRO BODY"); @@ -683,7 +673,7 @@ impl Parser { TokenKind::Label(l) => { let mut curr_spans = vec![self.current_token.span.clone()]; self.consume(); - self.check_duplicate_label(&l, label_set)?; + self.check_duplicate_label(contract, l.to_string())?; let inner_statements: Vec = self.parse_label()?; inner_statements .iter() diff --git a/huff_utils/src/abi.rs b/huff_utils/src/abi.rs index eef2384a..94c3e070 100644 --- a/huff_utils/src/abi.rs +++ b/huff_utils/src/abi.rs @@ -30,6 +30,7 @@ //! }], //! events: vec![], //! tables: vec![], +//! labels: vec![], //! }; //! //! // Create an ABI using that generate contract diff --git a/huff_utils/src/ast.rs b/huff_utils/src/ast.rs index 16d0157e..447d3160 100644 --- a/huff_utils/src/ast.rs +++ b/huff_utils/src/ast.rs @@ -116,6 +116,8 @@ pub struct Contract { pub events: Vec, /// Tables pub tables: Vec, + /// Labels + pub labels: Vec, } impl Contract {