feat(semantic): add semantic builder with untyped ast tree creation

This commit is contained in:
Boshen 2023-02-25 10:20:49 +08:00
parent 0de5020d07
commit 5f7a756229
10 changed files with 234 additions and 0 deletions

14
Cargo.lock generated
View file

@ -493,6 +493,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "indextree"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497f036ac2fae75c34224648a77802e5dd4e9cfb56f4713ab6b12b7160a0523b"
[[package]]
name = "io-lifetimes"
version = "1.0.5"
@ -838,6 +844,14 @@ dependencies = [
"oxc_parser",
]
[[package]]
name = "oxc_semantic"
version = "0.0.0"
dependencies = [
"indextree",
"oxc_ast",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"

View file

@ -27,6 +27,7 @@ serde_json = "1.0.93"
thiserror = "1.0.38"
jemallocator = "0.5.0"
clap = "4.1.4"
indextree = "4.5.0"
[profile.release]
lto = "fat"

View file

@ -15,6 +15,7 @@ pub mod span;
pub mod syntax_directed_operations;
pub mod visit;
pub use ast_kind::AstKind;
pub use num_bigint::BigUint;
pub use crate::ast_builder::*;

View file

@ -0,0 +1,15 @@
[package]
name = "oxc_semantic"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true
[dependencies]
oxc_ast = { path = "../oxc_ast" }
indextree = { workspace = true }

View file

@ -0,0 +1,10 @@
[package]
name = "oxc_printer"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true

View file

@ -0,0 +1,56 @@
//! Semantic Builder
//! This builds:
//! * The untyped and flattened ast nodes into an indextree
use oxc_ast::{ast::Program, visit::Visit, AstKind};
use crate::{
node::{AstNodeId, AstNodes, SemanticNode},
Semantic,
};
#[derive(Debug)]
pub struct SemanticBuilder<'a> {
nodes: AstNodes<'a>,
current_node_id: AstNodeId,
}
impl<'a> SemanticBuilder<'a> {
#[must_use]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let mut nodes = AstNodes::default();
let semantic_node = SemanticNode::new(AstKind::Root);
let current_node_id = nodes.new_node(semantic_node).into();
Self { nodes, current_node_id }
}
#[must_use]
pub fn build(mut self, program: &'a Program<'a>) -> Semantic<'a> {
// AST pass
self.visit_program(program);
Semantic { nodes: self.nodes }
}
fn create_ast_node(&mut self, kind: AstKind<'a>) {
let ast_node = SemanticNode::new(kind);
let node_id = self.nodes.new_node(ast_node);
self.current_node_id.append(node_id, &mut self.nodes);
self.current_node_id = node_id.into();
}
fn pop_ast_node(&mut self) {
self.current_node_id =
self.nodes[self.current_node_id.indextree_id()].parent().unwrap().into();
}
}
impl<'a> Visit<'a> for SemanticBuilder<'a> {
fn enter_node(&mut self, kind: AstKind<'a>) {
self.create_ast_node(kind);
}
fn leave_node(&mut self, _kind: AstKind<'a>) {
self.pop_ast_node();
}
}

View file

@ -0,0 +1,16 @@
mod builder;
mod node;
pub use builder::SemanticBuilder;
use node::AstNodes;
pub struct Semantic<'a> {
nodes: AstNodes<'a>,
}
impl<'a> Semantic<'a> {
#[must_use]
pub const fn nodes(&self) -> &AstNodes<'a> {
&self.nodes
}
}

View file

@ -0,0 +1,38 @@
use std::ops::Deref;
use indextree::NodeId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AstNodeId(NodeId);
impl AstNodeId {
#[must_use]
pub const fn new(node_id: NodeId) -> Self {
Self(node_id)
}
#[must_use]
pub const fn indextree_id(&self) -> NodeId {
self.0
}
}
impl From<NodeId> for AstNodeId {
fn from(node_id: NodeId) -> Self {
Self(node_id)
}
}
impl From<AstNodeId> for NodeId {
fn from(ast_node_id: AstNodeId) -> Self {
ast_node_id.indextree_id()
}
}
impl Deref for AstNodeId {
type Target = NodeId;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View file

@ -0,0 +1,27 @@
mod id;
mod tree;
pub use id::AstNodeId;
use oxc_ast::AstKind;
pub use tree::AstNodes;
/// Indextree node containing a semantic node
pub type AstNode<'a> = indextree::Node<SemanticNode<'a>>;
/// Semantic node contains all the semantic information about an ast node.
#[derive(Debug, Clone, Copy)]
pub struct SemanticNode<'a> {
/// A pointer to the ast node, which resides in the `bumpalo` memory arena.
kind: AstKind<'a>,
}
impl<'a> SemanticNode<'a> {
#[must_use]
pub const fn new(kind: AstKind<'a>) -> Self {
Self { kind }
}
pub const fn kind(&self) -> AstKind<'a> {
self.kind
}
}

View file

@ -0,0 +1,56 @@
use std::ops::{Deref, DerefMut, Index, IndexMut};
use indextree::{Arena, NodeId};
use super::{AstNode, AstNodeId, SemanticNode};
/// Untyped AST nodes flattened into an indextree
#[derive(Debug, Default)]
pub struct AstNodes<'a> {
/// The memory storage of the indextree is backed by a vector,
/// which allows for efficient traversal.
/// This also allows for parallel traversal by using `rayon`.
nodes: Arena<SemanticNode<'a>>,
}
impl<'a> Index<NodeId> for AstNodes<'a> {
type Output = AstNode<'a>;
fn index(&self, id: NodeId) -> &Self::Output {
&self.nodes[id]
}
}
impl<'a> IndexMut<NodeId> for AstNodes<'a> {
fn index_mut(&mut self, id: NodeId) -> &mut AstNode<'a> {
&mut self.nodes[id]
}
}
impl<'a> Index<AstNodeId> for AstNodes<'a> {
type Output = SemanticNode<'a>;
fn index(&self, id: AstNodeId) -> &Self::Output {
self.nodes[id.indextree_id()].get()
}
}
impl<'a> IndexMut<AstNodeId> for AstNodes<'a> {
fn index_mut(&mut self, id: AstNodeId) -> &mut SemanticNode<'a> {
self.nodes[id.indextree_id()].get_mut()
}
}
impl<'a> Deref for AstNodes<'a> {
type Target = Arena<SemanticNode<'a>>;
fn deref(&self) -> &Self::Target {
&self.nodes
}
}
impl<'a> DerefMut for AstNodes<'a> {
fn deref_mut(&mut self) -> &mut Arena<SemanticNode<'a>> {
&mut self.nodes
}
}