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 block, an item, a mod's own kind of content; a screen.
31    Content,
32    Component,
33    /// A name a `using` brings in.
34    Import,
35    /// `'a`, declared by a generic list.
36    Lifetime,
37}
38
39impl DefKind {
40    /// A local: `let mut` or `let`.
41    pub fn local(mutable: bool) -> Self {
42        if mutable {
43            DefKind::Variable
44        } else {
45            DefKind::ReadOnly
46        }
47    }
48
49    /// Whether it may be assigned to.
50    pub fn assignable(self) -> bool {
51        matches!(self, DefKind::Variable | DefKind::Param | DefKind::Field)
52    }
53}
54
55/// One definition: its name, where, what it is.
56#[derive(Debug, Clone, PartialEq)]
57pub struct Def {
58    pub name: String,
59    pub span: Span,
60    pub kind: DefKind,
61}
62
63/// A source's definitions, and each use of one (where, which).
64#[derive(Debug, Clone, Default, PartialEq)]
65pub struct Symbols {
66    pub defs: Vec<Def>,
67    pub refs: Vec<(Span, usize)>,
68}
69
70impl Symbols {
71    /// The definition the name at `offset` (a byte in the source) stands
72    /// for: a use of it, or the definition itself.
73    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/// The names one scope declares.
84#[derive(Default)]
85pub(super) struct Scope {
86    names: HashMap<String, usize>,
87}
88
89impl Resolver<'_, '_> {
90    /// Runs `f` in a scope of its own.
91    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    /// Declares `name` here, cased as `case` says; a second one of a name in
99    /// one scope is an error, unless `again` allows it (a version's other branch).
100    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    /// Declares `name` in a block: a later one of the name hides the earlier.
127    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    /// The definition `name` stands for here, innermost first.
141    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    /// A use of `name` at `span`: recorded when it's one of the source's
149    /// own (others are the game's and the library's, checked later).
150    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}