1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// Copyright 2022 The Goscript Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//
// This code is adapted from the offical Go code written in Go
// with license as follows:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

use super::check::DeclInfo;
use super::constant;
use super::obj::LangObj;
use super::package::Package;
use super::scope::Scope;
use super::typ::*;
use super::universe::Universe;
use go_parser::{piggy_key_type, PiggyVec, Pos};
use std::borrow::Cow;

piggy_key_type! {
    pub struct ObjKey;
    pub struct TypeKey;
    pub struct PackageKey;
    pub struct DeclInfoKey;
    pub struct ScopeKey;
}

pub type LangObjs = PiggyVec<ObjKey, LangObj>;
pub type Types = PiggyVec<TypeKey, Type>;
pub type Packages = PiggyVec<PackageKey, Package>;
pub type Decls = PiggyVec<DeclInfoKey, DeclInfo>;
pub type Scopes = PiggyVec<ScopeKey, Scope>;

/// The container of all "managed" objects
/// also works as a "global" variable holder
pub struct TCObjects {
    pub lobjs: LangObjs,
    pub types: Types,
    pub pkgs: Packages,
    pub decls: Decls,
    pub scopes: Scopes,
    pub universe: Option<Universe>,
    // "global" variable
    pub fmt_qualifier: Box<dyn Fn(&Package) -> Cow<str>>,
}

fn default_fmt_qualifier(p: &Package) -> Cow<str> {
    p.path().into()
}

impl TCObjects {
    pub fn new() -> TCObjects {
        let fmtq = Box::new(default_fmt_qualifier);
        const CAP: usize = 16;
        let mut objs = TCObjects {
            lobjs: PiggyVec::with_capacity(CAP),
            types: PiggyVec::with_capacity(CAP),
            pkgs: PiggyVec::with_capacity(CAP),
            decls: PiggyVec::with_capacity(CAP),
            scopes: PiggyVec::with_capacity(CAP),
            universe: None,
            fmt_qualifier: fmtq,
        };
        objs.universe = Some(Universe::new(&mut objs));
        objs
    }

    pub fn universe(&self) -> &Universe {
        self.universe.as_ref().unwrap()
    }

    pub fn new_scope(
        &mut self,
        parent: Option<ScopeKey>,
        pos: Pos,
        end: Pos,
        comment: String,
        is_func: bool,
    ) -> ScopeKey {
        let scope = Scope::new(parent, pos, end, comment, is_func);
        let skey = self.scopes.insert(scope);
        if let Some(s) = parent {
            // don't add children to Universe scope
            if s != *self.universe().scope() {
                self.scopes[s].add_child(skey);
            }
        }
        skey
    }

    pub fn new_package(&mut self, path: String) -> PackageKey {
        let skey = self.new_scope(
            Some(*self.universe().scope()),
            0,
            0,
            format!("package {}", path),
            false,
        );
        let pkg = Package::new(path, None, skey);
        self.pkgs.insert(pkg)
    }

    pub fn new_pkg_name(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        imported: PackageKey,
    ) -> ObjKey {
        let lobj = LangObj::new_pkg_name(pos, pkg, name, imported, self.universe());
        self.lobjs.insert(lobj)
    }

    pub fn new_const(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
        val: constant::Value,
    ) -> ObjKey {
        let lobj = LangObj::new_const(pos, pkg, name, typ, val);
        self.lobjs.insert(lobj)
    }

    pub fn new_type_name(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
    ) -> ObjKey {
        let lobj = LangObj::new_type_name(pos, pkg, name, typ);
        self.lobjs.insert(lobj)
    }

    pub fn new_var(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
    ) -> ObjKey {
        let lobj = LangObj::new_var(pos, pkg, name, typ);
        self.lobjs.insert(lobj)
    }

    pub fn new_param_var(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
    ) -> ObjKey {
        let lobj = LangObj::new_param_var(pos, pkg, name, typ);
        self.lobjs.insert(lobj)
    }

    pub fn new_field(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
        embedded: bool,
    ) -> ObjKey {
        let lobj = LangObj::new_field(pos, pkg, name, typ, embedded);
        self.lobjs.insert(lobj)
    }

    pub fn new_func(
        &mut self,
        pos: Pos,
        pkg: Option<PackageKey>,
        name: String,
        typ: Option<TypeKey>,
    ) -> ObjKey {
        let lobj = LangObj::new_func(pos, pkg, name, typ);
        self.lobjs.insert(lobj)
    }

    pub fn new_label(&mut self, pos: Pos, pkg: Option<PackageKey>, name: String) -> ObjKey {
        let lobj = LangObj::new_label(pos, pkg, name, self.universe());
        self.lobjs.insert(lobj)
    }

    pub fn new_t_basic(&mut self, typ: BasicType, info: BasicInfo, name: &'static str) -> TypeKey {
        self.types
            .insert(Type::Basic(BasicDetail::new(typ, info, name)))
    }

    pub fn new_t_array(&mut self, elem: TypeKey, len: Option<u64>) -> TypeKey {
        self.types.insert(Type::Array(ArrayDetail::new(elem, len)))
    }

    pub fn new_t_slice(&mut self, elem: TypeKey) -> TypeKey {
        self.types.insert(Type::Slice(SliceDetail::new(elem)))
    }

    pub fn new_t_struct(
        &mut self,
        fields: Vec<ObjKey>,
        tags: Option<Vec<Option<String>>>,
    ) -> TypeKey {
        self.types
            .insert(Type::Struct(StructDetail::new(fields, tags, self)))
    }
    pub fn new_t_pointer(&mut self, base: TypeKey) -> TypeKey {
        self.types.insert(Type::Pointer(PointerDetail::new(base)))
    }

    pub fn new_t_tuple(&mut self, vars: Vec<ObjKey>) -> TypeKey {
        self.types.insert(Type::Tuple(TupleDetail::new(vars)))
    }

    pub fn new_t_signature(
        &mut self,
        scope: Option<ScopeKey>,
        recv: Option<ObjKey>,
        params: TypeKey,
        results: TypeKey,
        variadic: bool,
    ) -> TypeKey {
        self.types.insert(Type::Signature(SignatureDetail::new(
            scope, recv, params, results, variadic, self,
        )))
    }

    pub fn new_t_interface(&mut self, methods: Vec<ObjKey>, embeddeds: Vec<TypeKey>) -> TypeKey {
        let iface = Type::Interface(InterfaceDetail::new(methods, embeddeds, self));
        self.types.insert(iface)
    }

    pub fn new_t_empty_interface(&mut self) -> TypeKey {
        self.types
            .insert(Type::Interface(InterfaceDetail::new_empty()))
    }

    pub fn new_t_map(&mut self, key: TypeKey, elem: TypeKey) -> TypeKey {
        self.types.insert(Type::Map(MapDetail::new(key, elem)))
    }
    pub fn new_t_chan(&mut self, dir: ChanDir, elem: TypeKey) -> TypeKey {
        self.types.insert(Type::Chan(ChanDetail::new(dir, elem)))
    }
    pub fn new_t_named(
        &mut self,
        obj: Option<ObjKey>,
        underlying: Option<TypeKey>,
        methods: Vec<ObjKey>,
    ) -> TypeKey {
        self.types.insert(Type::Named(NamedDetail::new(
            obj, underlying, methods, self,
        )))
    }
}