Skip to content

Commit 931e456

Browse files
committed
feat(semantic): add scope tree to semantic analysis.
1 parent a3f05a1 commit 931e456

File tree

5 files changed

+176
-30
lines changed

5 files changed

+176
-30
lines changed

crates/fuse-ast/src/ast.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use fuse_common::Span;
1+
use fuse_common::{ReferenceId, Span};
22
use fuse_common_proc::serializable;
33
use std::{cell::Cell, rc::Rc};
44

@@ -94,7 +94,7 @@ pub struct TypeAnnotation {
9494
}
9595

9696
#[serializable]
97-
#[derive(Debug, PartialEq)]
97+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9898
pub struct Atom(pub Rc<str>);
9999

100100
#[serializable]
@@ -188,10 +188,6 @@ pub struct Identifier {
188188
pub reference: Cell<Option<ReferenceId>>,
189189
}
190190

191-
#[serializable]
192-
#[derive(Debug, PartialEq, Clone, Copy)]
193-
pub struct ReferenceId(u32);
194-
195191
#[serializable]
196192
#[derive(Debug, PartialEq)]
197193
pub struct Function {

crates/fuse-ast/src/visit.rs

+40-16
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,19 @@ macro_rules! visit_list {
1919
};
2020
}
2121

22+
macro_rules! visit_scope {
23+
($visitor:ident => $block:block) => {
24+
$visitor.enter_scope();
25+
$block
26+
$visitor.leave_scope();
27+
};
28+
}
29+
2230
pub trait Visitor<'ast>: Sized {
31+
fn enter_scope(&mut self) {}
32+
33+
fn leave_scope(&mut self) {}
34+
2335
fn visit_chunk(&mut self, chunk: &'ast Chunk) {
2436
walk_block(self, &chunk.body)
2537
}
@@ -164,7 +176,9 @@ pub trait Visitor<'ast>: Sized {
164176
}
165177

166178
pub fn walk_block<'ast, V: Visitor<'ast>>(visitor: &mut V, block: &'ast Block) {
167-
visit_list!(visitor.visit_statement(&block.statements))
179+
visit_scope!(visitor => {
180+
visit_list!(visitor.visit_statement(&block.statements));
181+
});
168182
}
169183

170184
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
216230
}
217231

218232
pub fn walk_function<'ast, V: Visitor<'ast>>(visitor: &mut V, func: &'ast Function) {
219-
visit!(visitor.visit_function_signature(&func.signature));
220-
visit!(visitor.visit_function_body(&func.body));
233+
visit_scope!(visitor => {
234+
visit!(visitor.visit_function_signature(&func.signature));
235+
visit!(visitor.visit_function_body(&func.body));
236+
});
221237
}
222238

223239
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
260276
}
261277

262278
pub fn walk_enum_declaration<'ast, V: Visitor<'ast>>(visitor: &mut V, decl: &'ast EnumDeclaration) {
263-
visit!(visitor.visit_identifier(&decl.identifier));
264-
visit_list!(visitor.visit_enum_variant(&decl.variants));
279+
visit_scope!(visitor => {
280+
visit!(visitor.visit_identifier(&decl.identifier));
281+
visit_list!(visitor.visit_enum_variant(&decl.variants));
282+
});
265283
}
266284

267285
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>>(
275293
visitor: &mut V,
276294
decl: &'ast StructDeclaration,
277295
) {
278-
visit!(visitor.visit_identifier(&decl.identifier));
279-
visit_list!(visitor.visit_struct_field(&decl.fields));
296+
visit_scope!(visitor => {
297+
visit!(visitor.visit_identifier(&decl.identifier));
298+
visit_list!(visitor.visit_struct_field(&decl.fields));
299+
});
280300
}
281301

282302
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
286306
}
287307

