mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-05-06 04:37:56 +00:00
Emerge Yul recompiler (#1)
Provide a modified (and incomplete) version of ZKSync zksolc that can compile the most basic contracts
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
//!
|
||||
//! The Yul IR parser error.
|
||||
//!
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
|
||||
///
|
||||
/// The Yul IR parser error.
|
||||
///
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// An invalid token received from the lexer.
|
||||
#[error("{location} Expected one of {expected:?}, found `{found}`")]
|
||||
InvalidToken {
|
||||
/// The invalid token location.
|
||||
location: Location,
|
||||
/// The list of expected tokens.
|
||||
expected: Vec<&'static str>,
|
||||
/// The invalid token.
|
||||
found: String,
|
||||
},
|
||||
/// A reserved keyword cannot be used as an identifier.
|
||||
#[error("{location} The identifier `{identifier}` is reserved")]
|
||||
ReservedIdentifier {
|
||||
/// The invalid token location.
|
||||
location: Location,
|
||||
/// The invalid identifier.
|
||||
identifier: String,
|
||||
},
|
||||
/// Invalid number of function arguments.
|
||||
#[error("{location} Function `{identifier}` must have {expected} arguments, found {found}")]
|
||||
InvalidNumberOfArguments {
|
||||
/// The invalid function location.
|
||||
location: Location,
|
||||
/// The invalid function name.
|
||||
identifier: String,
|
||||
/// The expected number of arguments.
|
||||
expected: usize,
|
||||
/// The actual number of arguments.
|
||||
found: usize,
|
||||
},
|
||||
/// Invalid object name.
|
||||
#[error(
|
||||
"{location} Objects must be named as '<name>' (deploy) and '<name>_deployed' (runtime)"
|
||||
)]
|
||||
InvalidObjectName {
|
||||
/// The invalid token location.
|
||||
location: Location,
|
||||
/// The expected identifier.
|
||||
expected: String,
|
||||
/// The invalid identifier.
|
||||
found: String,
|
||||
},
|
||||
/// Invalid attributes.
|
||||
#[error("{location} Found invalid LLVM attributes: {values:?}")]
|
||||
InvalidAttributes {
|
||||
/// The invalid token location.
|
||||
location: Location,
|
||||
/// The list of invalid attributes.
|
||||
values: BTreeSet<String>,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
//!
|
||||
//! The YUL source code identifier.
|
||||
//!
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::r#type::Type;
|
||||
|
||||
///
|
||||
/// The YUL source code identifier.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Identifier {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The inner string.
|
||||
pub inner: String,
|
||||
/// The type, if it has been explicitly specified.
|
||||
pub r#type: Option<Type>,
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
///
|
||||
/// A shortcut constructor.
|
||||
///
|
||||
pub fn new(location: Location, inner: String) -> Self {
|
||||
Self {
|
||||
location,
|
||||
inner,
|
||||
r#type: None,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// A shortcut constructor for a typed identifier.
|
||||
///
|
||||
pub fn new_with_type(location: Location, inner: String, r#type: Option<Type>) -> Self {
|
||||
Self {
|
||||
location,
|
||||
inner,
|
||||
r#type,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Parses the identifier list where the types cannot be specified.
|
||||
///
|
||||
pub fn parse_list(
|
||||
lexer: &mut Lexer,
|
||||
mut initial: Option<Token>,
|
||||
) -> Result<(Vec<Self>, Option<Token>), Error> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
let mut expected_comma = false;
|
||||
loop {
|
||||
let token = crate::yul::parser::take_or_next(initial.take(), lexer)?;
|
||||
|
||||
match token {
|
||||
Token {
|
||||
location,
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} if !expected_comma => {
|
||||
result.push(Self::new(location, identifier.inner));
|
||||
expected_comma = true;
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Comma),
|
||||
..
|
||||
} if expected_comma => {
|
||||
expected_comma = false;
|
||||
}
|
||||
token => return Ok((result, Some(token))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Parses the identifier list where the types may be optionally specified.
|
||||
///
|
||||
pub fn parse_typed_list(
|
||||
lexer: &mut Lexer,
|
||||
mut initial: Option<Token>,
|
||||
) -> Result<(Vec<Self>, Option<Token>), Error> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
let mut expected_comma = false;
|
||||
loop {
|
||||
let token = crate::yul::parser::take_or_next(initial.take(), lexer)?;
|
||||
|
||||
match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
location,
|
||||
..
|
||||
} if !expected_comma => {
|
||||
let r#type = match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Colon),
|
||||
..
|
||||
} => {
|
||||
lexer.next()?;
|
||||
Some(Type::parse(lexer, None)?)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
result.push(Self::new_with_type(location, identifier.inner, r#type));
|
||||
expected_comma = true;
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Comma),
|
||||
..
|
||||
} if expected_comma => {
|
||||
expected_comma = false;
|
||||
}
|
||||
token => return Ok((result, Some(token))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//!
|
||||
//! The YUL code block.
|
||||
//!
|
||||
|
||||
pub mod error;
|
||||
pub mod identifier;
|
||||
pub mod statement;
|
||||
pub mod r#type;
|
||||
|
||||
use crate::yul::lexer::error::Error as LexerError;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
|
||||
///
|
||||
/// Returns the `token` value if it is `Some(_)`, otherwise takes the next token from the `stream`.
|
||||
///
|
||||
pub fn take_or_next(mut token: Option<Token>, lexer: &mut Lexer) -> Result<Token, LexerError> {
|
||||
match token.take() {
|
||||
Some(token) => Ok(token),
|
||||
None => lexer.next(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//!
|
||||
//! The assignment expression statement.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::identifier::Identifier;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
|
||||
///
|
||||
/// The Yul assignment expression statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Assignment {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The variable bindings.
|
||||
pub bindings: Vec<Identifier>,
|
||||
/// The initializing expression.
|
||||
pub initializer: Expression,
|
||||
}
|
||||
|
||||
impl Assignment {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let (location, identifier) = match token {
|
||||
Token {
|
||||
location,
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} => (location, identifier),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{identifier}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let length = identifier.inner.len();
|
||||
|
||||
match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Assignment),
|
||||
..
|
||||
} => {
|
||||
lexer.next()?;
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
bindings: vec![Identifier::new(location, identifier.inner)],
|
||||
initializer: Expression::parse(lexer, None)?,
|
||||
})
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Comma),
|
||||
..
|
||||
} => {
|
||||
let (identifiers, next) = Identifier::parse_list(
|
||||
lexer,
|
||||
Some(Token::new(location, Lexeme::Identifier(identifier), length)),
|
||||
)?;
|
||||
|
||||
match crate::yul::parser::take_or_next(next, lexer)? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Assignment),
|
||||
..
|
||||
} => {}
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec![":="],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
bindings: identifiers,
|
||||
initializer: Expression::parse(lexer, None)?,
|
||||
})
|
||||
}
|
||||
token => Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec![":=", ","],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
self.initializer.get_missing_libraries()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Assignment
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
mut self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let value = match self.initializer.into_llvm(context)? {
|
||||
Some(value) => value,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
if self.bindings.len() == 1 {
|
||||
let identifier = self.bindings.remove(0);
|
||||
let pointer = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_stack_pointer(identifier.inner.as_str())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} Assignment to an undeclared variable `{}`",
|
||||
identifier.location,
|
||||
identifier.inner,
|
||||
)
|
||||
})?;
|
||||
context.build_store(pointer, value.to_llvm())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let llvm_type = value.to_llvm().into_struct_value().get_type();
|
||||
let tuple_pointer = context.build_alloca(llvm_type, "assignment_pointer");
|
||||
context.build_store(tuple_pointer, value.to_llvm())?;
|
||||
|
||||
for (index, binding) in self.bindings.into_iter().enumerate() {
|
||||
let field_pointer = context.build_gep(
|
||||
tuple_pointer,
|
||||
&[
|
||||
context.field_const(0),
|
||||
context
|
||||
.integer_type(era_compiler_common::BIT_LENGTH_X32)
|
||||
.const_int(index as u64, false),
|
||||
],
|
||||
context.field_type().as_basic_type_enum(),
|
||||
format!("assignment_binding_{index}_gep_pointer").as_str(),
|
||||
);
|
||||
|
||||
let binding_pointer = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_stack_pointer(binding.inner.as_str())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} Assignment to an undeclared variable `{}`",
|
||||
binding.location,
|
||||
binding.inner,
|
||||
)
|
||||
})?;
|
||||
let value = context.build_load(
|
||||
field_pointer,
|
||||
format!("assignment_binding_{index}_value").as_str(),
|
||||
)?;
|
||||
context.build_store(binding_pointer, value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
//!
|
||||
//! The source code block.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::statement::assignment::Assignment;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
use crate::yul::parser::statement::Statement;
|
||||
|
||||
///
|
||||
/// The Yul source code block.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Block {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The block statements.
|
||||
pub statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let mut statements = Vec::new();
|
||||
|
||||
let location = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyLeft),
|
||||
location,
|
||||
..
|
||||
} => location,
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let mut remaining = None;
|
||||
|
||||
loop {
|
||||
match crate::yul::parser::take_or_next(remaining.take(), lexer)? {
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Keyword(_),
|
||||
..
|
||||
} => {
|
||||
let (statement, next) = Statement::parse(lexer, Some(token))?;
|
||||
remaining = next;
|
||||
statements.push(statement);
|
||||
}
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Literal(_),
|
||||
..
|
||||
} => {
|
||||
statements
|
||||
.push(Expression::parse(lexer, Some(token)).map(Statement::Expression)?);
|
||||
}
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Identifier(_),
|
||||
..
|
||||
} => match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Assignment),
|
||||
..
|
||||
} => {
|
||||
statements.push(
|
||||
Assignment::parse(lexer, Some(token)).map(Statement::Assignment)?,
|
||||
);
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Comma),
|
||||
..
|
||||
} => {
|
||||
statements.push(
|
||||
Assignment::parse(lexer, Some(token)).map(Statement::Assignment)?,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
statements.push(
|
||||
Expression::parse(lexer, Some(token)).map(Statement::Expression)?,
|
||||
);
|
||||
}
|
||||
},
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyLeft),
|
||||
..
|
||||
} => statements.push(Block::parse(lexer, Some(token)).map(Statement::Block)?),
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyRight),
|
||||
..
|
||||
} => break,
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{keyword}", "{expression}", "{identifier}", "{", "}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
statements,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
let mut libraries = HashSet::new();
|
||||
for statement in self.statements.iter() {
|
||||
libraries.extend(statement.get_missing_libraries());
|
||||
}
|
||||
libraries
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Block
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let current_function = context.current_function().borrow().name().to_owned();
|
||||
let current_block = context.basic_block();
|
||||
|
||||
let mut functions = Vec::with_capacity(self.statements.len());
|
||||
let mut local_statements = Vec::with_capacity(self.statements.len());
|
||||
|
||||
for statement in self.statements.into_iter() {
|
||||
match statement {
|
||||
Statement::FunctionDefinition(mut statement) => {
|
||||
statement.declare(context)?;
|
||||
functions.push(statement);
|
||||
}
|
||||
statement => local_statements.push(statement),
|
||||
}
|
||||
}
|
||||
|
||||
for function in functions.into_iter() {
|
||||
function.into_llvm(context)?;
|
||||
}
|
||||
|
||||
context.set_current_function(current_function.as_str())?;
|
||||
context.set_basic_block(current_block);
|
||||
for statement in local_statements.into_iter() {
|
||||
if context.basic_block().get_terminator().is_some() {
|
||||
break;
|
||||
}
|
||||
|
||||
match statement {
|
||||
Statement::Block(block) => {
|
||||
block.into_llvm(context)?;
|
||||
}
|
||||
Statement::Expression(expression) => {
|
||||
expression.into_llvm(context)?;
|
||||
}
|
||||
Statement::VariableDeclaration(statement) => statement.into_llvm(context)?,
|
||||
Statement::Assignment(statement) => statement.into_llvm(context)?,
|
||||
Statement::IfConditional(statement) => statement.into_llvm(context)?,
|
||||
Statement::Switch(statement) => statement.into_llvm(context)?,
|
||||
Statement::ForLoop(statement) => statement.into_llvm(context)?,
|
||||
Statement::Continue(_location) => {
|
||||
context.build_unconditional_branch(context.r#loop().continue_block);
|
||||
break;
|
||||
}
|
||||
Statement::Break(_location) => {
|
||||
context.build_unconditional_branch(context.r#loop().join_block);
|
||||
break;
|
||||
}
|
||||
Statement::Leave(_location) => {
|
||||
context.build_unconditional_branch(
|
||||
context.current_function().borrow().return_block(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
statement => anyhow::bail!(
|
||||
"{} Unexpected local statement: {:?}",
|
||||
statement.location(),
|
||||
statement
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_bracket_curly_left() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
(
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(11, 17),
|
||||
expected: vec!["{keyword}", "{expression}", "{identifier}", "{", "}"],
|
||||
found: "(".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_statement() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
:=
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(11, 17),
|
||||
expected: vec!["{keyword}", "{expression}", "{identifier}", "{", "}"],
|
||||
found: ":=".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
//!
|
||||
//! The YUL code.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::keyword::Keyword;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
|
||||
///
|
||||
/// The YUL code entity, which is the first block of the object.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Code {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The main block.
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
impl Code {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let location = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Code),
|
||||
location,
|
||||
..
|
||||
} => location,
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["code"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let block = Block::parse(lexer, None)?;
|
||||
|
||||
Ok(Self { location, block })
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
self.block.get_missing_libraries()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Code
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
self.block.into_llvm(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_code() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
data {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(3, 5),
|
||||
expected: vec!["code"],
|
||||
found: "data".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,491 @@
|
||||
//!
|
||||
//! The function name.
|
||||
//!
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
///
|
||||
/// The function name.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Name {
|
||||
/// The user-defined function.
|
||||
UserDefined(String),
|
||||
|
||||
/// `x + y`
|
||||
Add,
|
||||
/// `x - y`
|
||||
Sub,
|
||||
/// `x * y`
|
||||
Mul,
|
||||
/// `x / y` or `0` if `y == 0`
|
||||
Div,
|
||||
/// `x % y` or `0` if `y == 0`
|
||||
Mod,
|
||||
/// `x / y`, for signed numbers in two’s complement, `0` if `y == 0`
|
||||
Sdiv,
|
||||
/// `x % y`, for signed numbers in two’s complement, `0` if `y == 0`
|
||||
Smod,
|
||||
|
||||
/// `1` if `x < y`, `0` otherwise
|
||||
Lt,
|
||||
/// `1` if `x > y`, `0` otherwise
|
||||
Gt,
|
||||
/// `1` if `x == y`, `0` otherwise
|
||||
Eq,
|
||||
/// `1` if `x == 0`, `0` otherwise
|
||||
IsZero,
|
||||
/// `1` if `x < y`, `0` otherwise, for signed numbers in two’s complement
|
||||
Slt,
|
||||
/// `1` if `x > y`, `0` otherwise, for signed numbers in two’s complement
|
||||
Sgt,
|
||||
|
||||
/// bitwise "or" of `x` and `y`
|
||||
Or,
|
||||
/// bitwise "xor" of `x` and `y`
|
||||
Xor,
|
||||
/// bitwise "not" of `x` (every bit of `x` is negated)
|
||||
Not,
|
||||
/// bitwise "and" of `x` and `y`
|
||||
And,
|
||||
/// logical shift left `y` by `x` bits
|
||||
Shl,
|
||||
/// logical shift right `y` by `x` bits
|
||||
Shr,
|
||||
/// signed arithmetic shift right `y` by `x` bits
|
||||
Sar,
|
||||
/// `n`th byte of `x`, where the most significant byte is the `0`th byte
|
||||
Byte,
|
||||
/// discard value x
|
||||
Pop,
|
||||
|
||||
/// `(x + y) % m` with arbitrary precision arithmetic, `0` if `m == 0`
|
||||
AddMod,
|
||||
/// `(x * y) % m` with arbitrary precision arithmetic, `0` if `m == 0`
|
||||
MulMod,
|
||||
/// `x` to the power of `y`
|
||||
Exp,
|
||||
/// sign extend from `(i*8+7)`th bit counting from least significant
|
||||
SignExtend,
|
||||
|
||||
/// `keccak(mem[p…(p+n)))`
|
||||
Keccak256,
|
||||
|
||||
/// `mem[p…(p+32))`
|
||||
MLoad,
|
||||
/// `mem[p…(p+32)) := v`
|
||||
MStore,
|
||||
/// `mem[p] := v & 0xff` (only modifies a single byte)
|
||||
MStore8,
|
||||
/// heap memory copy
|
||||
MCopy,
|
||||
|
||||
/// `storage[p]`
|
||||
SLoad,
|
||||
/// `storage[p] := v`
|
||||
SStore,
|
||||
/// transient `storage[p]`
|
||||
TLoad,
|
||||
/// transient `storage[p] := v`
|
||||
TStore,
|
||||
/// `loadimmutable` storage read
|
||||
LoadImmutable,
|
||||
/// `setimmutable` storage write
|
||||
SetImmutable,
|
||||
|
||||
/// call data starting from position `p` (32 bytes)
|
||||
CallDataLoad,
|
||||
/// size of call data in bytes
|
||||
CallDataSize,
|
||||
/// copy `s` bytes from calldata at position `f` to memory at position `t`
|
||||
CallDataCopy,
|
||||
/// size of the code of the current contract / execution context
|
||||
CodeSize,
|
||||
/// copy `s` bytes from code at position `f` to mem at position `t`
|
||||
CodeCopy,
|
||||
/// size of the code at address `a`
|
||||
ExtCodeSize,
|
||||
/// code hash of address `a`
|
||||
ExtCodeHash,
|
||||
/// size of the last returndata
|
||||
ReturnDataSize,
|
||||
/// copy `s` bytes from returndata at position `f` to mem at position `t`
|
||||
ReturnDataCopy,
|
||||
|
||||
/// end execution, return data `mem[p…(p+s))`
|
||||
Return,
|
||||
/// end execution, revert state changes, return data `mem[p…(p+s))`
|
||||
Revert,
|
||||
/// stop execution, identical to `return(0, 0)`
|
||||
Stop,
|
||||
/// end execution with invalid instruction
|
||||
Invalid,
|
||||
|
||||
/// log without topics and data `mem[p…(p+s))`
|
||||
Log0,
|
||||
/// log with topic t1 and data `mem[p…(p+s))`
|
||||
Log1,
|
||||
/// log with topics t1, t2 and data `mem[p…(p+s))`
|
||||
Log2,
|
||||
/// log with topics t1, t2, t3 and data `mem[p…(p+s))`
|
||||
Log3,
|
||||
/// log with topics t1, t2, t3, t4 and data `mem[p…(p+s))`
|
||||
Log4,
|
||||
|
||||
/// call contract at address a with input `mem[in…(in+insize))` providing `g` gas and `v` wei
|
||||
/// and output area `mem[out…(out+outsize))` returning 0 on error (e.g. out of gas)
|
||||
/// and 1 on success
|
||||
/// [See more](https://docs.soliditylang.org/en/v0.8.2/yul.html#yul-call-return-area)
|
||||
Call,
|
||||
/// identical to call but only use the code from a and stay in the context of the current
|
||||
/// contract otherwise
|
||||
CallCode,
|
||||
/// identical to `callcode` but also keeps `caller` and `callvalue`
|
||||
DelegateCall,
|
||||
/// identical to `call(g, a, 0, in, insize, out, outsize)` but do not allows state modifications
|
||||
StaticCall,
|
||||
|
||||
/// create new contract with code `mem[p…(p+n))` and send `v` wei and return the new address
|
||||
///
|
||||
/// Passes bytecode to the system contracts.
|
||||
Create,
|
||||
/// create new contract with code `mem[p…(p+n))` at address
|
||||
/// `keccak256(0xff . this . s . keccak256(mem[p…(p+n)))` and send `v` wei and return the
|
||||
/// new address, where `0xff` is a 1-byte value, this is the current contract’s address as a
|
||||
/// 20-byte value and `s` is a big-endian 256-bit value
|
||||
///
|
||||
/// Passes bytecode to the system contracts.
|
||||
Create2,
|
||||
/// create new contract with code `mem[p…(p+n))` and send `v` wei and return the new address
|
||||
///
|
||||
/// Passes hash to the system contracts.
|
||||
ZkCreate,
|
||||
/// create new contract with code `mem[p…(p+n))` at address
|
||||
/// `keccak256(0xff . this . s . keccak256(mem[p…(p+n)))` and send `v` wei and return the
|
||||
/// new address, where `0xff` is a 1-byte value, this is the current contract’s address as a
|
||||
/// 20-byte value and `s` is a big-endian 256-bit value
|
||||
///
|
||||
/// Passes hash to the system contracts.
|
||||
ZkCreate2,
|
||||
/// returns the size in the data area
|
||||
DataSize,
|
||||
/// is equivalent to `CodeCopy`
|
||||
DataCopy,
|
||||
/// returns the offset in the data area
|
||||
DataOffset,
|
||||
|
||||
/// `linkersymbol` is a stub call
|
||||
LinkerSymbol,
|
||||
/// `memoryguard` is a stub call
|
||||
MemoryGuard,
|
||||
|
||||
/// address of the current contract / execution context
|
||||
Address,
|
||||
/// call sender (excluding `delegatecall`)
|
||||
Caller,
|
||||
|
||||
/// wei sent together with the current call
|
||||
CallValue,
|
||||
/// gas still available to execution
|
||||
Gas,
|
||||
/// wei balance at address `a`
|
||||
Balance,
|
||||
/// equivalent to `balance(address())`, but cheaper
|
||||
SelfBalance,
|
||||
|
||||
/// block gas limit of the current block
|
||||
GasLimit,
|
||||
/// gas price of the transaction
|
||||
GasPrice,
|
||||
/// transaction sender
|
||||
Origin,
|
||||
/// ID of the executing chain (EIP 1344)
|
||||
ChainId,
|
||||
/// current block number
|
||||
Number,
|
||||
/// timestamp of the current block in seconds since the epoch
|
||||
Timestamp,
|
||||
/// hash of block nr b - only for last 256 blocks excluding current
|
||||
BlockHash,
|
||||
/// versioned hash of transaction’s i-th blob
|
||||
BlobHash,
|
||||
/// difficulty of the current block
|
||||
Difficulty,
|
||||
/// https://eips.ethereum.org/EIPS/eip-4399
|
||||
Prevrandao,
|
||||
/// current mining beneficiary
|
||||
CoinBase,
|
||||
/// size of memory, i.e. largest accessed memory index
|
||||
MSize,
|
||||
|
||||
/// verbatim instruction with 0 inputs and 0 outputs
|
||||
/// only works in the Yul mode, so it is mostly used as a tool for extending Yul for zkSync
|
||||
Verbatim {
|
||||
/// the number of input arguments
|
||||
input_size: usize,
|
||||
/// the number of output arguments
|
||||
output_size: usize,
|
||||
},
|
||||
|
||||
/// current block’s base fee (EIP-3198 and EIP-1559)
|
||||
BaseFee,
|
||||
/// current block’s blob base fee (EIP-7516 and EIP-4844)
|
||||
BlobBaseFee,
|
||||
/// current position in code
|
||||
Pc,
|
||||
/// like `codecopy(t, f, s)` but take code at address `a`
|
||||
ExtCodeCopy,
|
||||
/// end execution, destroy current contract and send funds to `a`
|
||||
SelfDestruct,
|
||||
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkToL1,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkCodeSource,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkPrecompile,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkMeta,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSetContextU128,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSetPubdataPrice,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkIncrementTxCounter,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkEventInitialize,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkEventWrite,
|
||||
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkMimicCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSystemMimicCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkMimicCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSystemMimicCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkRawCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkRawCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSystemCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkSystemCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkStaticRawCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkStaticRawCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkStaticSystemCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkStaticSystemCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkDelegateRawCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkDelegateRawCallByRef,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkDelegateSystemCall,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkDelegateSystemCallByRef,
|
||||
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkLoadCalldataIntoActivePtr,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkLoadReturndataIntoActivePtr,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkPtrAddIntoActive,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkPtrShrinkIntoActive,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkPtrPackIntoActive,
|
||||
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkMultiplicationHigh,
|
||||
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkGlobalLoad,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkGlobalExtraAbiData,
|
||||
/// The eponymous EraVM Yul extension instruction.
|
||||
ZkGlobalStore,
|
||||
}
|
||||
|
||||
impl Name {
|
||||
///
|
||||
/// Tries parsing the verbatim instruction.
|
||||
///
|
||||
fn parse_verbatim(input: &str) -> Option<Self> {
|
||||
let verbatim = input.strip_prefix("verbatim")?;
|
||||
let regex = regex::Regex::new(r"_(\d+)i_(\d+)o").expect("Always valid");
|
||||
let captures = regex.captures(verbatim)?;
|
||||
let input_size: usize = captures.get(1)?.as_str().parse().ok()?;
|
||||
let output_size: usize = captures.get(2)?.as_str().parse().ok()?;
|
||||
Some(Self::Verbatim {
|
||||
input_size,
|
||||
output_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Name {
|
||||
fn from(input: &str) -> Self {
|
||||
if let Some(verbatim) = Self::parse_verbatim(input) {
|
||||
return verbatim;
|
||||
}
|
||||
|
||||
match input {
|
||||
"add" => Self::Add,
|
||||
"sub" => Self::Sub,
|
||||
"mul" => Self::Mul,
|
||||
"div" => Self::Div,
|
||||
"mod" => Self::Mod,
|
||||
"sdiv" => Self::Sdiv,
|
||||
"smod" => Self::Smod,
|
||||
|
||||
"lt" => Self::Lt,
|
||||
"gt" => Self::Gt,
|
||||
"eq" => Self::Eq,
|
||||
"iszero" => Self::IsZero,
|
||||
"slt" => Self::Slt,
|
||||
"sgt" => Self::Sgt,
|
||||
|
||||
"or" => Self::Or,
|
||||
"xor" => Self::Xor,
|
||||
"not" => Self::Not,
|
||||
"and" => Self::And,
|
||||
"shl" => Self::Shl,
|
||||
"shr" => Self::Shr,
|
||||
"sar" => Self::Sar,
|
||||
"byte" => Self::Byte,
|
||||
"pop" => Self::Pop,
|
||||
|
||||
"addmod" => Self::AddMod,
|
||||
"mulmod" => Self::MulMod,
|
||||
"exp" => Self::Exp,
|
||||
"signextend" => Self::SignExtend,
|
||||
|
||||
"keccak256" => Self::Keccak256,
|
||||
|
||||
"mload" => Self::MLoad,
|
||||
"mstore" => Self::MStore,
|
||||
"mstore8" => Self::MStore8,
|
||||
"mcopy" => Self::MCopy,
|
||||
|
||||
"sload" => Self::SLoad,
|
||||
"sstore" => Self::SStore,
|
||||
"tload" => Self::TLoad,
|
||||
"tstore" => Self::TStore,
|
||||
"loadimmutable" => Self::LoadImmutable,
|
||||
"setimmutable" => Self::SetImmutable,
|
||||
|
||||
"calldataload" => Self::CallDataLoad,
|
||||
"calldatasize" => Self::CallDataSize,
|
||||
"calldatacopy" => Self::CallDataCopy,
|
||||
"codesize" => Self::CodeSize,
|
||||
"codecopy" => Self::CodeCopy,
|
||||
"returndatasize" => Self::ReturnDataSize,
|
||||
"returndatacopy" => Self::ReturnDataCopy,
|
||||
"extcodesize" => Self::ExtCodeSize,
|
||||
"extcodehash" => Self::ExtCodeHash,
|
||||
|
||||
"return" => Self::Return,
|
||||
"revert" => Self::Revert,
|
||||
|
||||
"log0" => Self::Log0,
|
||||
"log1" => Self::Log1,
|
||||
"log2" => Self::Log2,
|
||||
"log3" => Self::Log3,
|
||||
"log4" => Self::Log4,
|
||||
|
||||
"call" => Self::Call,
|
||||
"delegatecall" => Self::DelegateCall,
|
||||
"staticcall" => Self::StaticCall,
|
||||
|
||||
"create" => Self::Create,
|
||||
"create2" => Self::Create2,
|
||||
"$zk_create" => Self::ZkCreate,
|
||||
"$zk_create2" => Self::ZkCreate2,
|
||||
"datasize" => Self::DataSize,
|
||||
"dataoffset" => Self::DataOffset,
|
||||
"datacopy" => Self::DataCopy,
|
||||
|
||||
"stop" => Self::Stop,
|
||||
"invalid" => Self::Invalid,
|
||||
|
||||
"linkersymbol" => Self::LinkerSymbol,
|
||||
"memoryguard" => Self::MemoryGuard,
|
||||
|
||||
"address" => Self::Address,
|
||||
"caller" => Self::Caller,
|
||||
|
||||
"callvalue" => Self::CallValue,
|
||||
"gas" => Self::Gas,
|
||||
"balance" => Self::Balance,
|
||||
"selfbalance" => Self::SelfBalance,
|
||||
|
||||
"gaslimit" => Self::GasLimit,
|
||||
"gasprice" => Self::GasPrice,
|
||||
"origin" => Self::Origin,
|
||||
"chainid" => Self::ChainId,
|
||||
"timestamp" => Self::Timestamp,
|
||||
"number" => Self::Number,
|
||||
"blockhash" => Self::BlockHash,
|
||||
"blobhash" => Self::BlobHash,
|
||||
"difficulty" => Self::Difficulty,
|
||||
"prevrandao" => Self::Prevrandao,
|
||||
"coinbase" => Self::CoinBase,
|
||||
"basefee" => Self::BaseFee,
|
||||
"blobbasefee" => Self::BlobBaseFee,
|
||||
"msize" => Self::MSize,
|
||||
|
||||
"callcode" => Self::CallCode,
|
||||
"pc" => Self::Pc,
|
||||
"extcodecopy" => Self::ExtCodeCopy,
|
||||
"selfdestruct" => Self::SelfDestruct,
|
||||
|
||||
"$zk_to_l1" => Self::ZkToL1,
|
||||
"$zk_code_source" => Self::ZkCodeSource,
|
||||
"$zk_precompile" => Self::ZkPrecompile,
|
||||
"$zk_meta" => Self::ZkMeta,
|
||||
"$zk_set_context_u128" => Self::ZkSetContextU128,
|
||||
"$zk_set_pubdata_price" => Self::ZkSetPubdataPrice,
|
||||
"$zk_increment_tx_counter" => Self::ZkIncrementTxCounter,
|
||||
"$zk_event_initialize" => Self::ZkEventInitialize,
|
||||
"$zk_event_write" => Self::ZkEventWrite,
|
||||
|
||||
"$zk_mimic_call" => Self::ZkMimicCall,
|
||||
"$zk_system_mimic_call" => Self::ZkSystemMimicCall,
|
||||
"$zk_mimic_call_byref" => Self::ZkMimicCallByRef,
|
||||
"$zk_system_mimic_call_byref" => Self::ZkSystemMimicCallByRef,
|
||||
"$zk_raw_call" => Self::ZkRawCall,
|
||||
"$zk_raw_call_byref" => Self::ZkRawCallByRef,
|
||||
"$zk_system_call" => Self::ZkSystemCall,
|
||||
"$zk_system_call_byref" => Self::ZkSystemCallByRef,
|
||||
"$zk_static_raw_call" => Self::ZkStaticRawCall,
|
||||
"$zk_static_raw_call_byref" => Self::ZkStaticRawCallByRef,
|
||||
"$zk_static_system_call" => Self::ZkStaticSystemCall,
|
||||
"$zk_static_system_call_byref" => Self::ZkStaticSystemCallByRef,
|
||||
"$zk_delegate_raw_call" => Self::ZkDelegateRawCall,
|
||||
"$zk_delegate_raw_call_byref" => Self::ZkDelegateRawCallByRef,
|
||||
"$zk_delegate_system_call" => Self::ZkDelegateSystemCall,
|
||||
"$zk_delegate_system_call_byref" => Self::ZkDelegateSystemCallByRef,
|
||||
|
||||
"$zk_load_calldata_into_active_ptr" => Self::ZkLoadCalldataIntoActivePtr,
|
||||
"$zk_load_returndata_into_active_ptr" => Self::ZkLoadReturndataIntoActivePtr,
|
||||
"$zk_ptr_add_into_active" => Self::ZkPtrAddIntoActive,
|
||||
"$zk_ptr_shrink_into_active" => Self::ZkPtrShrinkIntoActive,
|
||||
"$zk_ptr_pack_into_active" => Self::ZkPtrPackIntoActive,
|
||||
|
||||
"$zk_multiplication_high" => Self::ZkMultiplicationHigh,
|
||||
|
||||
"$zk_global_load" => Self::ZkGlobalLoad,
|
||||
"$zk_global_extra_abi_data" => Self::ZkGlobalExtraAbiData,
|
||||
"$zk_global_store" => Self::ZkGlobalStore,
|
||||
|
||||
input => Self::UserDefined(input.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,841 @@
|
||||
//!
|
||||
//! Translates the verbatim simulations.
|
||||
//!
|
||||
|
||||
use anyhow::Ok;
|
||||
|
||||
use crate::yul::parser::statement::expression::function_call::FunctionCall;
|
||||
|
||||
///
|
||||
/// Translates the verbatim simulations.
|
||||
///
|
||||
pub fn verbatim<'ctx, D>(
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<'ctx, D>,
|
||||
call: &mut FunctionCall,
|
||||
input_size: usize,
|
||||
output_size: usize,
|
||||
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>>
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
if output_size > 1 {
|
||||
anyhow::bail!(
|
||||
"{} Verbatim instructions with multiple return values are not supported",
|
||||
call.location
|
||||
);
|
||||
}
|
||||
|
||||
let mut arguments = call.pop_arguments::<D, 1>(context)?;
|
||||
let identifier = arguments[0]
|
||||
.original
|
||||
.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("{} Verbatim literal is missing", call.location))?;
|
||||
match identifier.as_str() {
|
||||
identifier @ "to_l1" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::to_l1(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "code_source" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_general::code_source(context).map(Some)
|
||||
}
|
||||
identifier @ "precompile" => {
|
||||
const ARGUMENTS_COUNT: usize = 2;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::precompile(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "meta" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_general::meta(context).map(Some)
|
||||
}
|
||||
identifier @ "mimic_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2],
|
||||
vec![context.field_const(0), context.field_const(0)],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "mimic_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 2;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
vec![context.field_const(0), context.field_const(0)],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_mimic_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 7;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2],
|
||||
vec![
|
||||
arguments[3].into_int_value(),
|
||||
arguments[4].into_int_value(),
|
||||
arguments[5].into_int_value(),
|
||||
arguments[6].into_int_value(),
|
||||
],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_mimic_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 6;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
vec![
|
||||
arguments[2].into_int_value(),
|
||||
arguments[3].into_int_value(),
|
||||
arguments[4].into_int_value(),
|
||||
arguments[5].into_int_value(),
|
||||
],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "raw_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 4;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
todo!()
|
||||
//era_compiler_llvm_context::eravm_call::raw_far(
|
||||
// context,
|
||||
// context.llvm_runtime().far_call,
|
||||
// arguments[0].into_int_value(),
|
||||
// arguments[1],
|
||||
// arguments[2].into_int_value(),
|
||||
// arguments[3].into_int_value(),
|
||||
//)
|
||||
//.map(Some)
|
||||
}
|
||||
identifier @ "raw_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().far_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_call" => {
|
||||
unimplemented!()
|
||||
}
|
||||
identifier @ "system_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 5;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::system(
|
||||
context,
|
||||
context.llvm_runtime().far_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
context.field_const(0),
|
||||
context.field_const(0),
|
||||
vec![
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
arguments[3].into_int_value(),
|
||||
arguments[4].into_int_value(),
|
||||
],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "raw_static_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 4;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().static_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1],
|
||||
arguments[2].into_int_value(),
|
||||
arguments[3].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "raw_static_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().static_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_static_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 6;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::system(
|
||||
context,
|
||||
context.llvm_runtime().static_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1],
|
||||
arguments[4].into_int_value(),
|
||||
arguments[5].into_int_value(),
|
||||
vec![arguments[2].into_int_value(), arguments[3].into_int_value()],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_static_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 5;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::system(
|
||||
context,
|
||||
context.llvm_runtime().static_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
arguments[3].into_int_value(),
|
||||
arguments[4].into_int_value(),
|
||||
vec![arguments[1].into_int_value(), arguments[2].into_int_value()],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "raw_delegate_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 4;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().delegate_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1],
|
||||
arguments[2].into_int_value(),
|
||||
arguments[3].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "raw_delegate_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().delegate_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_delegate_call" => {
|
||||
const ARGUMENTS_COUNT: usize = 6;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::system(
|
||||
context,
|
||||
context.llvm_runtime().delegate_call,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1],
|
||||
arguments[4].into_int_value(),
|
||||
arguments[5].into_int_value(),
|
||||
vec![arguments[2].into_int_value(), arguments[3].into_int_value()],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "system_delegate_call_byref" => {
|
||||
const ARGUMENTS_COUNT: usize = 5;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_call::system(
|
||||
context,
|
||||
context.llvm_runtime().delegate_call_byref,
|
||||
arguments[0].into_int_value(),
|
||||
context.get_global_value(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_ACTIVE_POINTER,
|
||||
)?,
|
||||
arguments[3].into_int_value(),
|
||||
arguments[4].into_int_value(),
|
||||
vec![arguments[1].into_int_value(), arguments[2].into_int_value()],
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "set_context_u128" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::set_context_value(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "set_pubdata_price" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::set_pubdata_price(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "increment_tx_counter" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_general::increment_tx_counter(context).map(Some)
|
||||
}
|
||||
identifier @ "event_initialize" => {
|
||||
const ARGUMENTS_COUNT: usize = 2;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::event(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
true,
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "event_write" => {
|
||||
const ARGUMENTS_COUNT: usize = 2;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_general::event(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
false,
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "calldata_ptr_to_active" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_abi::calldata_ptr_to_active(context).map(Some)
|
||||
}
|
||||
identifier @ "return_data_ptr_to_active" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_abi::return_data_ptr_to_active(context).map(Some)
|
||||
}
|
||||
identifier @ "active_ptr_add_assign" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_add_assign(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "active_ptr_shrink_assign" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_shrink_assign(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "active_ptr_pack_assign" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_pack_assign(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "mul_high" => {
|
||||
const ARGUMENTS_COUNT: usize = 2;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_math::multiplication_512(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "throw" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_utils::throw(context);
|
||||
Ok(None)
|
||||
}
|
||||
identifier
|
||||
if identifier.starts_with(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_VERBATIM_GETTER_PREFIX,
|
||||
) =>
|
||||
{
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
match identifier
|
||||
.strip_prefix(era_compiler_llvm_context::eravm_const::GLOBAL_VERBATIM_GETTER_PREFIX)
|
||||
{
|
||||
Some(identifier)
|
||||
if identifier
|
||||
== era_compiler_llvm_context::eravm_const::GLOBAL_CALLDATA_POINTER =>
|
||||
{
|
||||
context.get_global_value(identifier).map(Some)
|
||||
}
|
||||
Some(identifier)
|
||||
if identifier == era_compiler_llvm_context::eravm_const::GLOBAL_CALL_FLAGS =>
|
||||
{
|
||||
context.get_global_value(identifier).map(Some)
|
||||
}
|
||||
Some(identifier)
|
||||
if identifier
|
||||
== era_compiler_llvm_context::eravm_const::GLOBAL_RETURN_DATA_POINTER =>
|
||||
{
|
||||
context.get_global_value(identifier).map(Some)
|
||||
}
|
||||
Some(identifier)
|
||||
if identifier.starts_with(
|
||||
era_compiler_llvm_context::eravm_const::GLOBAL_EXTRA_ABI_DATA,
|
||||
) =>
|
||||
{
|
||||
let stripped = identifier
|
||||
.strip_prefix(era_compiler_llvm_context::eravm_const::GLOBAL_EXTRA_ABI_DATA)
|
||||
.expect("Always exists");
|
||||
let stripped = stripped.strip_prefix('_').ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} Invalid global variable identifier `{:?}`",
|
||||
call.location,
|
||||
identifier
|
||||
)
|
||||
})?;
|
||||
let index = stripped.parse::<u64>().map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{} Invalid global variable identifier `{:?}`: {}",
|
||||
call.location,
|
||||
identifier,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
if index >= (era_compiler_llvm_context::eravm_const::EXTRA_ABI_DATA_SIZE as u64)
|
||||
{
|
||||
anyhow::bail!(
|
||||
"{} Extra ABI data overflow. Only indexes `0..=9` are allowed",
|
||||
call.location,
|
||||
);
|
||||
}
|
||||
era_compiler_llvm_context::eravm_abi::get_extra_abi_data(
|
||||
context,
|
||||
context.field_const(index),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier => Err(anyhow::anyhow!(
|
||||
"{} Invalid global variable identifier `{:?}`",
|
||||
call.location,
|
||||
identifier
|
||||
)),
|
||||
}
|
||||
}
|
||||
identifier @ "active_ptr_data_load" => {
|
||||
const ARGUMENTS_COUNT: usize = 1;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_data_load(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
)
|
||||
.map(Some)
|
||||
}
|
||||
identifier @ "active_ptr_data_size" => {
|
||||
const ARGUMENTS_COUNT: usize = 0;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_data_size(context).map(Some)
|
||||
}
|
||||
identifier @ "active_ptr_data_copy" => {
|
||||
const ARGUMENTS_COUNT: usize = 3;
|
||||
if input_size != ARGUMENTS_COUNT {
|
||||
anyhow::bail!(
|
||||
"{} Internal function `{}` expected {} arguments, found {}",
|
||||
call.location,
|
||||
identifier,
|
||||
ARGUMENTS_COUNT,
|
||||
input_size
|
||||
);
|
||||
}
|
||||
|
||||
let arguments = call.pop_arguments_llvm::<D, ARGUMENTS_COUNT>(context)?;
|
||||
era_compiler_llvm_context::eravm_abi::active_ptr_data_copy(
|
||||
context,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
arguments[2].into_int_value(),
|
||||
)
|
||||
.map(|_| None)
|
||||
}
|
||||
identifier => anyhow::bail!(
|
||||
"{} Found unknown internal function `{}`",
|
||||
call.location,
|
||||
identifier
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
//!
|
||||
//! The YUL source code literal.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
use num::Num;
|
||||
use num::One;
|
||||
use num::Zero;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::literal::boolean::Boolean as BooleanLiteral;
|
||||
use crate::yul::lexer::token::lexeme::literal::integer::Integer as IntegerLiteral;
|
||||
use crate::yul::lexer::token::lexeme::literal::Literal as LexicalLiteral;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::r#type::Type;
|
||||
|
||||
///
|
||||
/// Represents a literal in YUL without differentiating its type.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Literal {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The lexical literal.
|
||||
pub inner: LexicalLiteral,
|
||||
/// The type, if it has been explicitly specified.
|
||||
pub yul_type: Option<Type>,
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let (location, literal) = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Literal(literal),
|
||||
location,
|
||||
..
|
||||
} => (location, literal),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{literal}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let yul_type = match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Colon),
|
||||
..
|
||||
} => {
|
||||
lexer.next()?;
|
||||
Some(Type::parse(lexer, None)?)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
inner: literal,
|
||||
yul_type,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts the literal into its LLVM.
|
||||
///
|
||||
pub fn into_llvm<'ctx, D>(
|
||||
self,
|
||||
context: &era_compiler_llvm_context::EraVMContext<'ctx, D>,
|
||||
) -> anyhow::Result<era_compiler_llvm_context::EraVMArgument<'ctx>>
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
match self.inner {
|
||||
LexicalLiteral::Boolean(inner) => {
|
||||
let value = self
|
||||
.yul_type
|
||||
.unwrap_or_default()
|
||||
.into_llvm(context)
|
||||
.const_int(
|
||||
match inner {
|
||||
BooleanLiteral::False => 0,
|
||||
BooleanLiteral::True => 1,
|
||||
},
|
||||
false,
|
||||
)
|
||||
.as_basic_value_enum();
|
||||
|
||||
let constant = match inner {
|
||||
BooleanLiteral::False => num::BigUint::zero(),
|
||||
BooleanLiteral::True => num::BigUint::one(),
|
||||
};
|
||||
|
||||
Ok(era_compiler_llvm_context::EraVMArgument::new_with_constant(
|
||||
value, constant,
|
||||
))
|
||||
}
|
||||
LexicalLiteral::Integer(inner) => {
|
||||
let r#type = self.yul_type.unwrap_or_default().into_llvm(context);
|
||||
let value = match inner {
|
||||
IntegerLiteral::Decimal { ref inner } => r#type.const_int_from_string(
|
||||
inner.as_str(),
|
||||
inkwell::types::StringRadix::Decimal,
|
||||
),
|
||||
IntegerLiteral::Hexadecimal { ref inner } => r#type.const_int_from_string(
|
||||
&inner["0x".len()..],
|
||||
inkwell::types::StringRadix::Hexadecimal,
|
||||
),
|
||||
}
|
||||
.expect("The value is valid")
|
||||
.as_basic_value_enum();
|
||||
|
||||
let constant = match inner {
|
||||
IntegerLiteral::Decimal { ref inner } => num::BigUint::from_str_radix(
|
||||
inner.as_str(),
|
||||
era_compiler_common::BASE_DECIMAL,
|
||||
),
|
||||
IntegerLiteral::Hexadecimal { ref inner } => num::BigUint::from_str_radix(
|
||||
&inner["0x".len()..],
|
||||
era_compiler_common::BASE_HEXADECIMAL,
|
||||
),
|
||||
}
|
||||
.expect("Always valid");
|
||||
|
||||
Ok(era_compiler_llvm_context::EraVMArgument::new_with_constant(
|
||||
value, constant,
|
||||
))
|
||||
}
|
||||
LexicalLiteral::String(inner) => {
|
||||
let string = inner.inner;
|
||||
let r#type = self.yul_type.unwrap_or_default().into_llvm(context);
|
||||
|
||||
let mut hex_string = if inner.is_hexadecimal {
|
||||
string.clone()
|
||||
} else {
|
||||
let mut hex_string =
|
||||
String::with_capacity(era_compiler_common::BYTE_LENGTH_FIELD * 2);
|
||||
let mut index = 0;
|
||||
loop {
|
||||
if index >= string.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
if string[index..].starts_with('\\') {
|
||||
index += 1;
|
||||
|
||||
if string[index..].starts_with('x') {
|
||||
hex_string.push_str(&string[index + 1..index + 3]);
|
||||
index += 3;
|
||||
} else if string[index..].starts_with('u') {
|
||||
let codepoint_str = &string[index + 1..index + 5];
|
||||
let codepoint = u32::from_str_radix(
|
||||
codepoint_str,
|
||||
era_compiler_common::BASE_HEXADECIMAL,
|
||||
)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"Invalid codepoint `{}`: {}",
|
||||
codepoint_str,
|
||||
error
|
||||
)
|
||||
})?;
|
||||
let unicode_char = char::from_u32(codepoint).ok_or_else(|| {
|
||||
anyhow::anyhow!("Invalid codepoint {}", codepoint)
|
||||
})?;
|
||||
let mut unicode_bytes = vec![0u8; 3];
|
||||
unicode_char.encode_utf8(&mut unicode_bytes);
|
||||
|
||||
for byte in unicode_bytes.into_iter() {
|
||||
hex_string.push_str(format!("{:02x}", byte).as_str());
|
||||
}
|
||||
index += 5;
|
||||
} else if string[index..].starts_with('t') {
|
||||
hex_string.push_str("09");
|
||||
index += 1;
|
||||
} else if string[index..].starts_with('n') {
|
||||
hex_string.push_str("0a");
|
||||
index += 1;
|
||||
} else if string[index..].starts_with('r') {
|
||||
hex_string.push_str("0d");
|
||||
index += 1;
|
||||
} else if string[index..].starts_with('\n') {
|
||||
index += 1;
|
||||
} else {
|
||||
hex_string
|
||||
.push_str(format!("{:02x}", string.as_bytes()[index]).as_str());
|
||||
index += 1;
|
||||
}
|
||||
} else {
|
||||
hex_string
|
||||
.push_str(format!("{:02x}", string.as_bytes()[index]).as_str());
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
hex_string
|
||||
};
|
||||
|
||||
if hex_string.len() > era_compiler_common::BYTE_LENGTH_FIELD * 2 {
|
||||
return Ok(era_compiler_llvm_context::EraVMArgument::new_with_original(
|
||||
r#type.const_zero().as_basic_value_enum(),
|
||||
string,
|
||||
));
|
||||
}
|
||||
|
||||
if hex_string.len() < era_compiler_common::BYTE_LENGTH_FIELD * 2 {
|
||||
hex_string.push_str(
|
||||
"0".repeat((era_compiler_common::BYTE_LENGTH_FIELD * 2) - hex_string.len())
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
let value = r#type
|
||||
.const_int_from_string(
|
||||
hex_string.as_str(),
|
||||
inkwell::types::StringRadix::Hexadecimal,
|
||||
)
|
||||
.expect("The value is valid")
|
||||
.as_basic_value_enum();
|
||||
Ok(era_compiler_llvm_context::EraVMArgument::new_with_original(
|
||||
value, string,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//!
|
||||
//! The expression statement.
|
||||
//!
|
||||
|
||||
pub mod function_call;
|
||||
pub mod literal;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::identifier::Identifier;
|
||||
|
||||
use self::function_call::FunctionCall;
|
||||
use self::literal::Literal;
|
||||
|
||||
///
|
||||
/// The Yul expression statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Expression {
|
||||
/// The function call subexpression.
|
||||
FunctionCall(FunctionCall),
|
||||
/// The identifier operand.
|
||||
Identifier(Identifier),
|
||||
/// The literal operand.
|
||||
Literal(Literal),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let (location, identifier) = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Literal(_),
|
||||
..
|
||||
} => return Ok(Self::Literal(Literal::parse(lexer, Some(token))?)),
|
||||
Token {
|
||||
location,
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} => (location, identifier),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{literal}", "{identifier}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let length = identifier.inner.len();
|
||||
|
||||
match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft),
|
||||
..
|
||||
} => {
|
||||
lexer.next()?;
|
||||
Ok(Self::FunctionCall(FunctionCall::parse(
|
||||
lexer,
|
||||
Some(Token::new(location, Lexeme::Identifier(identifier), length)),
|
||||
)?))
|
||||
}
|
||||
_ => Ok(Self::Identifier(Identifier::new(
|
||||
location,
|
||||
identifier.inner,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
match self {
|
||||
Self::FunctionCall(inner) => inner.get_missing_libraries(),
|
||||
Self::Identifier(_) => HashSet::new(),
|
||||
Self::Literal(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the statement location.
|
||||
///
|
||||
pub fn location(&self) -> Location {
|
||||
match self {
|
||||
Self::FunctionCall(inner) => inner.location,
|
||||
Self::Identifier(inner) => inner.location,
|
||||
Self::Literal(inner) => inner.location,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts the expression into an LLVM value.
|
||||
///
|
||||
pub fn into_llvm<'ctx, D>(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<'ctx, D>,
|
||||
) -> anyhow::Result<Option<era_compiler_llvm_context::EraVMArgument<'ctx>>>
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
match self {
|
||||
Self::Literal(literal) => literal
|
||||
.clone()
|
||||
.into_llvm(context)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{} Invalid literal `{}`: {}",
|
||||
literal.location,
|
||||
literal.inner.to_string(),
|
||||
error
|
||||
)
|
||||
})
|
||||
.map(Some),
|
||||
Self::Identifier(identifier) => {
|
||||
let pointer = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_stack_pointer(identifier.inner.as_str())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} Undeclared variable `{}`",
|
||||
identifier.location,
|
||||
identifier.inner,
|
||||
)
|
||||
})?;
|
||||
|
||||
let constant = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.yul()
|
||||
.get_constant(identifier.inner.as_str());
|
||||
|
||||
let value = context.build_load(pointer, identifier.inner.as_str())?;
|
||||
|
||||
match constant {
|
||||
Some(constant) => Ok(Some(
|
||||
era_compiler_llvm_context::EraVMArgument::new_with_constant(
|
||||
value, constant,
|
||||
),
|
||||
)),
|
||||
None => Ok(Some(value.into())),
|
||||
}
|
||||
}
|
||||
Self::FunctionCall(call) => Ok(call
|
||||
.into_llvm(context)?
|
||||
.map(era_compiler_llvm_context::EraVMArgument::new)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//!
|
||||
//! The for-loop statement.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
|
||||
///
|
||||
/// The Yul for-loop statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct ForLoop {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The index variables initialization block.
|
||||
pub initializer: Block,
|
||||
/// The continue condition block.
|
||||
pub condition: Expression,
|
||||
/// The index variables mutating block.
|
||||
pub finalizer: Block,
|
||||
/// The loop body.
|
||||
pub body: Block,
|
||||
}
|
||||
|
||||
impl ForLoop {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
let location = token.location;
|
||||
|
||||
let initializer = Block::parse(lexer, Some(token))?;
|
||||
|
||||
let condition = Expression::parse(lexer, None)?;
|
||||
|
||||
let finalizer = Block::parse(lexer, None)?;
|
||||
|
||||
let body = Block::parse(lexer, None)?;
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
initializer,
|
||||
condition,
|
||||
finalizer,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
let mut libraries = self.initializer.get_missing_libraries();
|
||||
libraries.extend(self.condition.get_missing_libraries());
|
||||
libraries.extend(self.finalizer.get_missing_libraries());
|
||||
libraries.extend(self.body.get_missing_libraries());
|
||||
libraries
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for ForLoop
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
self.initializer.into_llvm(context)?;
|
||||
|
||||
let condition_block = context.append_basic_block("for_condition");
|
||||
let body_block = context.append_basic_block("for_body");
|
||||
let increment_block = context.append_basic_block("for_increment");
|
||||
let join_block = context.append_basic_block("for_join");
|
||||
|
||||
context.build_unconditional_branch(condition_block);
|
||||
context.set_basic_block(condition_block);
|
||||
let condition = self
|
||||
.condition
|
||||
.into_llvm(context)?
|
||||
.expect("Always exists")
|
||||
.to_llvm()
|
||||
.into_int_value();
|
||||
let condition = context.builder().build_int_z_extend_or_bit_cast(
|
||||
condition,
|
||||
context.field_type(),
|
||||
"for_condition_extended",
|
||||
)?;
|
||||
let condition = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::NE,
|
||||
condition,
|
||||
context.field_const(0),
|
||||
"for_condition_compared",
|
||||
)?;
|
||||
context.build_conditional_branch(condition, body_block, join_block)?;
|
||||
|
||||
context.push_loop(body_block, increment_block, join_block);
|
||||
|
||||
context.set_basic_block(body_block);
|
||||
self.body.into_llvm(context)?;
|
||||
context.build_unconditional_branch(increment_block);
|
||||
|
||||
context.set_basic_block(increment_block);
|
||||
self.finalizer.into_llvm(context)?;
|
||||
context.build_unconditional_branch(condition_block);
|
||||
|
||||
context.pop_loop();
|
||||
context.set_basic_block(join_block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,720 @@
|
||||
//!
|
||||
//! The function definition statement.
|
||||
//!
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::identifier::Identifier;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
use crate::yul::parser::statement::expression::function_call::name::Name as FunctionName;
|
||||
|
||||
///
|
||||
/// The function definition statement.
|
||||
///
|
||||
/// All functions are translated in two steps:
|
||||
/// 1. The hoisted declaration
|
||||
/// 2. The definition, which now has the access to all function signatures
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionDefinition {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The function identifier.
|
||||
pub identifier: String,
|
||||
/// The function formal arguments.
|
||||
pub arguments: Vec<Identifier>,
|
||||
/// The function return variables.
|
||||
pub result: Vec<Identifier>,
|
||||
/// The function body block.
|
||||
pub body: Block,
|
||||
/// The function LLVM attributes encoded in the identifier.
|
||||
pub attributes: BTreeSet<era_compiler_llvm_context::EraVMAttribute>,
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
/// The LLVM attribute section prefix.
|
||||
pub const LLVM_ATTRIBUTE_PREFIX: &'static str = "$llvm_";
|
||||
|
||||
/// The LLVM attribute section suffix.
|
||||
pub const LLVM_ATTRIBUTE_SUFFIX: &'static str = "_llvm$";
|
||||
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let (location, identifier) = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
location,
|
||||
..
|
||||
} => (location, identifier),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{identifier}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let identifier = Identifier::new(location, identifier.inner);
|
||||
|
||||
match FunctionName::from(identifier.inner.as_str()) {
|
||||
FunctionName::UserDefined(_) => {}
|
||||
_function_name => {
|
||||
return Err(ParserError::ReservedIdentifier {
|
||||
location,
|
||||
identifier: identifier.inner,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
match lexer.next()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft),
|
||||
..
|
||||
} => {}
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["("],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let (mut arguments, next) = Identifier::parse_typed_list(lexer, None)?;
|
||||
if identifier
|
||||
.inner
|
||||
.contains(era_compiler_llvm_context::EraVMFunction::ZKSYNC_NEAR_CALL_ABI_PREFIX)
|
||||
{
|
||||
if arguments.is_empty() {
|
||||
return Err(ParserError::InvalidNumberOfArguments {
|
||||
location,
|
||||
identifier: identifier.inner,
|
||||
expected: 1,
|
||||
found: arguments.len(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
arguments.remove(0);
|
||||
}
|
||||
if identifier.inner.contains(
|
||||
era_compiler_llvm_context::EraVMFunction::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER,
|
||||
) && !arguments.is_empty()
|
||||
{
|
||||
return Err(ParserError::InvalidNumberOfArguments {
|
||||
location,
|
||||
identifier: identifier.inner,
|
||||
expected: 0,
|
||||
found: arguments.len(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
match crate::yul::parser::take_or_next(next, lexer)? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::ParenthesisRight),
|
||||
..
|
||||
} => {}
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec![")"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let (result, next) = match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Arrow),
|
||||
..
|
||||
} => {
|
||||
lexer.next()?;
|
||||
Identifier::parse_typed_list(lexer, None)?
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyLeft),
|
||||
..
|
||||
} => (vec![], None),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["->", "{"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let body = Block::parse(lexer, next)?;
|
||||
|
||||
let attributes = Self::get_llvm_attributes(&identifier)?;
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
identifier: identifier.inner,
|
||||
arguments,
|
||||
result,
|
||||
body,
|
||||
attributes,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Gets the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
self.body.get_missing_libraries()
|
||||
}
|
||||
|
||||
///
|
||||
/// Gets the list of LLVM attributes provided in the function name.
|
||||
///
|
||||
pub fn get_llvm_attributes(
|
||||
identifier: &Identifier,
|
||||
) -> Result<BTreeSet<era_compiler_llvm_context::EraVMAttribute>, Error> {
|
||||
let mut valid_attributes = BTreeSet::new();
|
||||
|
||||
let llvm_begin = identifier.inner.find(Self::LLVM_ATTRIBUTE_PREFIX);
|
||||
let llvm_end = identifier.inner.find(Self::LLVM_ATTRIBUTE_SUFFIX);
|
||||
let attribute_string = if let (Some(llvm_begin), Some(llvm_end)) = (llvm_begin, llvm_end) {
|
||||
if llvm_begin < llvm_end {
|
||||
&identifier.inner[llvm_begin + Self::LLVM_ATTRIBUTE_PREFIX.len()..llvm_end]
|
||||
} else {
|
||||
return Ok(valid_attributes);
|
||||
}
|
||||
} else {
|
||||
return Ok(valid_attributes);
|
||||
};
|
||||
|
||||
let mut invalid_attributes = BTreeSet::new();
|
||||
for value in attribute_string.split('_') {
|
||||
match era_compiler_llvm_context::EraVMAttribute::try_from(value) {
|
||||
Ok(attribute) => valid_attributes.insert(attribute),
|
||||
Err(value) => invalid_attributes.insert(value),
|
||||
};
|
||||
}
|
||||
|
||||
if !invalid_attributes.is_empty() {
|
||||
return Err(ParserError::InvalidAttributes {
|
||||
location: identifier.location,
|
||||
values: invalid_attributes,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(valid_attributes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for FunctionDefinition
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn declare(
|
||||
&mut self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let argument_types: Vec<_> = self
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|argument| {
|
||||
let yul_type = argument.r#type.to_owned().unwrap_or_default();
|
||||
yul_type.into_llvm(context).as_basic_type_enum()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let function_type = context.function_type(
|
||||
argument_types,
|
||||
self.result.len(),
|
||||
self.identifier
|
||||
.starts_with(era_compiler_llvm_context::EraVMFunction::ZKSYNC_NEAR_CALL_ABI_PREFIX),
|
||||
);
|
||||
|
||||
let function = context.add_function(
|
||||
self.identifier.as_str(),
|
||||
function_type,
|
||||
self.result.len(),
|
||||
Some(inkwell::module::Linkage::Private),
|
||||
)?;
|
||||
era_compiler_llvm_context::EraVMFunction::set_attributes(
|
||||
context.llvm(),
|
||||
function.borrow().declaration(),
|
||||
self.attributes.clone().into_iter().collect(),
|
||||
true,
|
||||
);
|
||||
function
|
||||
.borrow_mut()
|
||||
.set_yul_data(era_compiler_llvm_context::EraVMFunctionYulData::default());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn into_llvm(
|
||||
mut self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
context.set_current_function(self.identifier.as_str())?;
|
||||
let r#return = context.current_function().borrow().r#return();
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||
match r#return {
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::None => {}
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Primitive { pointer } => {
|
||||
let identifier = self.result.pop().expect("Always exists");
|
||||
let r#type = identifier.r#type.unwrap_or_default();
|
||||
context.build_store(pointer, r#type.into_llvm(context).const_zero())?;
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.insert_stack_pointer(identifier.inner, pointer);
|
||||
}
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Compound { pointer, .. } => {
|
||||
for (index, identifier) in self.result.into_iter().enumerate() {
|
||||
let r#type = identifier.r#type.unwrap_or_default().into_llvm(context);
|
||||
let pointer = context.build_gep(
|
||||
pointer,
|
||||
&[
|
||||
context.field_const(0),
|
||||
context
|
||||
.integer_type(era_compiler_common::BIT_LENGTH_X32)
|
||||
.const_int(index as u64, false),
|
||||
],
|
||||
context.field_type(),
|
||||
format!("return_{index}_gep_pointer").as_str(),
|
||||
);
|
||||
context.build_store(pointer, r#type.const_zero())?;
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.insert_stack_pointer(identifier.inner.clone(), pointer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let argument_types: Vec<_> = self
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|argument| {
|
||||
let yul_type = argument.r#type.to_owned().unwrap_or_default();
|
||||
yul_type.into_llvm(context)
|
||||
})
|
||||
.collect();
|
||||
for (mut index, argument) in self.arguments.iter().enumerate() {
|
||||
let pointer = context.build_alloca(argument_types[index], argument.inner.as_str());
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.insert_stack_pointer(argument.inner.clone(), pointer);
|
||||
if self
|
||||
.identifier
|
||||
.starts_with(era_compiler_llvm_context::EraVMFunction::ZKSYNC_NEAR_CALL_ABI_PREFIX)
|
||||
&& matches!(
|
||||
context.current_function().borrow().r#return(),
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Compound { .. }
|
||||
)
|
||||
&& context.is_system_mode()
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
context.build_store(
|
||||
pointer,
|
||||
context.current_function().borrow().get_nth_param(index),
|
||||
)?;
|
||||
}
|
||||
|
||||
self.body.into_llvm(context)?;
|
||||
match context
|
||||
.basic_block()
|
||||
.get_last_instruction()
|
||||
.map(|instruction| instruction.get_opcode())
|
||||
{
|
||||
Some(inkwell::values::InstructionOpcode::Br) => {}
|
||||
Some(inkwell::values::InstructionOpcode::Switch) => {}
|
||||
_ => context
|
||||
.build_unconditional_branch(context.current_function().borrow().return_block()),
|
||||
}
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().return_block());
|
||||
match context.current_function().borrow().r#return() {
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::None => {
|
||||
context.build_return(None);
|
||||
}
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Primitive { pointer } => {
|
||||
let return_value = context.build_load(pointer, "return_value")?;
|
||||
context.build_return(Some(&return_value));
|
||||
}
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Compound { pointer, .. }
|
||||
if context.current_function().borrow().name().starts_with(
|
||||
era_compiler_llvm_context::EraVMFunction::ZKSYNC_NEAR_CALL_ABI_PREFIX,
|
||||
) =>
|
||||
{
|
||||
context.build_return(Some(&pointer.value));
|
||||
}
|
||||
era_compiler_llvm_context::EraVMFunctionReturn::Compound { pointer, .. } => {
|
||||
let return_value = context.build_load(pointer, "return_value")?;
|
||||
context.build_return(Some(&return_value));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_identifier() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function 256() -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(14, 22),
|
||||
expected: vec!["{identifier}"],
|
||||
found: "256".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_parenthesis_left() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function test{) -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(14, 26),
|
||||
expected: vec!["("],
|
||||
found: "{".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_parenthesis_right() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function test(} -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(14, 27),
|
||||
expected: vec![")"],
|
||||
found: "}".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_arrow_or_bracket_curly_left() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function test() := result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(14, 29),
|
||||
expected: vec!["->", "{"],
|
||||
found: ":=".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_number_of_arguments_near_call_abi() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function ZKSYNC_NEAR_CALL_test() -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidNumberOfArguments {
|
||||
location: Location::new(14, 22),
|
||||
identifier: "ZKSYNC_NEAR_CALL_test".to_owned(),
|
||||
expected: 1,
|
||||
found: 0,
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_number_of_arguments_near_call_abi_catch() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function ZKSYNC_CATCH_NEAR_CALL(length) {
|
||||
revert(0, length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidNumberOfArguments {
|
||||
location: Location::new(14, 22),
|
||||
identifier: "ZKSYNC_CATCH_NEAR_CALL".to_owned(),
|
||||
expected: 0,
|
||||
found: 1,
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_reserved_identifier() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function basefee() -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::ReservedIdentifier {
|
||||
location: Location::new(14, 22),
|
||||
identifier: "basefee".to_owned()
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_attributes_single() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function test_$llvm_UnknownAttribute_llvm$_test() -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let mut invalid_attributes = BTreeSet::new();
|
||||
invalid_attributes.insert("UnknownAttribute".to_owned());
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidAttributes {
|
||||
location: Location::new(14, 22),
|
||||
values: invalid_attributes,
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_attributes_multiple_repeated() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
|
||||
function test_$llvm_UnknownAttribute1_UnknownAttribute1_UnknownAttribute2_llvm$_test() -> result {
|
||||
result := 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let mut invalid_attributes = BTreeSet::new();
|
||||
invalid_attributes.insert("UnknownAttribute1".to_owned());
|
||||
invalid_attributes.insert("UnknownAttribute2".to_owned());
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidAttributes {
|
||||
location: Location::new(14, 22),
|
||||
values: invalid_attributes,
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//!
|
||||
//! The if-conditional statement.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
|
||||
///
|
||||
/// The Yul if-conditional statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct IfConditional {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The condition expression.
|
||||
pub condition: Expression,
|
||||
/// The conditional block.
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
impl IfConditional {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
let location = token.location;
|
||||
|
||||
let condition = Expression::parse(lexer, Some(token))?;
|
||||
|
||||
let block = Block::parse(lexer, None)?;
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
condition,
|
||||
block,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
let mut libraries = self.condition.get_missing_libraries();
|
||||
libraries.extend(self.block.get_missing_libraries());
|
||||
libraries
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for IfConditional
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let condition = self
|
||||
.condition
|
||||
.into_llvm(context)?
|
||||
.expect("Always exists")
|
||||
.to_llvm()
|
||||
.into_int_value();
|
||||
let condition = context.builder().build_int_z_extend_or_bit_cast(
|
||||
condition,
|
||||
context.field_type(),
|
||||
"if_condition_extended",
|
||||
)?;
|
||||
let condition = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::NE,
|
||||
condition,
|
||||
context.field_const(0),
|
||||
"if_condition_compared",
|
||||
)?;
|
||||
let main_block = context.append_basic_block("if_main");
|
||||
let join_block = context.append_basic_block("if_join");
|
||||
context.build_conditional_branch(condition, main_block, join_block)?;
|
||||
context.set_basic_block(main_block);
|
||||
self.block.into_llvm(context)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
context.set_basic_block(join_block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
//!
|
||||
//! The block statement.
|
||||
//!
|
||||
|
||||
pub mod assignment;
|
||||
pub mod block;
|
||||
pub mod code;
|
||||
pub mod expression;
|
||||
pub mod for_loop;
|
||||
pub mod function_definition;
|
||||
pub mod if_conditional;
|
||||
pub mod object;
|
||||
pub mod switch;
|
||||
pub mod variable_declaration;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::keyword::Keyword;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
|
||||
use self::assignment::Assignment;
|
||||
use self::block::Block;
|
||||
use self::code::Code;
|
||||
use self::expression::Expression;
|
||||
use self::for_loop::ForLoop;
|
||||
use self::function_definition::FunctionDefinition;
|
||||
use self::if_conditional::IfConditional;
|
||||
use self::object::Object;
|
||||
use self::switch::Switch;
|
||||
use self::variable_declaration::VariableDeclaration;
|
||||
|
||||
///
|
||||
/// The Yul block statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Statement {
|
||||
/// The object element.
|
||||
Object(Object),
|
||||
/// The code element.
|
||||
Code(Code),
|
||||
/// The code block.
|
||||
Block(Block),
|
||||
/// The expression.
|
||||
Expression(Expression),
|
||||
/// The `function` statement.
|
||||
FunctionDefinition(FunctionDefinition),
|
||||
/// The `let` statement.
|
||||
VariableDeclaration(VariableDeclaration),
|
||||
/// The `:=` existing variables reassignment statement.
|
||||
Assignment(Assignment),
|
||||
/// The `if` statement.
|
||||
IfConditional(IfConditional),
|
||||
/// The `switch` statement.
|
||||
Switch(Switch),
|
||||
/// The `for` statement.
|
||||
ForLoop(ForLoop),
|
||||
/// The `continue` statement.
|
||||
Continue(Location),
|
||||
/// The `break` statement.
|
||||
Break(Location),
|
||||
/// The `leave` statement.
|
||||
Leave(Location),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(
|
||||
lexer: &mut Lexer,
|
||||
initial: Option<Token>,
|
||||
) -> Result<(Self, Option<Token>), Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
match token {
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Object),
|
||||
..
|
||||
} => Ok((Statement::Object(Object::parse(lexer, Some(token))?), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Code),
|
||||
..
|
||||
} => Ok((Statement::Code(Code::parse(lexer, None)?), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Function),
|
||||
..
|
||||
} => Ok((
|
||||
Statement::FunctionDefinition(FunctionDefinition::parse(lexer, None)?),
|
||||
None,
|
||||
)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Let),
|
||||
..
|
||||
} => {
|
||||
let (statement, next) = VariableDeclaration::parse(lexer, None)?;
|
||||
Ok((Statement::VariableDeclaration(statement), next))
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::If),
|
||||
..
|
||||
} => Ok((
|
||||
Statement::IfConditional(IfConditional::parse(lexer, None)?),
|
||||
None,
|
||||
)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Switch),
|
||||
..
|
||||
} => Ok((Statement::Switch(Switch::parse(lexer, None)?), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::For),
|
||||
..
|
||||
} => Ok((Statement::ForLoop(ForLoop::parse(lexer, None)?), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Continue),
|
||||
location,
|
||||
..
|
||||
} => Ok((Statement::Continue(location), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Break),
|
||||
location,
|
||||
..
|
||||
} => Ok((Statement::Break(location), None)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Leave),
|
||||
location,
|
||||
..
|
||||
} => Ok((Statement::Leave(location), None)),
|
||||
token => Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec![
|
||||
"object", "code", "function", "let", "if", "switch", "for", "continue",
|
||||
"break", "leave",
|
||||
],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
match self {
|
||||
Self::Object(inner) => inner.get_missing_libraries(),
|
||||
Self::Code(inner) => inner.get_missing_libraries(),
|
||||
Self::Block(inner) => inner.get_missing_libraries(),
|
||||
Self::Expression(inner) => inner.get_missing_libraries(),
|
||||
Self::FunctionDefinition(inner) => inner.get_missing_libraries(),
|
||||
Self::VariableDeclaration(inner) => inner.get_missing_libraries(),
|
||||
Self::Assignment(inner) => inner.get_missing_libraries(),
|
||||
Self::IfConditional(inner) => inner.get_missing_libraries(),
|
||||
Self::Switch(inner) => inner.get_missing_libraries(),
|
||||
Self::ForLoop(inner) => inner.get_missing_libraries(),
|
||||
Self::Continue(_) => HashSet::new(),
|
||||
Self::Break(_) => HashSet::new(),
|
||||
Self::Leave(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the statement location.
|
||||
///
|
||||
pub fn location(&self) -> Location {
|
||||
match self {
|
||||
Self::Object(inner) => inner.location,
|
||||
Self::Code(inner) => inner.location,
|
||||
Self::Block(inner) => inner.location,
|
||||
Self::Expression(inner) => inner.location(),
|
||||
Self::FunctionDefinition(inner) => inner.location,
|
||||
Self::VariableDeclaration(inner) => inner.location,
|
||||
Self::Assignment(inner) => inner.location,
|
||||
Self::IfConditional(inner) => inner.location,
|
||||
Self::Switch(inner) => inner.location,
|
||||
Self::ForLoop(inner) => inner.location,
|
||||
Self::Continue(location) => *location,
|
||||
Self::Break(location) => *location,
|
||||
Self::Leave(location) => *location,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
//!
|
||||
//! The YUL object.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::keyword::Keyword;
|
||||
use crate::yul::lexer::token::lexeme::literal::Literal;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::statement::code::Code;
|
||||
|
||||
///
|
||||
/// The upper-level YUL object, representing the deploy code.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Object {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The identifier.
|
||||
pub identifier: String,
|
||||
/// The code.
|
||||
pub code: Code,
|
||||
/// The optional inner object, representing the runtime code.
|
||||
pub inner_object: Option<Box<Self>>,
|
||||
/// The factory dependency objects, which are represented by nested Yul object. The nested
|
||||
/// objects are duplicates of the upper-level objects describing the dependencies, so only
|
||||
/// their identifiers are preserved. The identifiers are used to address upper-level objects.
|
||||
pub factory_dependencies: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let location = match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Object),
|
||||
location,
|
||||
..
|
||||
} => location,
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["object"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let identifier = match lexer.next()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Literal(Literal::String(literal)),
|
||||
..
|
||||
} => literal.inner,
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{string}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let is_runtime_code = identifier.ends_with("_deployed");
|
||||
|
||||
match lexer.next()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyLeft),
|
||||
..
|
||||
} => {}
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let code = Code::parse(lexer, None)?;
|
||||
let mut inner_object = None;
|
||||
let mut factory_dependencies = HashSet::new();
|
||||
|
||||
if !is_runtime_code {
|
||||
inner_object = match lexer.peek()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Object),
|
||||
..
|
||||
} => {
|
||||
let mut object = Self::parse(lexer, None)?;
|
||||
|
||||
if format!("{identifier}_deployed") != object.identifier {
|
||||
return Err(ParserError::InvalidObjectName {
|
||||
location: object.location,
|
||||
expected: format!("{identifier}_deployed"),
|
||||
found: object.identifier,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
factory_dependencies.extend(object.factory_dependencies.drain());
|
||||
Some(Box::new(object))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Token {
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} = lexer.peek()?
|
||||
{
|
||||
if identifier.inner.as_str() == "data" {
|
||||
let _data = lexer.next()?;
|
||||
let _identifier = lexer.next()?;
|
||||
let _metadata = lexer.next()?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
loop {
|
||||
match lexer.next()? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::BracketCurlyRight),
|
||||
..
|
||||
} => break,
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Object),
|
||||
..
|
||||
} => {
|
||||
let dependency = Self::parse(lexer, Some(token))?;
|
||||
factory_dependencies.insert(dependency.identifier);
|
||||
}
|
||||
Token {
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} if identifier.inner.as_str() == "data" => {
|
||||
let _identifier = lexer.next()?;
|
||||
let _metadata = lexer.next()?;
|
||||
}
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["object", "}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
identifier,
|
||||
code,
|
||||
inner_object,
|
||||
factory_dependencies,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
let mut missing_libraries = self.code.get_missing_libraries();
|
||||
if let Some(inner_object) = &self.inner_object {
|
||||
missing_libraries.extend(inner_object.get_missing_libraries());
|
||||
}
|
||||
missing_libraries
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Object
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn declare(
|
||||
&mut self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut entry = era_compiler_llvm_context::EraVMEntryFunction::default();
|
||||
entry.declare(context)?;
|
||||
|
||||
let mut runtime = era_compiler_llvm_context::EraVMRuntime::new(
|
||||
era_compiler_llvm_context::EraVMAddressSpace::Heap,
|
||||
);
|
||||
runtime.declare(context)?;
|
||||
|
||||
era_compiler_llvm_context::EraVMDeployCodeFunction::new(
|
||||
era_compiler_llvm_context::EraVMDummyLLVMWritable::default(),
|
||||
)
|
||||
.declare(context)?;
|
||||
era_compiler_llvm_context::EraVMRuntimeCodeFunction::new(
|
||||
era_compiler_llvm_context::EraVMDummyLLVMWritable::default(),
|
||||
)
|
||||
.declare(context)?;
|
||||
|
||||
for name in [
|
||||
era_compiler_llvm_context::EraVMRuntime::FUNCTION_DEPLOY_CODE,
|
||||
era_compiler_llvm_context::EraVMRuntime::FUNCTION_RUNTIME_CODE,
|
||||
era_compiler_llvm_context::EraVMRuntime::FUNCTION_ENTRY,
|
||||
]
|
||||
.into_iter()
|
||||
{
|
||||
context
|
||||
.get_function(name)
|
||||
.expect("Always exists")
|
||||
.borrow_mut()
|
||||
.set_yul_data(era_compiler_llvm_context::EraVMFunctionYulData::default());
|
||||
}
|
||||
|
||||
entry.into_llvm(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.identifier.ends_with("_deployed") {
|
||||
era_compiler_llvm_context::EraVMRuntimeCodeFunction::new(self.code)
|
||||
.into_llvm(context)?;
|
||||
} else {
|
||||
era_compiler_llvm_context::EraVMDeployCodeFunction::new(self.code)
|
||||
.into_llvm(context)?;
|
||||
}
|
||||
|
||||
match self.inner_object {
|
||||
Some(object) => {
|
||||
object.into_llvm(context)?;
|
||||
}
|
||||
None => {
|
||||
let runtime = era_compiler_llvm_context::EraVMRuntime::new(
|
||||
era_compiler_llvm_context::EraVMAddressSpace::Heap,
|
||||
);
|
||||
runtime.into_llvm(context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_object() {
|
||||
let input = r#"
|
||||
class "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(2, 1),
|
||||
expected: vec!["object"],
|
||||
found: "class".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_identifier() {
|
||||
let input = r#"
|
||||
object 256 {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(2, 8),
|
||||
expected: vec!["{string}"],
|
||||
found: "256".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_bracket_curly_left() {
|
||||
let input = r#"
|
||||
object "Test" (
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(2, 15),
|
||||
expected: vec!["{"],
|
||||
found: "(".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_object_inner() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
class "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(8, 5),
|
||||
expected: vec!["object", "}"],
|
||||
found: "class".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_object_name() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Invalid" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidObjectName {
|
||||
location: Location::new(8, 5),
|
||||
expected: "Test_deployed".to_owned(),
|
||||
found: "Invalid".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//!
|
||||
//! The switch statement case.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
use crate::yul::parser::statement::expression::literal::Literal;
|
||||
|
||||
///
|
||||
/// The Yul switch statement case.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Case {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The matched constant.
|
||||
pub literal: Literal,
|
||||
/// The case block.
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
let (location, literal) = match token {
|
||||
token @ Token {
|
||||
lexeme: Lexeme::Literal(_),
|
||||
location,
|
||||
..
|
||||
} => (location, Literal::parse(lexer, Some(token))?),
|
||||
token => {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{literal}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let block = Block::parse(lexer, None)?;
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
literal,
|
||||
block,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
self.block.get_missing_libraries()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_literal() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
switch 42
|
||||
case x {}
|
||||
default {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(12, 26),
|
||||
expected: vec!["{literal}"],
|
||||
found: "x".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
//!
|
||||
//! The switch statement.
|
||||
//!
|
||||
|
||||
pub mod case;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::keyword::Keyword;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::statement::block::Block;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
|
||||
use self::case::Case;
|
||||
|
||||
///
|
||||
/// The Yul switch statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Switch {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The expression being matched.
|
||||
pub expression: Expression,
|
||||
/// The non-default cases.
|
||||
pub cases: Vec<Case>,
|
||||
/// The optional default case, if `cases` do not cover all possible values.
|
||||
pub default: Option<Block>,
|
||||
}
|
||||
|
||||
///
|
||||
/// The parsing state.
|
||||
///
|
||||
pub enum State {
|
||||
/// After match expression.
|
||||
CaseOrDefaultKeyword,
|
||||
/// After `case`.
|
||||
CaseBlock,
|
||||
/// After `default`.
|
||||
DefaultBlock,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let mut token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
let location = token.location;
|
||||
let mut state = State::CaseOrDefaultKeyword;
|
||||
|
||||
let expression = Expression::parse(lexer, Some(token.clone()))?;
|
||||
let mut cases = Vec::new();
|
||||
let mut default = None;
|
||||
|
||||
loop {
|
||||
match state {
|
||||
State::CaseOrDefaultKeyword => match lexer.peek()? {
|
||||
_token @ Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Case),
|
||||
..
|
||||
} => {
|
||||
token = _token;
|
||||
state = State::CaseBlock;
|
||||
}
|
||||
_token @ Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Default),
|
||||
..
|
||||
} => {
|
||||
token = _token;
|
||||
state = State::DefaultBlock;
|
||||
}
|
||||
_token => {
|
||||
token = _token;
|
||||
break;
|
||||
}
|
||||
},
|
||||
State::CaseBlock => {
|
||||
lexer.next()?;
|
||||
cases.push(Case::parse(lexer, None)?);
|
||||
state = State::CaseOrDefaultKeyword;
|
||||
}
|
||||
State::DefaultBlock => {
|
||||
lexer.next()?;
|
||||
default = Some(Block::parse(lexer, None)?);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cases.is_empty() && default.is_none() {
|
||||
return Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["case", "default"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
location,
|
||||
expression,
|
||||
cases,
|
||||
default,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
let mut libraries = HashSet::new();
|
||||
for case in self.cases.iter() {
|
||||
libraries.extend(case.get_missing_libraries());
|
||||
}
|
||||
if let Some(default) = &self.default {
|
||||
libraries.extend(default.get_missing_libraries());
|
||||
}
|
||||
libraries
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Switch
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm(
|
||||
self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<D>,
|
||||
) -> anyhow::Result<()> {
|
||||
let scrutinee = self.expression.into_llvm(context)?;
|
||||
|
||||
if self.cases.is_empty() {
|
||||
if let Some(block) = self.default {
|
||||
block.into_llvm(context)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let current_block = context.basic_block();
|
||||
let join_block = context.append_basic_block("switch_join_block");
|
||||
|
||||
let mut branches = Vec::with_capacity(self.cases.len());
|
||||
for (index, case) in self.cases.into_iter().enumerate() {
|
||||
let constant = case.literal.into_llvm(context)?.to_llvm();
|
||||
|
||||
let expression_block = context
|
||||
.append_basic_block(format!("switch_case_branch_{}_block", index + 1).as_str());
|
||||
context.set_basic_block(expression_block);
|
||||
case.block.into_llvm(context)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
branches.push((constant.into_int_value(), expression_block));
|
||||
}
|
||||
|
||||
let default_block = match self.default {
|
||||
Some(default) => {
|
||||
let default_block = context.append_basic_block("switch_default_block");
|
||||
context.set_basic_block(default_block);
|
||||
default.into_llvm(context)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
default_block
|
||||
}
|
||||
None => join_block,
|
||||
};
|
||||
|
||||
context.set_basic_block(current_block);
|
||||
context.builder().build_switch(
|
||||
scrutinee.expect("Always exists").to_llvm().into_int_value(),
|
||||
default_block,
|
||||
branches.as_slice(),
|
||||
)?;
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_invalid_token_case() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
switch 42
|
||||
branch x {}
|
||||
default {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidToken {
|
||||
location: Location::new(12, 21),
|
||||
expected: vec!["case", "default"],
|
||||
found: "branch".to_owned(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
//!
|
||||
//! The variable declaration statement.
|
||||
//!
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::symbol::Symbol;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
use crate::yul::parser::identifier::Identifier;
|
||||
use crate::yul::parser::statement::expression::function_call::name::Name as FunctionName;
|
||||
use crate::yul::parser::statement::expression::Expression;
|
||||
|
||||
///
|
||||
/// The Yul variable declaration statement.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct VariableDeclaration {
|
||||
/// The location.
|
||||
pub location: Location,
|
||||
/// The variable bindings list.
|
||||
pub bindings: Vec<Identifier>,
|
||||
/// The variable initializing expression.
|
||||
pub expression: Option<Expression>,
|
||||
}
|
||||
|
||||
impl VariableDeclaration {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(
|
||||
lexer: &mut Lexer,
|
||||
initial: Option<Token>,
|
||||
) -> Result<(Self, Option<Token>), Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
let location = token.location;
|
||||
|
||||
let (bindings, next) = Identifier::parse_typed_list(lexer, Some(token))?;
|
||||
for binding in bindings.iter() {
|
||||
match FunctionName::from(binding.inner.as_str()) {
|
||||
FunctionName::UserDefined(_) => continue,
|
||||
_function_name => {
|
||||
return Err(ParserError::ReservedIdentifier {
|
||||
location: binding.location,
|
||||
identifier: binding.inner.to_owned(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match crate::yul::parser::take_or_next(next, lexer)? {
|
||||
Token {
|
||||
lexeme: Lexeme::Symbol(Symbol::Assignment),
|
||||
..
|
||||
} => {}
|
||||
token => {
|
||||
return Ok((
|
||||
Self {
|
||||
location,
|
||||
bindings,
|
||||
expression: None,
|
||||
},
|
||||
Some(token),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let expression = Expression::parse(lexer, None)?;
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
location,
|
||||
bindings,
|
||||
expression: Some(expression),
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the list of missing deployable libraries.
|
||||
///
|
||||
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
||||
self.expression
|
||||
.as_ref()
|
||||
.map_or_else(HashSet::new, |expression| {
|
||||
expression.get_missing_libraries()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for VariableDeclaration
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
fn into_llvm<'ctx>(
|
||||
mut self,
|
||||
context: &mut era_compiler_llvm_context::EraVMContext<'ctx, D>,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.bindings.len() == 1 {
|
||||
let identifier = self.bindings.remove(0);
|
||||
let r#type = identifier.r#type.unwrap_or_default().into_llvm(context);
|
||||
let pointer = context.build_alloca(r#type, identifier.inner.as_str());
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.insert_stack_pointer(identifier.inner.clone(), pointer);
|
||||
|
||||
let value = if let Some(expression) = self.expression {
|
||||
match expression.into_llvm(context)? {
|
||||
Some(mut value) => {
|
||||
if let Some(constant) = value.constant.take() {
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.yul_mut()
|
||||
.insert_constant(identifier.inner, constant);
|
||||
}
|
||||
|
||||
value.to_llvm()
|
||||
}
|
||||
None => r#type.const_zero().as_basic_value_enum(),
|
||||
}
|
||||
} else {
|
||||
r#type.const_zero().as_basic_value_enum()
|
||||
};
|
||||
context.build_store(pointer, value)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (index, binding) in self.bindings.iter().enumerate() {
|
||||
let yul_type = binding
|
||||
.r#type
|
||||
.to_owned()
|
||||
.unwrap_or_default()
|
||||
.into_llvm(context);
|
||||
let pointer = context.build_alloca(
|
||||
yul_type.as_basic_type_enum(),
|
||||
format!("binding_{index}_pointer").as_str(),
|
||||
);
|
||||
context.build_store(pointer, yul_type.const_zero())?;
|
||||
context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.insert_stack_pointer(binding.inner.to_owned(), pointer);
|
||||
}
|
||||
|
||||
let expression = match self.expression.take() {
|
||||
Some(expression) => expression,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let location = expression.location();
|
||||
let expression = match expression.into_llvm(context)? {
|
||||
Some(expression) => expression,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let llvm_type = context.structure_type(
|
||||
self.bindings
|
||||
.iter()
|
||||
.map(|binding| {
|
||||
binding
|
||||
.r#type
|
||||
.to_owned()
|
||||
.unwrap_or_default()
|
||||
.into_llvm(context)
|
||||
.as_basic_type_enum()
|
||||
})
|
||||
.collect::<Vec<inkwell::types::BasicTypeEnum<'ctx>>>()
|
||||
.as_slice(),
|
||||
);
|
||||
if expression.value.get_type() != llvm_type.as_basic_type_enum() {
|
||||
anyhow::bail!(
|
||||
"{} Assignment to {:?} received an invalid number of arguments",
|
||||
location,
|
||||
self.bindings
|
||||
);
|
||||
}
|
||||
let pointer = context.build_alloca(llvm_type, "bindings_pointer");
|
||||
context.build_store(pointer, expression.to_llvm())?;
|
||||
|
||||
for (index, binding) in self.bindings.into_iter().enumerate() {
|
||||
let pointer = context.build_gep(
|
||||
pointer,
|
||||
&[
|
||||
context.field_const(0),
|
||||
context
|
||||
.integer_type(era_compiler_common::BIT_LENGTH_X32)
|
||||
.const_int(index as u64, false),
|
||||
],
|
||||
binding.r#type.unwrap_or_default().into_llvm(context),
|
||||
format!("binding_{index}_gep_pointer").as_str(),
|
||||
);
|
||||
|
||||
let value = context.build_load(pointer, format!("binding_{index}_value").as_str())?;
|
||||
let pointer = context
|
||||
.current_function()
|
||||
.borrow_mut()
|
||||
.get_stack_pointer(binding.inner.as_str())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} Assignment to an undeclared variable `{}`",
|
||||
binding.location,
|
||||
binding.inner
|
||||
)
|
||||
})?;
|
||||
context.build_store(pointer, value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::yul::lexer::token::location::Location;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
#[test]
|
||||
fn error_reserved_identifier() {
|
||||
let input = r#"
|
||||
object "Test" {
|
||||
code {
|
||||
{
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
object "Test_deployed" {
|
||||
code {
|
||||
{
|
||||
let basefee := 42
|
||||
return(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut lexer = Lexer::new(input.to_owned());
|
||||
let result = Object::parse(&mut lexer, None);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::ReservedIdentifier {
|
||||
location: Location::new(11, 21),
|
||||
identifier: "basefee".to_owned()
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//!
|
||||
//! The YUL source code type.
|
||||
//!
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::yul::error::Error;
|
||||
use crate::yul::lexer::token::lexeme::keyword::Keyword;
|
||||
use crate::yul::lexer::token::lexeme::Lexeme;
|
||||
use crate::yul::lexer::token::Token;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::error::Error as ParserError;
|
||||
|
||||
///
|
||||
/// The YUL source code type.
|
||||
///
|
||||
/// The type is not currently in use, so all values have the `uint256` type by default.
|
||||
///
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
/// The `bool` type.
|
||||
Bool,
|
||||
/// The `int{N}` type.
|
||||
Int(usize),
|
||||
/// The `uint{N}` type.
|
||||
UInt(usize),
|
||||
/// The custom user-defined type.
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Default for Type {
|
||||
fn default() -> Self {
|
||||
Self::UInt(era_compiler_common::BIT_LENGTH_FIELD)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
///
|
||||
/// The element parser.
|
||||
///
|
||||
pub fn parse(lexer: &mut Lexer, initial: Option<Token>) -> Result<Self, Error> {
|
||||
let token = crate::yul::parser::take_or_next(initial, lexer)?;
|
||||
|
||||
match token {
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Bool),
|
||||
..
|
||||
} => Ok(Self::Bool),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Int(bitlength)),
|
||||
..
|
||||
} => Ok(Self::Int(bitlength)),
|
||||
Token {
|
||||
lexeme: Lexeme::Keyword(Keyword::Uint(bitlength)),
|
||||
..
|
||||
} => Ok(Self::UInt(bitlength)),
|
||||
Token {
|
||||
lexeme: Lexeme::Identifier(identifier),
|
||||
..
|
||||
} => Ok(Self::Custom(identifier.inner)),
|
||||
token => Err(ParserError::InvalidToken {
|
||||
location: token.location,
|
||||
expected: vec!["{type}"],
|
||||
found: token.lexeme.to_string(),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts the type into its LLVM.
|
||||
///
|
||||
pub fn into_llvm<'ctx, D>(
|
||||
self,
|
||||
context: &era_compiler_llvm_context::EraVMContext<'ctx, D>,
|
||||
) -> inkwell::types::IntType<'ctx>
|
||||
where
|
||||
D: era_compiler_llvm_context::EraVMDependency + Clone,
|
||||
{
|
||||
match self {
|
||||
Self::Bool => context.integer_type(era_compiler_common::BIT_LENGTH_BOOLEAN),
|
||||
Self::Int(bitlength) => context.integer_type(bitlength),
|
||||
Self::UInt(bitlength) => context.integer_type(bitlength),
|
||||
Self::Custom(_) => context.field_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user