mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-25 21:07:56 +00:00
431b5a2ce5
- Lazily load function arguments so that they can be passed as pointers. - Lazily call the immutable store function to avoid storing zero sized immutable data. --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
220 lines
6.4 KiB
Rust
220 lines
6.4 KiB
Rust
//! 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> revive_llvm_context::PolkaVMWriteLLVM<D> for Switch
|
|
where
|
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
|
{
|
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<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)?.access(context)?;
|
|
|
|
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")
|
|
.access(context)?
|
|
.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())
|
|
);
|
|
}
|
|
}
|