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 crate::ast::Name;
10use crate::source::token::Span;
11
12/// What a name stands for.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum DefKind {
15    /// `let`, `const`, `val`, a pattern's binding: read only.
16    Value,
17    /// `var`, a `var` parameter or field, a patch's written local.
18    Variable,
19    Param,
20    Fn,
21    /// A type: a record, union, enum, alias, unit, generic.
22    Type,
23    /// An enum's case, a union's variant.
24    Case,
25    /// A record's field, a content's slots.
26    Field,
27    /// A block, an item, a mod's own kind of content; a screen.
28    Content,
29    /// A name a `use` brings in.
30    Import,
31}
32
33impl DefKind {
34    /// Whether it may be assigned to.
35    pub fn assignable(self) -> bool {
36        matches!(self, DefKind::Variable | DefKind::Field)
37    }
38}
39
40/// One definition: its name, where, what it is.
41#[derive(Debug, Clone, PartialEq)]
42pub struct Def {
43    pub name: String,
44    pub span: Span,
45    pub kind: DefKind,
46}
47
48/// A source's definitions, and each use of one (where, which).
49#[derive(Debug, Clone, Default, PartialEq)]
50pub struct Symbols {
51    pub defs: Vec<Def>,
52    pub refs: Vec<(Span, usize)>,
53}
54
55impl Symbols {
56    /// The definition the name at `offset` (a byte in the source) stands
57    /// for: a use of it, or the definition itself.
58    pub fn def_at(&self, offset: u32) -> Option<&Def> {
59        let inside = |s: Span| s.start <= offset && offset < s.end;
60        self.refs
61            .iter()
62            .find(|(s, _)| inside(*s))
63            .and_then(|(_, d)| self.defs.get(*d))
64            .or_else(|| self.defs.iter().find(|d| inside(d.span)))
65    }
66}
67
68/// The names one scope declares.
69#[derive(Default)]
70pub(super) struct Scope {
71    names: HashMap<String, usize>,
72}
73
74impl Resolver<'_, '_> {
75    /// Runs `f` in a scope of its own.
76    pub(super) fn scoped<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
77        self.scopes.push(Scope::default());
78        let out = f(self);
79        self.scopes.pop();
80        out
81    }
82
83    /// Declares `name` here; a second one of a name in one scope is an
84    /// error, unless `again` allows it (a version's other branch).
85    pub(super) fn define(&mut self, name: &Name, kind: DefKind, again: bool) {
86        let id = self.symbols.defs.len();
87        let Some(scope) = self.scopes.last_mut() else {
88            return;
89        };
90        if let Some(&first) = scope.names.get(&name.text) {
91            if !again {
92                let at = self.symbols.defs[first].span;
93                let line = self.report.line_of(at);
94                self.report.error(
95                    script_problem::DUPLICATE,
96                    name.span,
97                    format!("`{}` is declared already (line {line})", name.text),
98                );
99            }
100            return;
101        }
102        scope.names.insert(name.text.clone(), id);
103        self.symbols.defs.push(Def {
104            name: name.text.clone(),
105            span: name.span,
106            kind,
107        });
108    }
109
110    /// Declares `name` in a block: a later one of the name hides the earlier.
111    pub(super) fn bind(&mut self, name: &Name, kind: DefKind) {
112        let id = self.symbols.defs.len();
113        if let Some(scope) = self.scopes.last_mut() {
114            scope.names.insert(name.text.clone(), id);
115            self.symbols.defs.push(Def {
116                name: name.text.clone(),
117                span: name.span,
118                kind,
119            });
120        }
121    }
122
123    /// The definition `name` stands for here, innermost first.
124    pub(super) fn lookup(&self, name: &str) -> Option<usize> {
125        self.scopes
126            .iter()
127            .rev()
128            .find_map(|s| s.names.get(name).copied())
129    }
130
131    /// A use of `name` at `span`: recorded when it's one of the source's
132    /// own (others are the game's and the library's, checked later).
133    pub(super) fn refer(&mut self, name: &str, span: Span) -> Option<DefKind> {
134        let id = self.lookup(name)?;
135        self.symbols.refs.push((span, id));
136        Some(self.symbols.defs[id].kind)
137    }
138}