use koto_lexer::Span;
use std::{fmt, num::TryFromIntError};
use crate::{ConstantPool, Node, error::*};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct AstIndex(u32);
impl From<AstIndex> for u32 {
fn from(value: AstIndex) -> Self {
value.0
}
}
impl From<AstIndex> for usize {
fn from(value: AstIndex) -> Self {
value.0 as usize
}
}
impl From<u32> for AstIndex {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<&u32> for AstIndex {
fn from(value: &u32) -> Self {
Self(*value)
}
}
impl TryFrom<usize> for AstIndex {
type Error = TryFromIntError;
fn try_from(value: usize) -> std::result::Result<Self, Self::Error> {
Ok(Self(u32::try_from(value)?))
}
}
impl fmt::Display for AstIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Debug, Default)]
pub struct AstNode {
pub node: Node,
pub span: AstIndex,
}
#[derive(Debug, Default, Clone)]
pub struct Ast {
nodes: Vec<AstNode>,
spans: Vec<Span>,
constants: ConstantPool,
}
impl Ast {
pub fn with_capacity(capacity: usize) -> Self {
Self {
nodes: Vec::with_capacity(capacity),
spans: Vec::with_capacity(capacity),
constants: ConstantPool::default(),
}
}
pub fn push(&mut self, node: Node, span: Span) -> Result<AstIndex> {
self.spans.push(span);
let span_index = AstIndex::try_from(self.spans.len() - 1)
.map_err(|_| Error::new(InternalError::AstCapacityOverflow.into(), span))?;
self.nodes.push(AstNode {
node,
span: span_index,
});
AstIndex::try_from(self.nodes.len() - 1)
.map_err(|_| Error::new(InternalError::AstCapacityOverflow.into(), span))
}
pub fn node(&self, index: AstIndex) -> &AstNode {
&self.nodes[usize::from(index)]
}
pub fn span(&self, index: AstIndex) -> &Span {
&self.spans[usize::from(index)]
}
pub fn constants(&self) -> &ConstantPool {
&self.constants
}
pub fn consume_constants(self) -> ConstantPool {
self.constants
}
pub(crate) fn set_constants(&mut self, constants: ConstantPool) {
self.constants = constants
}
pub fn entry_point(&self) -> Option<AstIndex> {
if self.nodes.is_empty() {
None
} else {
AstIndex::try_from(self.nodes.len() - 1).ok()
}
}
pub fn nodes(&self) -> &[AstNode] {
&self.nodes
}
}