use crate::kw;
use crate::parser::{Cursor, Parse, Parser, Peek, Result};
use crate::token::Index;
pub(crate) fn allow_legacy_indices() -> bool {
const DEFAULT: bool = true;
static ALLOW: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
match std::env::var("WAST_STRICT_COMPONENT_INDICES").as_deref() {
Ok("0") => true,
Ok(_) => false,
Err(_) => DEFAULT,
}
});
*ALLOW
}
fn peek<K: Peek>(cursor: Cursor) -> Result<bool> {
if !K::peek(cursor)? {
return Ok(false);
}
let cursor = match cursor.keyword()? {
Some((_, c)) => c,
_ => return Ok(false),
};
let cursor = match cursor.id()? {
Some((_, cursor)) => Some(cursor),
None => cursor.integer()?.map(|p| p.1),
};
Ok(match cursor {
Some(cursor) => cursor.rparen()?.is_some() || cursor.string()?.is_some(),
None => false,
})
}
#[derive(Clone, Debug)]
pub struct CoreItemRef<'a, K> {
pub kind: K,
pub idx: Index<'a>,
pub export_name: Option<&'a str>,
}
impl<'a, K: Parse<'a>> Parse<'a> for CoreItemRef<'a, K> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let kind = parser.parse::<K>()?;
let idx = parser.parse()?;
let export_name = parser.parse()?;
Ok(Self {
kind,
idx,
export_name,
})
}
}
impl<'a, K: Peek> Peek for CoreItemRef<'a, K> {
fn peek(cursor: Cursor<'_>) -> Result<bool> {
peek::<K>(cursor)
}
fn display() -> &'static str {
"a core item reference"
}
}
#[derive(Clone, Debug)]
pub struct ItemRef<'a, K> {
pub kind: K,
pub idx: Index<'a>,
pub export_names: Vec<&'a str>,
}
impl<'a, K: Parse<'a>> Parse<'a> for ItemRef<'a, K> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let kind = parser.parse::<K>()?;
let idx = parser.parse()?;
let mut export_names = Vec::new();
while !parser.is_empty() {
export_names.push(parser.parse()?);
}
Ok(Self {
kind,
idx,
export_names,
})
}
}
impl<'a, K: Peek> Peek for ItemRef<'a, K> {
fn peek(cursor: Cursor<'_>) -> Result<bool> {
peek::<K>(cursor)
}
fn display() -> &'static str {
"a component item reference"
}
}
#[derive(Clone, Debug)]
pub struct IndexOrRef<'a, K>(pub ItemRef<'a, K>);
impl<'a, K> Parse<'a> for IndexOrRef<'a, K>
where
K: Parse<'a> + Default,
{
fn parse(parser: Parser<'a>) -> Result<Self> {
if parser.peek::<Index<'_>>()? {
Ok(IndexOrRef(ItemRef {
kind: K::default(),
idx: parser.parse()?,
export_names: Vec::new(),
}))
} else {
Ok(IndexOrRef(parser.parens(|p| p.parse())?))
}
}
}
#[derive(Clone, Debug)]
pub struct CorePrefixedRef<'a, K, const LEGACY: bool>(pub CoreItemRef<'a, K>);
impl<'a, K, const LEGACY: bool> Parse<'a> for CorePrefixedRef<'a, K, LEGACY>
where
K: Parse<'a> + Peek + Default,
{
fn parse(parser: Parser<'a>) -> Result<Self> {
if parser.peek::<Index<'_>>()? {
return Ok(CorePrefixedRef(CoreItemRef {
kind: K::default(),
idx: parser.parse()?,
export_name: None,
}));
}
parser.parens(|parser| {
if LEGACY && parser.peek::<K>()? {
if !allow_legacy_indices() {
let name = K::display().trim_matches('`');
return Err(parser.error(format!(
"the `core` keyword is required in this reference: \
`({name} ...)` should be written `(core {name} ...)` \
(or set WAST_STRICT_COMPONENT_INDICES=0 to accept \
the legacy syntax)"
)));
}
} else {
parser.parse::<kw::core>()?;
}
let item = parser.parse::<CoreItemRef<'a, K>>()?;
Ok(CorePrefixedRef(item))
})
}
}
pub(crate) fn parse_core_prefixed_contents<'a, K>(
parser: Parser<'a>,
kind: K,
) -> Result<CoreItemRef<'a, K>>
where
K: Parse<'a> + Peek + Default,
{
if parser.peek::<Index<'_>>()? {
let idx = parser.parse()?;
let export_name = if parser.peek::<&str>()? {
if !allow_legacy_indices() {
let name = K::display().trim_matches('`');
return Err(parser.error(format!(
"an export name must be written inside a nested \
reference: `({name} $i \"name\")` should be written \
`({name} (core {name} $i \"name\"))` \
(or set WAST_STRICT_COMPONENT_INDICES=0 to accept \
the legacy syntax)"
)));
}
Some(parser.parse()?)
} else {
None
};
return Ok(CoreItemRef {
kind,
idx,
export_name,
});
}
parser.parens(|parser| {
parser.parse::<kw::core>()?;
parser.parse::<CoreItemRef<'a, K>>()
})
}