Skip to content

Commit

Permalink
feat(semantic): add scope tree to semantic analysis.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Mar 11, 2024
1 parent a3f05a1 commit 931e456
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 30 deletions.
8 changes: 2 additions & 6 deletions crates/fuse-ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fuse_common::Span;
use fuse_common::{ReferenceId, Span};
use fuse_common_proc::serializable;
use std::{cell::Cell, rc::Rc};

Expand Down Expand Up @@ -94,7 +94,7 @@ pub struct TypeAnnotation {
}

#[serializable]
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Atom(pub Rc<str>);

#[serializable]
Expand Down Expand Up @@ -188,10 +188,6 @@ pub struct Identifier {
pub reference: Cell<Option<ReferenceId>>,
}

#[serializable]
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct ReferenceId(u32);

#[serializable]
#[derive(Debug, PartialEq)]
pub struct Function {
Expand Down
56 changes: 40 additions & 16 deletions crates/fuse-ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@ macro_rules! visit_list {
};
}

macro_rules! visit_scope {
($visitor:ident => $block:block) => {
$visitor.enter_scope();
$block
$visitor.leave_scope();
};
}

pub trait Visitor<'ast>: Sized {
fn enter_scope(&mut self) {}

fn leave_scope(&mut self) {}

fn visit_chunk(&mut self, chunk: &'ast Chunk) {
walk_block(self, &chunk.body)
}
Expand Down Expand Up @@ -164,7 +176,9 @@ pub trait Visitor<'ast>: Sized {
}

pub fn walk_block<'ast, V: Visitor<'ast>>(visitor: &mut V, block: &'ast Block) {
visit_list!(visitor.visit_statement(&block.statements))
visit_scope!(visitor => {
visit_list!(visitor.visit_statement(&block.statements));
});
}

