Skip to main content

yog_script_analyze/ast/
indexer.rs

1//! An indexer (`T this[i32 i] { get; set; }`): what `x[i]` reads and
2//! writes. Its accessors are methods named as the CLR names them —
3//! `get_Item(i)`, `set_Item(i, value)` — so inheritance and the JVM treat
4//! them as any method.
5
6use super::{Accessor, Method, MethodBody, Name, Param, Type, value_param};
7use crate::source::token::Span;
8use crate::words::{get, set};
9use yog_script_ids::clr::INDEXER;
10
11/// What joins an accessor's word to the indexer's name (`get_Item`).
12use yog_script_ids::clr::ACCESSOR_SEP;
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Indexer {
16    pub ty: Type,
17    pub params: Vec<Param>,
18    pub accessors: Vec<Accessor>,
19    pub span: Span,
20    /// Its `get` and its `set` as methods, made once: what's checked and
21    /// compiled is these (one tree, so what's found of it is found once).
22    get: Option<Box<Method>>,
23    set: Option<Box<Method>>,
24}
25
26/// An indexer's accessor's method name: `get_Item`, `set_Item`.
27pub fn accessor_name(word: &str) -> String {
28    format!("{word}{ACCESSOR_SEP}{INDEXER}")
29}
30
31impl Indexer {
32    /// Indexer `ty this[params] { accessors }` (at `span`), its accessors
33    /// made methods.
34    pub fn new(ty: Type, params: Vec<Param>, accessors: Vec<Accessor>, span: Span) -> Self {
35        let mut ix = Indexer {
36            ty,
37            params,
38            accessors,
39            span,
40            get: None,
41            set: None,
42        };
43        ix.get = ix.accessor(get::WORD.text).map(|body| {
44            Box::new(ix.method(get::WORD.text, Some(ix.ty.clone()), ix.params.clone(), body))
45        });
46        ix.set = ix.accessor(set::WORD.text).map(|body| {
47            let mut params = ix.params.clone();
48            let at = Name {
49                text: set::WORD.text.to_string(),
50                span: ix.span,
51            };
52            params.push(value_param(&ix.ty, &at));
53            Box::new(ix.method(set::WORD.text, None, params, body))
54        });
55        ix
56    }
57
58    /// Its `get` as a method: what `x[i]` calls.
59    pub fn getter(&self) -> Option<&Method> {
60        self.get.as_deref()
61    }
62
63    /// Its `set` as a method, the value its last parameter: what `x[i] = v` calls.
64    pub fn setter(&self) -> Option<&Method> {
65        self.set.as_deref()
66    }
67
68    /// Accessor `word`'s body (none when it has none), if it has that accessor.
69    fn accessor(&self, word: &str) -> Option<Option<MethodBody>> {
70        self.accessors
71            .iter()
72            .find(|a| a.name.text == word)
73            .map(|a| a.body.clone())
74    }
75
76    fn method(
77        &self,
78        word: &str,
79        ret: Option<Type>,
80        params: Vec<Param>,
81        body: Option<MethodBody>,
82    ) -> Method {
83        Method {
84            ret,
85            name: Name {
86                text: accessor_name(word),
87                span: self.span,
88            },
89            explicit: None,
90            generics: Vec::new(),
91            params,
92            constraints: Vec::new(),
93            requires: None,
94            body,
95        }
96    }
97}