Skip to content

Commit f44bfa1

Browse files
committed
feat(semantic): add basic variable resolution.
1 parent 931e456 commit f44bfa1

File tree

2 files changed

+90
-31
lines changed

2 files changed

+90
-31
lines changed

crates/fuse-semantic/src/lib.rs

+83-29
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
use std::collections::HashMap;
22

33
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,
66
};
77
use fuse_common::ReferenceId;
88

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+
}
1029

1130
struct IdentifierMap(HashMap<Atom, ReferenceId>);
1231

@@ -15,9 +34,12 @@ impl IdentifierMap {
1534
Self(HashMap::new())
1635
}
1736

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())
2143
}
2244
}
2345

@@ -30,8 +52,8 @@ struct ScopeTree {
3052
impl ScopeTree {
3153
fn root_scope() -> Self {
3254
Self {
33-
current: 0,
34-
parent_ids: vec![0],
55+
current: ScopeId(0),
56+
parent_ids: vec![ScopeId(0)],
3557
identifier_maps: vec![IdentifierMap::new()],
3658
}
3759
}
@@ -40,12 +62,11 @@ impl ScopeTree {
4062
self.identifier_maps.push(IdentifierMap::new());
4163
self.parent_ids.push(self.current);
4264

43-
let scope_id = self.identifier_maps.len() - 1;
44-
4565
// length of all arrays should be same.
4666
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
4970
}
5071

5172
fn pop_stack(&mut self) {
@@ -57,16 +78,43 @@ impl ScopeTree {
5778
self.current = self.parent();
5879
}
5980

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)
62102
}
63103

64104
fn parent(&self) -> ScopeId {
65105
assert_ne!(
66106
self.current, 0,
67107
"Attempt to access the root scope's parent."
68108
);
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()]
70118
}
71119
}
72120

@@ -91,45 +139,51 @@ impl<'ast> Semantic<'ast> {
91139
self.visit_chunk(&self.chunk)
92140
}
93141

94-
fn reference_identifier(&mut self, ident: &Identifier) {
142+
fn declare_identifier(&mut self, ident: &Identifier) {
95143
self.last_reference += 1;
96144
self.scope
97-
.push_identifier(ident.name.clone(), self.last_reference);
145+
.set_identifier_reference(ident.name.clone(), self.last_reference);
98146
ident.reference.set(Some(self.last_reference))
99147
}
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+
}
100156
}
101157

102158
impl<'ast> Visitor<'ast> for Semantic<'ast> {
103159
fn enter_scope(&mut self) {
104-
println!("IN");
105160
self.scope.push_stack();
106161
}
107162

108163
fn leave_scope(&mut self) {
109-
println!("OUT");
110164
self.scope.pop_stack();
111165
}
112166

113167
fn visit_identifier(&mut self, ident: &Identifier) {
114-
println!("{ident:?}")
168+
self.reference_identifier(ident);
115169
}
116170

117171
fn visit_variable_declaration(&mut self, decl: &'ast VariableDeclaration) {
118172
match &decl.binding.kind {
119-
BindingPatternKind::Identifier(bind) => self.reference_identifier(&bind.identifier),
173+
BindingPatternKind::Identifier(bind) => self.declare_identifier(&bind.identifier),
120174
_ => todo!(),
121175
}
122176

123177
walk_variable_declaration(self, decl)
124178
}
125179

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)
134188
}
135189
}

crates/fusec/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ 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()).build();
7-
// panic!()
6+
let chunk = parsed.chunk.unwrap();
7+
let semantic = Semantic::new(source, &chunk).build();
8+
// panic!("{:?}", chunk)
89
}
910

1011
#[test]
1112
fn manual_test() {
1213
compile_chunk(r#"
14+
let z = 123
1315
let x = 123
1416
let y = x
17+
fn x()
18+
let x = y
19+
end
1520
"#)
1621
}

0 commit comments

Comments
 (0)