use crate::ast::{self, kw};
use crate::parser::{Lookahead1, Parse, Parser, Peek, Result};
#[derive(Debug)]
pub struct Memory<'a> {
pub span: ast::Span,
pub id: Option<ast::Id<'a>>,
pub exports: ast::InlineExport<'a>,
pub kind: MemoryKind<'a>,
}
#[derive(Debug)]
pub enum MemoryKind<'a> {
#[allow(missing_docs)]
Import {
import: ast::InlineImport<'a>,
ty: ast::MemoryType,
},
Normal(ast::MemoryType),
Inline {
is_32: bool,
data: Vec<DataVal<'a>>,
},
}
impl<'a> Parse<'a> for Memory<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let span = parser.parse::<kw::memory>()?.0;
let id = parser.parse()?;
let exports = parser.parse()?;
let mut l = parser.lookahead1();
let kind = if let Some(import) = parser.parse()? {
MemoryKind::Import {
import,
ty: parser.parse()?,
}
} else if l.peek::<ast::LParen>() || parser.peek2::<ast::LParen>() {
let is_32 = if parser.parse::<Option<kw::i32>>()?.is_some() {
true
} else if parser.parse::<Option<kw::i64>>()?.is_some() {
false
} else {
true
};
let data = parser.parens(|parser| {
parser.parse::<kw::data>()?;
let mut data = Vec::new();
while !parser.is_empty() {
data.push(parser.parse()?);
}
Ok(data)
})?;
MemoryKind::Inline { data, is_32 }
} else if l.peek::<u32>() || l.peek::<kw::i32>() || l.peek::<kw::i64>() {
MemoryKind::Normal(parser.parse()?)
} else {
return Err(l.error());
};
Ok(Memory {
span,
id,
exports,
kind,
})
}
}
#[derive(Debug)]
pub struct Data<'a> {
pub span: ast::Span,
pub id: Option<ast::Id<'a>>,
pub kind: DataKind<'a>,
pub data: Vec<DataVal<'a>>,
}
#[derive(Debug)]
pub enum DataKind<'a> {
Passive,
Active {
memory: ast::ItemRef<'a, kw::memory>,
offset: ast::Expression<'a>,
},
}
impl<'a> Parse<'a> for Data<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let span = parser.parse::<kw::data>()?.0;
let id = parser.parse()?;
let kind = if parser.peek::<kw::passive>() {
parser.parse::<kw::passive>()?;
DataKind::Passive
} else if parser.peek::<&[u8]>() {
DataKind::Passive
} else {
let memory = if let Some(index) = parser.parse::<Option<ast::IndexOrRef<_>>>()? {
index.0
} else {
ast::ItemRef::Item {
kind: kw::memory(parser.prev_span()),
idx: ast::Index::Num(0, span),
exports: Vec::new(),
}
};
let offset = parser.parens(|parser| {
if parser.peek::<kw::offset>() {
parser.parse::<kw::offset>()?;
}
parser.parse()
})?;
DataKind::Active { memory, offset }
};
let mut data = Vec::new();
while !parser.is_empty() {
data.push(parser.parse()?);
}
Ok(Data {
span,
id,
kind,
data,
})
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub enum DataVal<'a> {
String(&'a [u8]),
Integral(Vec<u8>),
}
impl DataVal<'_> {
pub fn len(&self) -> usize {
match self {
DataVal::String(s) => s.len(),
DataVal::Integral(s) => s.len(),
}
}
pub fn push_onto(&self, dst: &mut Vec<u8>) {
match self {
DataVal::String(s) => dst.extend_from_slice(s),
DataVal::Integral(s) => dst.extend_from_slice(s),
}
}
}
impl<'a> Parse<'a> for DataVal<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
if !parser.peek::<ast::LParen>() {
return Ok(DataVal::String(parser.parse()?));
}
return parser.parens(|p| {
let mut result = Vec::new();
let mut lookahead = p.lookahead1();
let l = &mut lookahead;
let r = &mut result;
if consume::<kw::i8, i8, _>(p, l, r, |u, v| v.push(u as u8))?
|| consume::<kw::i16, i16, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::i32, i32, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::i64, i64, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
|| consume::<kw::f32, ast::Float32, _>(p, l, r, |u, v| {
v.extend(&u.bits.to_le_bytes())
})?
|| consume::<kw::f64, ast::Float64, _>(p, l, r, |u, v| {
v.extend(&u.bits.to_le_bytes())
})?
|| consume::<kw::v128, ast::V128Const, _>(p, l, r, |u, v| {
v.extend(&u.to_le_bytes())
})?
{
Ok(DataVal::Integral(result))
} else {
Err(lookahead.error())
}
});
fn consume<'a, T: Peek + Parse<'a>, U: Parse<'a>, F>(
parser: Parser<'a>,
lookahead: &mut Lookahead1<'a>,
dst: &mut Vec<u8>,
push: F,
) -> Result<bool>
where
F: Fn(U, &mut Vec<u8>),
{
if !lookahead.peek::<T>() {
return Ok(false);
}
parser.parse::<T>()?;
while !parser.is_empty() {
let val = parser.parse::<U>()?;
push(val, dst);
}
Ok(true)
}
}
}