use crate::{Error, Result};
use std::ops::Range;
use wasmparser::BlockType;
#[derive(Debug, Default)]
pub struct Ast {
root: usize,
nodes: Vec<Node>,
ifs: Vec<usize>,
loops: Vec<usize>,
}
impl Ast {
pub fn has_if(&self) -> bool {
!self.ifs.is_empty()
}
pub fn get_ifs(&self) -> &[usize] {
&self.ifs
}
pub fn get_loops(&self) -> &[usize] {
&self.loops
}
pub fn get_root(&self) -> usize {
self.root
}
pub fn get_nodes(&self) -> Vec<Node> {
self.nodes.clone()
}
}
#[derive(Debug, Clone)]
pub enum Node {
IfElse {
consequent: Vec<usize>,
alternative: Option<Vec<usize>>,
ty: BlockType,
},
Code {
range: Range<usize>,
},
Loop {
body: Vec<usize>,
ty: BlockType,
range: Range<usize>,
},
Block {
body: Vec<usize>,
ty: BlockType,
},
Root(Vec<usize>),
}
#[derive(Debug)]
pub(crate) enum State {
If,
Else,
Loop,
Block,
Root,
}
#[derive(Debug)]
pub(crate) struct ParseContext {
current_parsing: Vec<usize>,
stack: Vec<Vec<usize>>,
frames: Vec<(State, Option<BlockType>, usize)>,
current_code_range: Range<usize>,
nodes: Vec<Node>,
ifs: Vec<usize>,
loops: Vec<usize>,
blocks: Vec<usize>,
}
impl Default for ParseContext {
fn default() -> Self {
ParseContext {
current_code_range: 0..0,
current_parsing: Vec::new(),
stack: Vec::new(),
frames: Vec::new(),
nodes: Vec::new(),
ifs: Vec::new(),
loops: Vec::new(),
blocks: Vec::new(),
}
}
}
impl ParseContext {
pub fn push_state(&mut self) {
self.stack.push(self.current_parsing.clone());
self.current_parsing = vec![]
}
pub fn pop_state(&mut self) -> Result<Vec<usize>> {
match self.stack.pop() {
Some(new_state) => {
self.current_parsing = new_state.clone();
Ok(new_state)
}
None => Err(Error::other("`pop_state` on an empty stack")),
}
}
pub fn push_node_to_current_parsing(&mut self, node: Node) -> usize {
let id = self.nodes.len();
self.nodes.push(node.clone());
self.current_parsing.push(id);
match node {
Node::IfElse {
consequent: _,
alternative: _,
ty: _,
} => self.ifs.push(id),
Node::Loop { .. } => self.loops.push(id),
Node::Block { .. } => self.blocks.push(id),
_ => {}
}
id
}
pub fn push_frame(&mut self, state: State, ty: Option<BlockType>, idx: usize) {
self.frames.push((state, ty, idx))
}
pub fn pop_frame(&mut self) -> Result<(State, Option<BlockType>, usize)> {
match self.frames.pop() {
Some(e) => Ok(e),
None => Err(Error::other("`pop_frame` on an empty frame stack")),
}
}
pub fn get_current_parsing(&self) -> Vec<usize> {
self.current_parsing.clone()
}
pub fn current_code_is_empty(&self) -> bool {
self.current_code_range.start == self.current_code_range.end
}
pub fn push_current_code_as_node(&mut self) -> usize {
self.push_node_to_current_parsing(Node::Code {
range: self.current_code_range.clone(),
})
}
pub fn reset_code_range_at(&mut self, idx: usize) {
self.current_code_range = idx..idx;
}
pub fn append_instruction_to_current_code(&mut self) {
self.current_code_range.end += 1;
}
pub fn finish(mut self) -> Ast {
let roots = self.get_current_parsing();
let root_id = self.push_node_to_current_parsing(Node::Root(roots));
Ast {
root: root_id,
nodes: self.nodes,
ifs: self.ifs,
loops: self.loops,
}
}
}