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