1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
use crate::{error::*, ConstantPool, Node};
use koto_lexer::Span;
/// The index type used by nodes in the [Ast]
pub type AstIndex = u32;
/// A [Node] in the [Ast], along with its corresponding [Span]
#[derive(Clone, Debug, Default)]
pub struct AstNode {
/// The node itself
pub node: Node,
/// The index of the node's corresponding [Span]
///
/// The span is stored in the [Ast], and retrieved via [Ast::span].
pub span: AstIndex,
}
/// A Koto program represented as an Abstract Syntax Tree
///
/// This is produced by the parser, and consumed by the compiler.
#[derive(Debug, Default)]
pub struct Ast {
nodes: Vec<AstNode>,
spans: Vec<Span>,
constants: ConstantPool,
}
impl Ast {
/// Initializes an Ast with the given capacity
pub fn with_capacity(capacity: usize) -> Self {
Self {
nodes: Vec::with_capacity(capacity),
spans: Vec::with_capacity(capacity),
constants: ConstantPool::default(),
}
}
/// Pushes a node and corresponding span onto the tree
pub fn push(&mut self, node: Node, span: Span) -> Result<AstIndex> {
// We could potentially achieve some compression by
// using a set for the spans, for now a Vec will do.
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))
}
/// Returns a node for a given node index
pub fn node(&self, index: AstIndex) -> &AstNode {
&self.nodes[index as usize]
}
/// Returns a span for a given span index
pub fn span(&self, index: AstIndex) -> &Span {
&self.spans[index as usize]
}
/// Returns the constant pool referred to by the AST
pub fn constants(&self) -> &ConstantPool {
&self.constants
}
/// Moves the constants out of the AST
///
/// This is used when building a `Chunk` after compilation.
/// The constants get transferred to the `Chunk` once the AST has been converted into bytecode.
pub fn consume_constants(self) -> ConstantPool {
self.constants
}
pub(crate) fn set_constants(&mut self, constants: ConstantPool) {
self.constants = constants
}
/// Returns the root node in the tree
pub fn entry_point(&self) -> Option<AstIndex> {
if self.nodes.is_empty() {
None
} else {
Some((self.nodes.len() - 1) as AstIndex)
}
}
/// Used in testing to validate the tree's contents
pub fn nodes(&self) -> &[AstNode] {
&self.nodes
}
}