yog_script_analyze/resolve/
scope.rs1use std::collections::HashMap;
5
6use yog_ids::script_problem;
7
8use super::Resolver;
9use super::naming::Case;
10use crate::ast::Name;
11use crate::source::token::Span;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DefKind {
16 Constant,
18 ReadOnly,
20 Variable,
22 Param,
23 Method,
24 Type,
26 Case,
28 Field,
30 ReadOnlyField,
32 Content,
34 Component,
35 Import,
37 Lifetime,
39}
40
41impl DefKind {
42 pub fn local(mutable: bool) -> Self {
44 if mutable {
45 DefKind::Variable
46 } else {
47 DefKind::ReadOnly
48 }
49 }
50
51 pub fn assignable(self) -> bool {
53 matches!(self, DefKind::Variable | DefKind::Param | DefKind::Field)
54 }
55}
56
57#[derive(Debug, Clone, PartialEq)]
59pub struct Def {
60 pub name: String,
61 pub span: Span,
62 pub kind: DefKind,
63}
64
65#[derive(Debug, Clone, Default, PartialEq)]
67pub struct Symbols {
68 pub defs: Vec<Def>,
69 pub refs: Vec<(Span, usize)>,
70}
71
72impl Symbols {
73 pub fn def_at(&self, offset: u32) -> Option<&Def> {
76 let inside = |s: Span| s.start <= offset && offset < s.end;
77 self.refs
78 .iter()
79 .find(|(s, _)| inside(*s))
80 .and_then(|(_, d)| self.defs.get(*d))
81 .or_else(|| self.defs.iter().find(|d| inside(d.span)))
82 }
83}
84
85#[derive(Default)]
87pub(super) struct Scope {
88 names: HashMap<String, usize>,
89 types: HashMap<(String, usize), usize>,
92}
93
94impl Resolver<'_, '_> {
95 pub(super) fn scoped<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
97 self.scopes.push(Scope::default());
98 let out = f(self);
99 self.scopes.pop();
100 out
101 }
102
103 pub(super) fn define(&mut self, name: &Name, kind: DefKind, case: Case, again: bool) {
106 let id = self.symbols.defs.len();
107 let Some(scope) = self.scopes.last_mut() else {
108 return;
109 };
110 if let Some(&first) = scope.names.get(&name.text) {
111 let overload =
114 kind == DefKind::Method && self.symbols.defs[first].kind == DefKind::Method;
115 if !again && !overload {
116 let at = self.symbols.defs[first].span;
117 let line = self.report.line_of(at);
118 self.report.error(
119 script_problem::DUPLICATE,
120 name.span,
121 format!("`{}` is declared already (line {line})", name.text),
122 );
123 }
124 return;
125 }
126 scope.names.insert(name.text.clone(), id);
127 self.symbols.defs.push(Def {
128 name: name.text.clone(),
129 span: name.span,
130 kind,
131 });
132 self.check_case(name, case);
133 }
134
135 pub(super) fn define_type(&mut self, name: &Name, arity: usize, case: Case, again: bool) {
139 let key = (name.text.clone(), arity);
140 let Some(scope) = self.scopes.last_mut() else {
141 return;
142 };
143 if let Some(&first) = scope.types.get(&key) {
144 if !again {
145 let at = self.symbols.defs[first].span;
146 let line = self.report.line_of(at);
147 self.report.error(
148 script_problem::DUPLICATE,
149 name.span,
150 format!(
151 "`{}` of {arity} type parameter(s) is declared already (line {line})",
152 name.text
153 ),
154 );
155 }
156 return;
157 }
158 let id = self.symbols.defs.len();
159 scope.types.insert(key, id);
160 scope.names.entry(name.text.clone()).or_insert(id);
161 self.symbols.defs.push(Def {
162 name: name.text.clone(),
163 span: name.span,
164 kind: DefKind::Type,
165 });
166 self.check_case(name, case);
167 }
168
169 pub(super) fn bind(&mut self, name: &Name, kind: DefKind) {
171 let id = self.symbols.defs.len();
172 if let Some(scope) = self.scopes.last_mut() {
173 scope.names.insert(name.text.clone(), id);
174 self.symbols.defs.push(Def {
175 name: name.text.clone(),
176 span: name.span,
177 kind,
178 });
179 }
180 let case = if kind == DefKind::Method {
182 Case::Pascal
183 } else {
184 Case::Camel
185 };
186 self.check_case(name, case);
187 }
188
189 pub(super) fn lookup(&self, name: &str) -> Option<usize> {
191 self.scopes
192 .iter()
193 .rev()
194 .find_map(|s| s.names.get(name).copied())
195 }
196
197 pub(super) fn refer(&mut self, name: &str, span: Span) -> Option<DefKind> {
200 let id = self.lookup(name)?;
201 self.symbols.refs.push((span, id));
202 Some(self.symbols.defs[id].kind)
203 }
204}