288308
pub fn walk_if<'ast, V: Visitor<'ast>>(visitor: &mut V, r#if: &'ast If) {
289-
visit!(visitor.visit_expression(&r#if.cond));
290-
visit!(visitor.visit_block(&r#if.body));
291-
if let Some(r#else) = &r#if.r#else {
292-
visit!(visitor.visit_else(r#else));
293-
}
309+
visit_scope!(visitor => {
310+
visit!(visitor.visit_expression(&r#if.cond));
311+
visit!(visitor.visit_block(&r#if.body));
312+
if let Some(r#else) = &r#if.r#else {
313+
visit!(visitor.visit_else(r#else));
314+
}
315+
});
294316
}
295317

296318
pub fn walk_else<'ast, V: Visitor<'ast>>(visitor: &mut V, r#else: &'ast Else) {
297-
match r#else {
298-
Else::If(r#if) => visit!(visitor.visit_if(r#if)),
299-
Else::Block(block) => visit!(visitor.visit_block(block)),
300-
}
319+
visit_scope!(visitor => {
320+
match r#else {
321+
Else::If(r#if) => visit!(visitor.visit_if(r#if)),
322+
Else::Block(block) => visit!(visitor.visit_block(block)),
323+
}
324+
});
301325
}
302326

303327
pub fn walk_unary_operator<'ast, V: Visitor<'ast>>(visitor: &mut V, op: &'ast UnaryOperator) {

crates/fuse-common/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ mod debug;
44

55
pub use span::*;
66
pub use span_view::*;
7+
8+
pub type ReferenceId = usize;

crates/fuse-semantic/src/lib.rs

+130-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,135 @@
1-
use fuse_ast::Chunk;
1+
use std::collections::HashMap;
22

3-
pub struct Semantic<'a> {
4-
source: &'a str,
5-
chunk: Chunk,
3+
use fuse_ast::{
4+
walk_binding_pattern, walk_variable_declaration, Atom, BindingPattern, BindingPatternKind,
5+
Chunk, Identifier, VariableDeclaration, Visitor,
6+
};
7+
use fuse_common::ReferenceId;
8+
9+
type ScopeId = ReferenceId;
10+
11+
struct IdentifierMap(HashMap<Atom, ReferenceId>);
12+
13+
impl IdentifierMap {
14+
fn new() -> Self {
15+
Self(HashMap::new())
16+
}
17+
18+
fn insert(&mut self, atom: Atom, ref_id: ReferenceId) {
19+
debug_assert!(!self.0.contains_key(&atom));
20+
self.0.insert(atom, ref_id);
21+
}
22+
}
23+
24+
struct ScopeTree {
25+
current: ScopeId,
26+
identifier_maps: Vec<IdentifierMap>,
27+
parent_ids: Vec<ScopeId>,
28+
}
29+
30+
impl ScopeTree {
31+
fn root_scope() -> Self {
32+
Self {
33+
current: 0,
34+
parent_ids: vec![0],
35+
identifier_maps: vec![IdentifierMap::new()],
36+
}
37+
}
38+
39+
fn push_stack(&mut self) -> ScopeId {
40+
self.identifier_maps.push(IdentifierMap::new());
41+
self.parent_ids.push(self.current);
42+
43+
let scope_id = self.identifier_maps.len() - 1;
44+
45+
// length of all arrays should be same.
46+
debug_assert!(self.identifier_maps.len() == self.parent_ids.len());
47+
self.current = scope_id;
48+
scope_id
49+
}
50+
51+
fn pop_stack(&mut self) {
52+
assert_ne!(
53+
self.current, 0,
54+
"Attempt to pop the root scope from the stack."
55+
);
56+
57+
self.current = self.parent();
58+
}
59+
60+
fn push_identifier(&mut self, atom: Atom, ref_id: ReferenceId) {
61+
self.identifier_maps[self.current].insert(atom, ref_id);
62+
}
63+
64+
fn parent(&self) -> ScopeId {
65+
assert_ne!(
66+
self.current, 0,
67+
"Attempt to access the root scope's parent."
68+
);
69+
self.parent_ids[self.current]
70+
}
71+
}
72+
73+
pub struct Semantic<'ast> {
74+
source: &'ast str,
75+
chunk: &'ast Chunk,
76+
scope: ScopeTree,
77+
last_reference: ReferenceId,
78+
}
79+
80+
impl<'ast> Semantic<'ast> {
81+
pub fn new(source: &'ast str, chunk: &'ast Chunk) -> Self {
82+
Self {
83+
source,
84+
chunk,
85+
scope: ScopeTree::root_scope(),
86+
last_reference: 0,
87+
}
88+
}
89+
90+
pub fn build(mut self) {
91+
self.visit_chunk(&self.chunk)
92+
}
93+
94+
fn reference_identifier(&mut self, ident: &Identifier) {
95+
self.last_reference += 1;
96+
self.scope
97+
.push_identifier(ident.name.clone(), self.last_reference);
98+
ident.reference.set(Some(self.last_reference))
99+
}
6100
}
7101

8-
impl<'a> Semantic<'a> {
9-
pub fn new(source: &'a str, chunk: Chunk) -> Self {
10-
Self { source, chunk }
102+
impl<'ast> Visitor<'ast> for Semantic<'ast> {
103+
fn enter_scope(&mut self) {
104+
println!("IN");
105+
self.scope.push_stack();
106+
}
107+
108+
fn leave_scope(&mut self) {
109+
println!("OUT");
110+
self.scope.pop_stack();
111+
}
112+
113+
fn visit_identifier(&mut self, ident: &Identifier) {
114+
println!("{ident:?}")
115+
}
116+
117+
fn visit_variable_declaration(&mut self, decl: &'ast VariableDeclaration) {
118+
match &decl.binding.kind {
119+
BindingPatternKind::Identifier(bind) => self.reference_identifier(&bind.identifier),
120+
_ => todo!(),
121+
}
122+
123+
walk_variable_declaration(self, decl)
124+
}
125+
126+
fn visit_binding_pattern(&mut self, pattern: &'ast BindingPattern) {
127+
match &pattern.kind {
128+
BindingPatternKind::Identifier(ident) => {
129+
println!("{ident:?}")
130+
}
131+
_ => {}
132+
}
133+
walk_binding_pattern(self, pattern)
11134
}
12135
}

crates/fusec/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use fuse_semantic::Semantic;
33

44
fn compile_chunk(source: &str) {
55
let parsed = Parser::new(source).parse();
6-
let semantic = Semantic::new(source, parsed.chunk.unwrap());
6+
let semantic = Semantic::new(source, &parsed.chunk.unwrap()).build();
7+
// panic!()
78
}
89

910
#[test]

0 commit comments

Comments
 (0)