1
1
use std:: collections:: HashMap ;
2
2
3
3
use fuse_ast:: {
4
- walk_binding_pattern, walk_variable_declaration, Atom , BindingPattern , BindingPatternKind ,
5
- Chunk , Identifier , VariableDeclaration , Visitor ,
4
+ walk_binding_pattern, walk_function , walk_variable_declaration, Atom , BindingPattern ,
5
+ BindingPatternKind , Chunk , Identifier , VariableDeclaration , Visitor ,
6
6
} ;
7
7
use fuse_common:: ReferenceId ;
8
8
9
- type ScopeId = ReferenceId ;
9
+ #[ derive( Debug , PartialEq , Clone , Copy ) ]
10
+ struct ScopeId ( ReferenceId ) ;
11
+
12
+ impl ScopeId {
13
+ #[ inline( always) ]
14
+ const fn as_index ( self ) -> ReferenceId {
15
+ self . 0
16
+ }
17
+
18
+ #[ inline( always) ]
19
+ const fn is_root ( & self ) -> bool {
20
+ self . 0 == 0
21
+ }
22
+ }
23
+
24
+ impl PartialEq < ReferenceId > for ScopeId {
25
+ fn eq ( & self , other : & ReferenceId ) -> bool {
26
+ self . 0 == * other
27
+ }
28
+ }
10
29
11
30
struct IdentifierMap ( HashMap < Atom , ReferenceId > ) ;
12
31
@@ -15,9 +34,12 @@ impl IdentifierMap {
15
34
Self ( HashMap :: new ( ) )
16
35
}
17
36
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) ;
37
+ fn insert ( & mut self , atom : Atom , ref_id : ReferenceId ) -> Option < ReferenceId > {
38
+ self . 0 . insert ( atom, ref_id)
39
+ }
40
+
41
+ fn get ( & self , atom : & Atom ) -> Option < ReferenceId > {
42
+ self . 0 . get ( atom) . map ( |r| r. clone ( ) )
21
43
}
22
44
}
23
45
@@ -30,8 +52,8 @@ struct ScopeTree {
30
52
impl ScopeTree {
31
53
fn root_scope ( ) -> Self {
32
54
Self {
33
- current : 0 ,
34
- parent_ids : vec ! [ 0 ] ,
55
+ current : ScopeId ( 0 ) ,
56
+ parent_ids : vec ! [ ScopeId ( 0 ) ] ,
35
57
identifier_maps : vec ! [ IdentifierMap :: new( ) ] ,
36
58
}
37
59
}
@@ -40,12 +62,11 @@ impl ScopeTree {
40
62
self . identifier_maps . push ( IdentifierMap :: new ( ) ) ;
41
63
self . parent_ids . push ( self . current ) ;
42
64
43
- let scope_id = self . identifier_maps . len ( ) - 1 ;
44
-
45
65
// length of all arrays should be same.
46
66
debug_assert ! ( self . identifier_maps. len( ) == self . parent_ids. len( ) ) ;
47
- self . current = scope_id;
48
- scope_id
67
+
68
+ self . current = ScopeId ( self . identifier_maps . len ( ) - 1 ) ;
69
+ self . current
49
70
}
50
71
51
72
fn pop_stack ( & mut self ) {
@@ -57,16 +78,43 @@ impl ScopeTree {
57
78
self . current = self . parent ( ) ;
58
79
}
59
80
60
- fn push_identifier ( & mut self , atom : Atom , ref_id : ReferenceId ) {
61
- self . identifier_maps [ self . current ] . insert ( atom, ref_id) ;
81
+ /// Get an identifier reference from current scope or its parents.
82
+ /// This function is implemented using loops instead of recursion.
83
+ fn identifier_reference ( & mut self , atom : & Atom ) -> Option < ReferenceId > {
84
+ let mut scope_id = self . current ;
85
+ let mut reference;
86
+ loop {
87
+ reference = self . identifier_maps [ scope_id. as_index ( ) ] . get ( atom) ;
88
+
89
+ if reference. is_some ( ) || scope_id. is_root ( ) {
90
+ break ;
91
+ } else {
92
+ scope_id = self . parent_of ( scope_id) ;
93
+ }
94
+ }
95
+ reference
96
+ }
97
+
98
+ /// Set a `ReferenceId` for the given identifier's `Atom` in the current scope.
99
+ /// Would return the last `ReferenceId` if we are shadowing it.
100
+ fn set_identifier_reference ( & mut self , atom : Atom , ref_id : ReferenceId ) -> Option < ReferenceId > {
101
+ self . identifier_maps [ self . current . as_index ( ) ] . insert ( atom, ref_id)
62
102
}
63
103
64
104
fn parent ( & self ) -> ScopeId {
65
105
assert_ne ! (
66
106
self . current, 0 ,
67
107
"Attempt to access the root scope's parent."
68
108
) ;
69
- self . parent_ids [ self . current ]
109
+ self . parent_ids [ self . current . as_index ( ) ]
110
+ }
111
+
112
+ fn parent_of ( & self , children_id : ScopeId ) -> ScopeId {
113
+ assert_ne ! (
114
+ self . current, 0 ,
115
+ "Attempt to access the root scope's parent."
116
+ ) ;
117
+ self . parent_ids [ children_id. as_index ( ) ]
70
118
}
71
119
}
72
120
@@ -91,45 +139,51 @@ impl<'ast> Semantic<'ast> {
91
139
self . visit_chunk ( & self . chunk )
92
140
}
93
141
94
- fn reference_identifier ( & mut self , ident : & Identifier ) {
142
+ fn declare_identifier ( & mut self , ident : & Identifier ) {
95
143
self . last_reference += 1 ;
96
144
self . scope
97
- . push_identifier ( ident. name . clone ( ) , self . last_reference ) ;
145
+ . set_identifier_reference ( ident. name . clone ( ) , self . last_reference ) ;
98
146
ident. reference . set ( Some ( self . last_reference ) )
99
147
}
148
+
149
+ fn reference_identifier ( & mut self , ident : & Identifier ) {
150
+ let reference = self
151
+ . scope
152
+ . identifier_reference ( & ident. name )
153
+ . expect ( "Reference to undefined identifier." ) ;
154
+ ident. reference . set ( Some ( reference) )
155
+ }
100
156
}
101
157
102
158
impl < ' ast > Visitor < ' ast > for Semantic < ' ast > {
103
159
fn enter_scope ( & mut self ) {
104
- println ! ( "IN" ) ;
105
160
self . scope . push_stack ( ) ;
106
161
}
107
162
108
163
fn leave_scope ( & mut self ) {
109
- println ! ( "OUT" ) ;
110
164
self . scope . pop_stack ( ) ;
111
165
}
112
166
113
167
fn visit_identifier ( & mut self , ident : & Identifier ) {
114
- println ! ( "{ ident:?}" )
168
+ self . reference_identifier ( ident) ;
115
169
}
116
170
117
171
fn visit_variable_declaration ( & mut self , decl : & ' ast VariableDeclaration ) {
118
172
match & decl. binding . kind {
119
- BindingPatternKind :: Identifier ( bind) => self . reference_identifier ( & bind. identifier ) ,
173
+ BindingPatternKind :: Identifier ( bind) => self . declare_identifier ( & bind. identifier ) ,
120
174
_ => todo ! ( ) ,
121
175
}
122
176
123
177
walk_variable_declaration ( self , decl)
124
178
}
125
179
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 )
180
+ fn visit_function_declaration ( & mut self , decl : & ' ast fuse_ast :: Function ) {
181
+ let identifier = decl
182
+ . signature
183
+ . identifier
184
+ . as_ref ( )
185
+ . expect ( "All function declarations need an identifier." ) ;
186
+ self . declare_identifier ( identifier ) ;
187
+ walk_function ( self , decl )
134
188
}
135
189
}
0 commit comments