Skip to content

Commit

Permalink
refac: extract parser
Browse files Browse the repository at this point in the history
  • Loading branch information
sayon committed Aug 26, 2024
1 parent 04e5795 commit cefb4ba
Show file tree
Hide file tree
Showing 30 changed files with 490 additions and 371 deletions.
3 changes: 2 additions & 1 deletion era-compiler-solidity/src/project/contract/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::collections::HashSet;

use crate::evmla::assembly::Assembly;
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
use crate::yul::parser::dialect::llvm::LLVMDialect;
use crate::yul::parser::statement::object::Object;

use self::eravm_assembly::EraVMAssembly;
Expand All @@ -37,7 +38,7 @@ impl IR {
///
/// A shortcut constructor.
///
pub fn new_yul(object: Object) -> Self {
pub fn new_yul(object: Object<LLVMDialect>) -> Self {
Self::Yul(Yul::new(object))
}

Expand Down
7 changes: 4 additions & 3 deletions era-compiler-solidity/src/project/contract/ir/yul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use std::collections::HashSet;

use crate::yul::parser::dialect::llvm::LLVMDialect;
use crate::yul::parser::statement::object::Object;

///
Expand All @@ -12,21 +13,21 @@ use crate::yul::parser::statement::object::Object;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Yul {
/// The Yul AST object.
pub object: Object,
pub object: Object<LLVMDialect>,
}

impl Yul {
///
/// A shortcut constructor.
///
pub fn new(object: Object) -> Self {
pub fn new(object: Object<LLVMDialect>) -> Self {
Self { object }
}

///
/// Extracts the runtime code from the Yul object.
///
pub fn take_runtime_code(&mut self) -> Option<Object> {
pub fn take_runtime_code(&mut self) -> Option<Object<LLVMDialect>> {
self.object.inner_object.take().map(|object| *object)
}

Expand Down
140 changes: 140 additions & 0 deletions era-compiler-solidity/src/yul/parser/dialect/llvm/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//!
//! Parser of LLVM attributes encoded in the function identifier.
//!

use std::collections::BTreeSet;

use crate::yul::error::Error as YulError;
use crate::yul::parser::error::Error as ParserError;
use crate::yul::parser::identifier::Identifier;

/// The LLVM attribute section prefix.
pub const LLVM_ATTRIBUTE_PREFIX: &str = "$llvm_";

/// The LLVM attribute section suffix.
pub const LLVM_ATTRIBUTE_SUFFIX: &str = "_llvm$";

///
/// Gets the list of LLVM attributes provided in the function name.
///
pub(crate) fn get_llvm_attributes(
identifier: &Identifier,
) -> Result<BTreeSet<era_compiler_llvm_context::Attribute>, YulError> {
let mut valid_attributes = BTreeSet::new();

let llvm_begin = identifier.inner.find(LLVM_ATTRIBUTE_PREFIX);
let llvm_end = identifier.inner.find(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 + 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::Attribute::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)
}

#[cfg(test)]
mod tests {
use std::collections::BTreeSet;

use crate::yul::error::Error as YulError;
use crate::yul::lexer::token::location::Location;
use crate::yul::parser::error::Error as ParserError;
use crate::yul::parser::identifier::Identifier;

use super::get_llvm_attributes;

fn identifier_of(name: &str) -> Identifier {
Identifier {
location: Location { line: 0, column: 0 },
inner: name.to_string(),
r#type: None,
}
}

fn attribute_helper(s: &&str) -> era_compiler_llvm_context::Attribute {
era_compiler_llvm_context::Attribute::try_from(*s).expect(
"Internal error in test: trying to create an instance of `era_compiler_llvm_context::Attribute` from an invalid string representation.",
)
}
fn immediate_attributes(
representations: &[&str],
) -> BTreeSet<era_compiler_llvm_context::Attribute> {
representations.iter().map(attribute_helper).collect()
}

#[test]
fn parse_single_attribute() {
let input = r#"
$llvm_Hot_llvm$
"#;
let expected = immediate_attributes(&["Hot"]);
let result = get_llvm_attributes(&identifier_of(input)).unwrap_or_else(|_| {
panic!(
"LLVM attribute parser should be able to parse a valid input: \"{}\"",
input
)
});
assert_eq!(result, expected)
}

#[test]
fn parse_multiple_attributes() {
let input = r#"
$llvm_Hot_Cold_MinSize_llvm$
"#;
let expected = immediate_attributes(&["Cold", "Hot", "MinSize"]);
let result = get_llvm_attributes(&identifier_of(input)).unwrap_or_else(|_| {
panic!(
"LLVM attribute parser should be able to parse a valid input: \"{}\"",
input
)
});
assert_eq!(result, expected)
}
#[test]
fn parse_malformed_attributes() {
let input = r#"
$llvm____*&@_llvm$
"#;
get_llvm_attributes(&identifier_of(input)).expect_err(&format!(
"LLVM attributes parser should not parse attributes from the malformed input \"{}\"",
input
));
}

#[test]
fn parse_invalid_attributes() {
let input = r#"
$llvm_Hot_Cold_MinSize_BogusAttr_llvm$
"#;

let values = BTreeSet::from(["BogusAttr".into()]);
let location = Location { line: 0, column: 0 };
let expected = YulError::Parser(ParserError::InvalidAttributes { location, values });
let result = get_llvm_attributes(&identifier_of(input))
.expect_err("LLVM attributes parser should not mask unknown attributes");

assert_eq!(result, expected);
}
}
69 changes: 69 additions & 0 deletions era-compiler-solidity/src/yul/parser/dialect/llvm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//!
//! LLVM-specific part of the parser.
//!

pub mod attributes;

use std::collections::BTreeSet;

use crate::yul::error::Error;
use crate::yul::lexer::token::location::Location;
use crate::yul::lexer::Lexer;
use crate::yul::parser::error::Error as ParserError;
use crate::yul::parser::identifier::Identifier;

use self::attributes::get_llvm_attributes;

use super::Dialect;

/// LLVM-specific part of the parser.
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct LLVMDialect {}

impl Dialect for LLVMDialect {
type FunctionAttribute = era_compiler_llvm_context::Attribute;

fn extract_attributes(
identifier: &Identifier,
_: &mut Lexer,
) -> Result<BTreeSet<Self::FunctionAttribute>, crate::yul::error::Error> {
get_llvm_attributes(identifier)
}

fn sanitize_function(
identifier: &Identifier,
arguments: &mut Vec<Identifier>,
location: Location,
_lexer: &mut Lexer,
) -> Result<(), Error> {
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.clone(),
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.clone(),
expected: 0,
found: arguments.len(),
}
.into());
}
Ok(())
}
}
44 changes: 44 additions & 0 deletions era-compiler-solidity/src/yul/parser/dialect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//!
//! Describes a pragmatic, target-specific part of the parser.
//!

pub mod llvm;

use std::collections::BTreeSet;
use std::fmt::Debug;

use serde::Deserialize;
use serde::Serialize;

use crate::yul::error::Error;
use crate::yul::lexer::token::location::Location;
use crate::yul::lexer::Lexer;

use super::identifier::Identifier;

/// Describes a pragmatic, target-specific part of the parser.
pub trait Dialect: for<'de> Deserialize<'de> + Serialize + Eq + PartialEq + Clone + Debug {
/// Type of function attributes parsed from their identifiers.
type FunctionAttribute: for<'de> Deserialize<'de>
+ Debug
+ Clone
+ Eq
+ PartialEq
+ Serialize
+ Ord;

/// Extractor for the function attributes.
fn extract_attributes(
identifier: &Identifier,
lexer: &mut Lexer,
) -> Result<BTreeSet<Self::FunctionAttribute>, Error>;

/// Check the dialect-specific function invariants and potentially modify
/// their arguments list.
fn sanitize_function(
identifier: &Identifier,
arguments: &mut Vec<Identifier>,
location: Location,
lexer: &mut Lexer,
) -> Result<(), Error>;
}
1 change: 1 addition & 0 deletions era-compiler-solidity/src/yul/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! The YUL code block.
//!

pub mod dialect;
pub mod error;
pub mod identifier;
pub mod statement;
Expand Down
23 changes: 15 additions & 8 deletions era-compiler-solidity/src/yul/parser/statement/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ 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::dialect::llvm::LLVMDialect;
use crate::yul::parser::dialect::Dialect;
use crate::yul::parser::error::Error as ParserError;
use crate::yul::parser::statement::assignment::Assignment;
use crate::yul::parser::statement::expression::Expression;
Expand All @@ -20,15 +22,19 @@ use crate::yul::parser::statement::Statement;
///
/// The Yul source code block.
///
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Block {
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
#[serde(bound = "P: serde::de::DeserializeOwned")]
pub struct Block<P>
where
P: Dialect,
{
/// The location.
pub location: Location,
/// The block statements.
pub statements: Vec<Statement>,
pub statements: Vec<Statement<P>>,
}

impl Block {
impl<P: Dialect> Block<P> {
///
/// The element parser.
///
Expand Down Expand Up @@ -135,7 +141,7 @@ impl Block {
}
}

impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Block
impl<D> era_compiler_llvm_context::EraVMWriteLLVM<D> for Block<LLVMDialect>
where
D: era_compiler_llvm_context::Dependency,
{
Expand Down Expand Up @@ -207,7 +213,7 @@ where
}
}

impl<D> era_compiler_llvm_context::EVMWriteLLVM<D> for Block
impl<D> era_compiler_llvm_context::EVMWriteLLVM<D> for Block<LLVMDialect>
where
D: era_compiler_llvm_context::Dependency,
{
Expand Down Expand Up @@ -283,6 +289,7 @@ where
mod tests {
use crate::yul::lexer::token::location::Location;
use crate::yul::lexer::Lexer;
use crate::yul::parser::dialect::llvm::LLVMDialect;
use crate::yul::parser::error::Error;
use crate::yul::parser::statement::object::Object;

Expand All @@ -308,7 +315,7 @@ object "Test" {
"#;

let mut lexer = Lexer::new(input.to_owned());
let result = Object::parse(&mut lexer, None);
let result = Object::<LLVMDialect>::parse(&mut lexer, None);
assert_eq!(
result,
Err(Error::InvalidToken {
Expand Down Expand Up @@ -341,7 +348,7 @@ object "Test" {
"#;

let mut lexer = Lexer::new(input.to_owned());
let result = Object::parse(&mut lexer, None);
let result = Object::<LLVMDialect>::parse(&mut lexer, None);
assert_eq!(
result,
Err(Error::InvalidToken {
Expand Down
Loading

0 comments on commit cefb4ba

Please sign in to comment.