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 Content,
32 Component,
33 Import,
35 Lifetime,
37}
38
39impl DefKind {
40 pub fn local(mutable: bool) -> Self {
42 if mutable {
43 DefKind::Variable
44 } else {
45 DefKind::ReadOnly
46 }
47 }
48
49 pub fn assignable(self) -> bool {
51 matches!(self, DefKind::Variable | DefKind::Param | DefKind::Field)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq)]
57pub struct Def {
58 pub name: String,
59 pub span: Span,
60 pub kind: DefKind,
61}
62
63#[derive(Debug, Clone, Default, PartialEq)]
65pub struct Symbols {
66 pub defs: Vec<Def>,
67 pub refs: Vec<(Span, usize)>,
68}
69
70impl Symbols {
71 pub fn def_at(&self, offset: u32) -> Option<&Def> {
74 let inside = |s: Span| s.start <= offset && offset < s.end;
75 self.refs
76 .iter()
77 .find(|(s, _)| inside(*s))
78 .and_then(|(_, d)| self.defs.get(*d))
79 .or_else(|| self.defs.iter().find(|d| inside(d.span)))
80 }
81}
82
83#[derive(Default)]
85pub(super) struct Scope {
86 names: HashMap<String, usize>,
87}
88
89impl Resolver<'_, '_> {
90 pub(super) fn scoped<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
92 self.scopes.push(Scope::default());
93 let out = f(self);
94 self.scopes.pop();
95 out
96 }
97
98 pub(super) fn define(&mut self, name: &Name, kind: DefKind, case: Case, again: bool) {
101 let id = self.symbols.defs.len();
102 let Some(scope) = self.scopes.last_mut() else {
103 return;
104 };
105 if let Some(&first) = scope.names.get(&name.text) {
106 if !again {
107 let at = self.symbols.defs[first].span;
108 let line = self.report.line_of(at);
109 self.report.error(
110 script_problem::DUPLICATE,
111 name.span,
112 format!("`{}` is declared already (line {line})", name.text),
113 );
114 }
115 return;
116 }
117 scope.names.insert(name.text.clone(), id);
118 self.symbols.defs.push(Def {
119 name: name.text.clone(),
120 span: name.span,
121 kind,
122 });
123 self.check_case(name, case);
124 }
125
126 pub(super) fn bind(&mut self, name: &Name, kind: DefKind) {
128 let id = self.symbols.defs.len();
129 if let Some(scope) = self.scopes.last_mut() {
130 scope.names.insert(name.text.clone(), id);
131 self.symbols.defs.push(Def {
132 name: name.text.clone(),
133 span: name.span,
134 kind,
135 });
136 }
137 self.check_case(name, Case::Camel);
138 }
139
140 pub(super) fn lookup(&self, name: &str) -> Option<usize> {
142 self.scopes
143 .iter()
144 .rev()
145 .find_map(|s| s.names.get(name).copied())
146 }
147
148 pub(super) fn refer(&mut self, name: &str, span: Span) -> Option<DefKind> {
151 let id = self.lookup(name)?;
152 self.symbols.refs.push((span, id));
153 Some(self.symbols.defs[id].kind)
154 }
155}