diff --git a/crates/fuse-ast/src/ast.rs b/crates/fuse-ast/src/ast.rs index 01ac54d..9a7f79d 100644 --- a/crates/fuse-ast/src/ast.rs +++ b/crates/fuse-ast/src/ast.rs @@ -1,4 +1,4 @@ -use fuse_common::Span; +use fuse_common::{ReferenceId, Span}; use fuse_common_proc::serializable; use std::{cell::Cell, rc::Rc}; @@ -94,7 +94,7 @@ pub struct TypeAnnotation { } #[serializable] -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Atom(pub Rc); #[serializable] @@ -188,10 +188,6 @@ pub struct Identifier { pub reference: Cell>, } -#[serializable] -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct ReferenceId(u32); - #[serializable] #[derive(Debug, PartialEq)] pub struct Function { diff --git a/crates/fuse-ast/src/visit.rs b/crates/fuse-ast/src/visit.rs index 6a50c36..d6cb5fd 100644 --- a/crates/fuse-ast/src/visit.rs +++ b/crates/fuse-ast/src/visit.rs @@ -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) } @@ -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) { @@ -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>>( @@ -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) { @@ -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) { @@ -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) { diff --git a/crates/fuse-common/src/lib.rs b/crates/fuse-common/src/lib.rs index 398b34b..df4960b 100644 --- a/crates/fuse-common/src/lib.rs +++ b/crates/fuse-common/src/lib.rs @@ -4,3 +4,5 @@ mod debug; pub use span::*; pub use span_view::*; + +pub type ReferenceId = usize; diff --git a/crates/fuse-semantic/src/lib.rs b/crates/fuse-semantic/src/lib.rs index b25970b..2bcf6b1 100644 --- a/crates/fuse-semantic/src/lib.rs +++ b/crates/fuse-semantic/src/lib.rs @@ -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); + +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, + parent_ids: Vec, +} + +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) } } diff --git a/crates/fusec/src/lib.rs b/crates/fusec/src/lib.rs index 029efeb..3795074 100644 --- a/crates/fusec/src/lib.rs +++ b/crates/fusec/src/lib.rs @@ -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]