Skip to main content

yog_script_analyze/resolve/
scope.rs

1//! What a source declares and where each name is used: the definitions,
2//! the scopes they're looked up in, and the references found.
3
4use 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/// What a name stands for.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DefKind {
16    /// `const`: known at build time.
17    Constant,
18    /// `readonly`, a computed property, a `foreach`'s item: read only.
19    ReadOnly,
20    /// A local, a pattern's binding, a patch's written local.
21    Variable,
22    Param,
23    Method,
24    /// A type: a class, record, union, enum, interface, alias, unit, generic.
25    Type,
26    /// An enum's case, a union's variant.
27    Case,
28    /// A field or a property that can be set.
29    Field,
30    /// A `readonly` field: set by its type's constructors only (C#'s).
31    ReadOnlyField,
32    /// A block, an item, a mod's own kind of content; a screen.
33    Content,
34    Component,
35    /// A name a `using` brings in.
36    Import,
37    /// `'a`, declared by a generic list.
38    Lifetime,
39}
40
41impl DefKind {
42    /// A local: `let mut` or `let`.
43    pub fn local(mutable: bool) -> Self {
44        if mutable {
45            DefKind::Variable
46        } else {
47            DefKind::ReadOnly
48        }
49    }
50
51    /// Whether it may be assigned to.
52    pub fn assignable(self) -> bool {
53        matches!(self, DefKind::Variable | DefKind::Param | DefKind::Field)
54    }
55}
56
57/// One definition: its name, where, what it is.
58#[derive(Debug, Clone, PartialEq)]
59pub struct Def {
60    pub name: String,
61    pub span: Span,
62    pub kind: DefKind,
63}
64
65/// A source's definitions, and each use of one (where, which).
66#[derive(Debug, Clone, Default, PartialEq)]
67pub struct Symbols {
68    pub defs: Vec<Def>,
69    pub refs: Vec<(Span, usize)>,
70}
71
72impl Symbols {
73    /// The definition the name at `offset` (a byte in the source) stands
74    /// for: a use of it, or the definition itself.
75    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/// The names one scope declares.
86#[derive(Default)]
87pub(super) struct Scope {
88    names: HashMap<String, usize>,
89    /// The types declared here by name and arity: `Box` and `Box<T>` are
90    /// two (C#'s), one name for lookup.
91    types: HashMap<(String, usize), usize>,
92}
93
94impl Resolver<'_, '_> {
95    /// Runs `f` in a scope of its own.
96    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    /// Declares `name` here, cased as `case` says; a second one of a name in
104    /// one scope is an error, unless `again` allows it (a version's other branch).
105    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            // A method beside methods of its name is an overload (C#'s): the
112            // type checker tells them apart by their parameters.
113            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    /// Declares type `name` of `arity` type parameters: a second of the same
136    /// name and arity is an error (unless `again`), one of another arity a
137    /// type apart.
138    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    /// Declares `name` in a block: a later one of the name hides the earlier.
170    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        // A local function is named as any method is (C#'s).
181        let case = if kind == DefKind::Method {
182            Case::Pascal
183        } else {
184            Case::Camel
185        };
186        self.check_case(name, case);
187    }
188
189    /// The definition `name` stands for here, innermost first.
190    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    /// A use of `name` at `span`: recorded when it's one of the source's
198    /// own (others are the game's and the library's, checked later).
199    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}