huff_utils/
ast.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{bytecode::*, bytes_util::*, error::CodegenError, evm::Opcode};
4use std::path::PathBuf;
5
6/// A contained literal
7pub type Literal = [u8; 32];
8
9/// A File Path
10///
11/// Used for parsing the huff imports.
12pub type FilePath = PathBuf;
13
14/// A Huff Contract Representation
15///
16/// This is the representation of a contract as it is parsed from huff source code.
17/// Thus, it is also the root of the AST.
18///
19/// For examples of Huff contracts, see the [huff-examples repository](https://github.com/huff-language/huff-examples).
20#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
21pub struct Contract {
22    /// Macro definitions
23    pub macros: Vec<MacroDefinition>,
24    /// Invocations of macros
25    pub invocations: Vec<MacroInvocation>,
26    /// File Imports
27    pub imports: Vec<FilePath>,
28    /// Constants
29    pub constants: Vec<ConstantDefinition>,
30    /// Functions
31    pub functions: Vec<Function>,
32    /// Events
33    pub events: Vec<Event>,
34    /// Tables
35    pub tables: Vec<Table>,
36}
37
38impl Contract {
39    /// Returns the first macro that matches the provided name
40    pub fn find_macro_by_name(&self, name: &str) -> Option<MacroDefinition> {
41        if let Some(m) = self
42            .macros
43            .iter()
44            .filter(|m| m.name == name)
45            .cloned()
46            .collect::<Vec<MacroDefinition>>()
47            .get(0)
48        {
49            Some(m.clone())
50        } else {
51            tracing::warn!("Failed to find macro \"{}\" in contract", name);
52            None
53        }
54    }
55
56    /// Derives the FreeStoragePointers into their bytes32 representation
57    pub fn derive_storage_pointers(&mut self) {
58        let mut storage_pointers: Vec<[u8; 32]> = Vec::new();
59        let mut last_assigned_free_pointer = 0;
60        // do the non fsp consts first, so we can check for conflicts
61        // when going over the fsp consts
62        for constant in &self.constants {
63            if let ConstVal::Literal(literal) = &constant.value {
64                storage_pointers.push(*literal);
65            }
66        }
67        for constant in self.constants.iter_mut() {
68            if let ConstVal::FreeStoragePointer(_) = &constant.value {
69                let mut fsp_bytes = str_to_bytes32(&format!("{}", last_assigned_free_pointer));
70                while storage_pointers.contains(&fsp_bytes) {
71                    last_assigned_free_pointer += 1;
72                    fsp_bytes = str_to_bytes32(&format!("{}", last_assigned_free_pointer));
73                }
74                storage_pointers.push(fsp_bytes);
75                last_assigned_free_pointer += 1;
76                constant.value = ConstVal::Literal(fsp_bytes);
77            }
78        }
79    }
80}
81
82/// A function, event, or macro argument
83#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
84pub struct Argument {
85    /// Type of the argument
86    pub arg_type: Option<String>,
87    /// The name of the argument
88    pub name: Option<String>,
89    /// Is the argument indexed? TODO: should be valid for event arguments ONLY
90    pub indexed: bool,
91}
92
93/// A Function Signature
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
95pub struct Function {
96    /// The name of the function
97    pub name: String,
98    /// The function signature
99    pub signature: [u8; 4],
100    /// The parameters of the function
101    pub inputs: Vec<Argument>,
102    /// The function type
103    pub fn_type: FunctionType,
104    /// The return values of the function
105    pub outputs: Vec<Argument>,
106}
107
108/// Function Types
109#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
110pub enum FunctionType {
111    /// Viewable Function
112    View,
113    /// Payable Function
114    Payable,
115    /// Non Payable Function
116    NonPayable,
117    /// Pure Function
118    Pure,
119}
120
121/// An Event Signature
122#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
123pub struct Event {
124    /// The name of the event
125    pub name: String,
126    /// The parameters of the event
127    pub parameters: Vec<Argument>,
128}
129
130/// A Table Definition
131#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
132pub struct Table {
133    /// The name of the table
134    pub name: String,
135    // TODO:::
136}
137
138/// A Macro Definition
139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
140pub struct MacroDefinition {
141    /// The Macro Name
142    pub name: String,
143    /// A list of Macro parameters
144    pub parameters: Vec<Argument>,
145    /// A list of Statements contained in the Macro
146    pub statements: Vec<Statement>,
147    /// The take size
148    pub takes: usize,
149    /// The return size
150    pub returns: usize,
151}
152
153impl ToIRBytecode<CodegenError> for MacroDefinition {
154    fn to_irbytecode(&self) -> Result<IRBytecode, CodegenError> {
155        let mut inner_irbytes: Vec<IRByte> = vec![];
156
157        // Iterate and translate each statement to bytecode
158        self.statements.iter().for_each(|statement| {
159            match statement {
160                Statement::Literal(l) => {
161                    let combined = l
162                        .iter()
163                        .map(|b| IRByte::Byte(Byte(format!("{:04x}", b))))
164                        .collect::<Vec<IRByte>>();
165                    println!("Combined IRBytes: {:?}", combined);
166                    combined.iter().for_each(|irb| inner_irbytes.push(irb.clone()));
167                }
168                Statement::Opcode(o) => {
169                    let opcode_str = o.string();
170                    tracing::info!("Got opcode hex string: {}", opcode_str);
171                    inner_irbytes.push(IRByte::Byte(Byte(opcode_str)))
172                }
173                Statement::MacroInvocation(mi) => {
174                    inner_irbytes.push(IRByte::Statement(Statement::MacroInvocation(mi.clone())));
175                }
176                Statement::Constant(name) => {
177                    // Constant needs to be evaluated at the top-level
178                    inner_irbytes.push(IRByte::Constant(name.to_string()));
179                }
180                Statement::ArgCall(arg_name) => {
181                    // Arg call needs to use a destination defined in the calling macro context
182                    inner_irbytes.push(IRByte::ArgCall(arg_name.to_string()));
183                }
184            }
185        });
186        Ok(IRBytecode(inner_irbytes))
187    }
188}
189
190impl MacroDefinition {
191    /// Public associated function that instantiates a MacroDefinition.
192    pub fn new(
193        name: String,
194        parameters: Vec<Argument>,
195        statements: Vec<Statement>,
196        takes: usize,
197        returns: usize,
198    ) -> Self {
199        MacroDefinition { name, parameters, statements, takes, returns }
200    }
201}
202
203/// A Macro Invocation
204#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
205pub struct MacroInvocation {
206    /// The Macro Name
207    pub macro_name: String,
208    /// A list of Macro arguments
209    pub args: Vec<MacroArg>,
210}
211
212/// An argument passed when invoking a maco
213#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
214pub enum MacroArg {
215    /// Macro Literal Argument
216    Literal(Literal),
217    /// Macro Iden String Argument
218    Ident(String),
219}
220
221/// Free Storage Pointer Unit Struct
222#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
223pub struct FreeStoragePointer;
224
225/// A Constant Value
226#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
227pub enum ConstVal {
228    /// A literal value for the constant
229    Literal(Literal),
230    /// A Free Storage Pointer
231    FreeStoragePointer(FreeStoragePointer),
232}
233
234/// A Constant Definition
235#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
236pub struct ConstantDefinition {
237    /// The Constant name
238    pub name: String,
239    /// The Constant value
240    pub value: ConstVal,
241}
242
243/// A Statement
244#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
245pub enum Statement {
246    /// A Literal Statement
247    Literal(Literal),
248    /// An Opcode Statement
249    Opcode(Opcode),
250    /// A Macro Invocation Statement
251    MacroInvocation(MacroInvocation),
252    /// A Constant Push
253    Constant(String),
254    /// An Arg Call
255    ArgCall(String),
256}