use crate::TokenID;
use arena_terms::Term;
use parlex::{ParlexError, Span, Token};
#[derive(Debug, Clone, Copy, Default)]
pub enum Value {
#[default]
None,
Term(Term),
Index(usize),
}
macro_rules! impl_tryfrom_value {
( $( $Variant:ident => $ty:ty ),+ $(,)? ) => {
$(
impl ::core::convert::TryFrom<Value> for $ty {
type Error = ParlexError;
fn try_from(v: Value) -> Result<Self, ParlexError> {
match v {
Value::$Variant(x) => Ok(x),
other => Err(ParlexError {
message: format!("expected `Value::{}`, found {:?}", stringify!($Variant), other),
span: None,
}),
}
}
}
)+
};
}
impl_tryfrom_value! {
Term => Term,
Index => usize,
}
impl TryFrom<Value> for Option<Term> {
type Error = ParlexError;
fn try_from(v: Value) -> Result<Self, ParlexError> {
match v {
Value::None => Ok(None),
Value::Term(x) => Ok(Some(x)),
other => Err(ParlexError {
message: format!("expected `Value::Term` or `Value::None`, found {:?}", other),
span: None,
}),
}
}
}
#[derive(Debug, Clone)]
pub struct TermToken {
pub token_id: TokenID,
pub value: Value,
pub span: Option<Span>,
pub op_tab_index: Option<usize>,
}
impl TermToken {
#[must_use]
pub fn new(token_id: TokenID, value: Value, span: Option<Span>) -> Self {
Self {
token_id,
value,
span,
op_tab_index: None,
}
}
}
impl TermToken {
pub fn merge_span(&mut self, other_token: &TermToken) {
match other_token.span() {
Some(other_span) => match &mut self.span {
Some(span) => {
*span = span.merge(&other_span);
}
None => {
self.span = Some(other_span);
}
},
None => (),
}
}
}
impl Token for TermToken {
type TokenID = TokenID;
fn token_id(&self) -> Self::TokenID {
self.token_id
}
fn span(&self) -> Option<Span> {
self.span
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::convert::TryFrom;
#[test]
fn try_from_value_to_usize_success() {
let v = Value::Index(42);
let got = usize::try_from(v).expect("Index -> usize should succeed");
assert_eq!(got, 42);
}
#[test]
fn try_from_value_to_usize_wrong_variant_err() {
let v = Value::None;
let err = usize::try_from(v).expect_err("None -> usize must error");
let msg = format!("{err:#}");
assert!(msg.contains("expected `Value::Index`"), "msg={msg}");
assert!(msg.contains("None"), "msg={msg}");
}
#[test]
fn try_from_value_to_option_term_none_success() {
let v = Value::None;
let got = <Option<Term>>::try_from(v).expect("None -> Option<Term> should succeed");
assert!(got.is_none());
}
#[test]
fn try_from_value_to_option_term_wrong_variant_err() {
let v = Value::Index(7);
let err = <Option<Term>>::try_from(v).expect_err("Index -> Option<Term> must error");
let msg = format!("{err:#}");
dbg!(&msg);
assert!(
msg.contains("expected `Value::Term` or `Value::None`"),
"msg={msg}"
);
}
#[test]
fn roundtrip_index_via_value() {
let input = 123usize;
let got = usize::try_from(Value::Index(input)).unwrap();
assert_eq!(got, input);
}
}