pub fn walk_statement<'ast, V: Visitor<'ast>>(visitor: &mut V, statement: &'ast Statement) {
Expand Down Expand Up @@ -216,8 +230,10 @@ pub fn walk_expression<'ast, V: Visitor<'ast>>(visitor: &mut V, expression: &'as
}

pub fn walk_function<'ast, V: Visitor<'ast>>(visitor: &mut V, func: &'ast Function) {
visit!(visitor.visit_function_signature(&func.signature));
visit!(visitor.visit_function_body(&func.body));
visit_scope!(visitor => {
visit!(visitor.visit_function_signature(&func.signature));
visit!(visitor.visit_function_body(&func.body));
});
}

pub fn walk_function_signature<'ast, V: Visitor<'ast>>(
Expand Down Expand Up @@ -260,8 +276,10 @@ pub fn walk_function_body<'ast, V: Visitor<'ast>>(visitor: &mut V, body: &'ast F
}

pub fn walk_enum_declaration<'ast, V: Visitor<'ast>>(visitor: &mut V, decl: &'ast EnumDeclaration) {
visit!(visitor.visit_identifier(&decl.identifier));
visit_list!(visitor.visit_enum_variant(&decl.variants));
visit_scope!(visitor => {
visit!(visitor.visit_identifier(&decl.identifier));
visit_list!(visitor.visit_enum_variant(&decl.variants));
});
}

pub fn walk_enum_variant<'ast, V: Visitor<'ast>>(visitor: &mut V, var: &'ast EnumVariant) {
Expand All @@ -275,8 +293,10 @@ pub fn walk_struct_declaration<'ast, V: Visitor<'ast>>(
visitor: &mut V,
decl: &'ast StructDeclaration,
) {
visit!(visitor.visit_identifier(&decl.identifier));
visit_list!(visitor.visit_struct_field(&decl.fields));
visit_scope!(visitor => {
visit!(visitor.visit_identifier(&decl.identifier));
visit_list!(visitor.visit_struct_field(&decl.fields));
});
}

pub fn walk_struct_field<'ast, V: Visitor<'ast>>(visitor: &mut V, decl: &'ast StructField) {
Expand All @@ -286,18 +306,22 @@ pub fn walk_struct_field<'ast, V: Visitor<'ast>>(visitor: &mut V, decl: &'ast St
}

pub fn walk_if<'ast, V: Visitor<'ast>>(visitor: &mut V, r#if: &'ast If) {
visit!(visitor.visit_expression(&r#if.cond));
visit!(visitor.visit_block(&r#if.body));
if let Some(r#else) = &r#if.r#else {
visit!(visitor.visit_else(r#else));
}
visit_scope!(visitor => {
visit!(visitor.visit_expression(&r#if.cond));
visit!(visitor.visit_block(&r#if.body));
if let Some(r#else) = &r#if.r#else {
visit!(visitor.visit_else(r#else));
}
});
}

pub fn walk_else<'ast, V: Visitor<'ast>>(visitor: &mut V, r#else: &'ast Else) {
match r#else {
Else::If(r#if) => visit!(visitor.visit_if(r#if)),
Else::Block(block) => visit!(visitor.visit_block(block)),
}
visit_scope!(visitor => {
match r#else {
Else::If(r#if) => visit!(visitor.visit_if(r#if)),
Else::Block(block) => visit!(visitor.visit_block(block)),
}
});
}

pub fn walk_unary_operator<'ast, V: Visitor<'ast>>(visitor: &mut V, op: &'ast UnaryOperator) {
Expand Down
2 changes: 2 additions & 0 deletions crates/fuse-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ mod debug;

pub use span::*;
pub use span_view::*;

pub type ReferenceId = usize;
137 changes: 130 additions & 7 deletions crates/fuse-semantic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,135 @@
use fuse_ast::Chunk;
use std::collections::HashMap;

pub struct Semantic<'a> {
source: &'a str,
chunk: Chunk,
use fuse_ast::{
walk_binding_pattern, walk_variable_declaration, Atom, BindingPattern, BindingPatternKind,
Chunk, Identifier, VariableDeclaration, Visitor,
};
use fuse_common::ReferenceId;

type ScopeId = ReferenceId;

struct IdentifierMap(HashMap<Atom, ReferenceId>);

impl IdentifierMap {
fn new() -> Self {
Self(HashMap::new())
}

fn insert(&mut self, atom: Atom, ref_id: ReferenceId) {
debug_assert!(!self.0.contains_key(&atom));
self.0.insert(atom, ref_id);
}
}

struct ScopeTree {
current: ScopeId,
identifier_maps: Vec<IdentifierMap>,
parent_ids: Vec<ScopeId>,
}

impl ScopeTree {
fn root_scope() -> Self {
Self {
current: 0,
parent_ids: vec![0],
identifier_maps: vec![IdentifierMap::new()],
}
}

fn push_stack(&mut self) -> ScopeId {
self.identifier_maps.push(IdentifierMap::new());
self.parent_ids.push(self.current);

let scope_id = self.identifier_maps.len() - 1;

// length of all arrays should be same.
debug_assert!(self.identifier_maps.len() == self.parent_ids.len());
self.current = scope_id;
scope_id
}

fn pop_stack(&mut self) {
assert_ne!(
self.current, 0,
"Attempt to pop the root scope from the stack."
);

self.current = self.parent();
}

fn push_identifier(&mut self, atom: Atom, ref_id: ReferenceId) {
self.identifier_maps[self.current].insert(atom, ref_id);
}

fn parent(&self) -> ScopeId {
assert_ne!(
self.current, 0,
"Attempt to access the root scope's parent."
);
self.parent_ids[self.current]
}
}

pub struct Semantic<'ast> {
source: &'ast str,
chunk: &'ast Chunk,
scope: ScopeTree,
last_reference: ReferenceId,
}

impl<'ast> Semantic<'ast> {
pub fn new(source: &'ast str, chunk: &'ast Chunk) -> Self {
Self {
source,
chunk,
scope: ScopeTree::root_scope(),
last_reference: 0,
}
}

pub fn build(mut self) {
self.visit_chunk(&self.chunk)
}

fn reference_identifier(&mut self, ident: &Identifier) {
self.last_reference += 1;
self.scope
.push_identifier(ident.name.clone(), self.last_reference);
ident.reference.set(Some(self.last_reference))
}
}

impl<'a> Semantic<'a> {
pub fn new(source: &'a str, chunk: Chunk) -> Self {
Self { source, chunk }
impl<'ast> Visitor<'ast> for Semantic<'ast> {
fn enter_scope(&mut self) {
println!("IN");
self.scope.push_stack();
}

fn leave_scope(&mut self) {
println!("OUT");
self.scope.pop_stack();
}

fn visit_identifier(&mut self, ident: &Identifier) {
println!("{ident:?}")
}

fn visit_variable_declaration(&mut self, decl: &'ast VariableDeclaration) {
match &decl.binding.kind {
BindingPatternKind::Identifier(bind) => self.reference_identifier(&bind.identifier),
_ => todo!(),
}

walk_variable_declaration(self, decl)
}

fn visit_binding_pattern(&mut self, pattern: &'ast BindingPattern) {
match &pattern.kind {
BindingPatternKind::Identifier(ident) => {
println!("{ident:?}")
}
_ => {}
}
walk_binding_pattern(self, pattern)
}
}
3 changes: 2 additions & 1 deletion crates/fusec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use fuse_semantic::Semantic;

fn compile_chunk(source: &str) {
let parsed = Parser::new(source).parse();
let semantic = Semantic::new(source, parsed.chunk.unwrap());
let semantic = Semantic::new(source, &parsed.chunk.unwrap()).build();
// panic!()
}

#[test]
Expand Down

0 comments on commit 931e456

Please sign in to comment.