mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 05:38:00 +00:00
52bf16a383
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
296 lines
9.5 KiB
Rust
296 lines
9.5 KiB
Rust
//! The source code block.
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
use inkwell::debug_info::AsDIScope;
|
|
|
|
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> revive_llvm_context::PolkaVMWriteLLVM<D> for Block
|
|
where
|
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
|
{
|
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<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(), Some(self.location.line))?;
|
|
|
|
if let Some(debug_info) = context.debug_info() {
|
|
let di_builder = debug_info.builder();
|
|
let di_scope = debug_info.top_scope().expect("expected a debug-info scope");
|
|
let di_block_scope = di_builder
|
|
.create_lexical_block(
|
|
di_scope,
|
|
debug_info.compilation_unit().get_file(),
|
|
self.location.line,
|
|
0,
|
|
)
|
|
.as_debug_info_scope();
|
|
context.push_debug_scope(di_block_scope);
|
|
context.set_debug_location(self.location.line, 0, None)?;
|
|
}
|
|
|
|
context.set_basic_block(current_block);
|
|
for statement in local_statements.into_iter() {
|
|
context.set_debug_location(statement.location().line, 0, None)?;
|
|
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, &mut None)?;
|
|
}
|
|
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
|
|
),
|
|
}
|
|
}
|
|
|
|
context.pop_debug_scope();
|
|
|
|
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())
|
|
);
|
|
}
|
|
}
|