1use std::fmt;
2use std::path::Path;
3
4use erg_common::error::Location;
5use erg_common::pathutil::NormalizedPathBuf;
6use erg_common::set::Set;
7use erg_common::traits::Immutable;
8use erg_common::{switch_lang, Str};
9
10use erg_parser::ast::DefId;
11
12use crate::context::{ContextKind, DefaultInfo};
13use crate::ty::{Field, HasType, Type, Visibility};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[repr(u8)]
17pub enum Mutability {
18    Immutable,
19    Const,
20}
21
22impl From<&str> for Mutability {
23    fn from(item: &str) -> Self {
24        if item.chars().next().is_some_and(|c| c.is_uppercase()) {
25            Self::Const
26        } else {
27            Self::Immutable
28        }
29    }
30}
31
32impl Mutability {
33    pub const fn is_const(&self) -> bool {
34        matches!(self, Self::Const)
35    }
36}
37
38use Mutability::*;
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub enum VarKind {
42    Defined(DefId),
43    Declared,
44    InstanceAttr,
45    Parameter {
46        def_id: DefId,
47        var: bool,
48        default: DefaultInfo,
49    },
50    Auto,
51    FixedAuto,
52    DoesNotExist,
53    Builtin,
54}
55
56impl VarKind {
57    pub const fn parameter(def_id: DefId, var: bool, default: DefaultInfo) -> Self {
58        Self::Parameter {
59            def_id,
60            var,
61            default,
62        }
63    }
64
65    pub const fn nd_parameter(def_id: DefId) -> Self {
66        Self::parameter(def_id, false, DefaultInfo::NonDefault)
67    }
68
69    pub const fn has_default(&self) -> bool {
70        match self {
71            Self::Parameter { default, .. } => default.has_default(),
72            _ => false,
73        }
74    }
75
76    pub const fn is_parameter(&self) -> bool {
77        matches!(self, Self::Parameter { .. })
78    }
79
80    pub const fn is_var_params(&self) -> bool {
81        match self {
82            Self::Parameter { var, .. } => *var,
83            _ => false,
84        }
85    }
86
87    pub const fn is_defined(&self) -> bool {
88        matches!(self, Self::Defined(_))
89    }
90
91    pub const fn can_capture(&self) -> bool {
92        matches!(
93            self,
94            Self::Defined(_) | Self::Declared | Self::Parameter { .. }
95        )
96    }
97
98    pub const fn does_not_exist(&self) -> bool {
99        matches!(self, Self::DoesNotExist)
100    }
101
102    pub const fn is_builtin(&self) -> bool {
103        matches!(self, Self::Builtin)
104    }
105
106    pub const fn is_auto(&self) -> bool {
107        matches!(self, Self::Auto)
108    }
109
110    pub const fn is_instance_attr(&self) -> bool {
111        matches!(self, Self::InstanceAttr)
112    }
113
114    pub const fn display(&self) -> &'static str {
115        match self {
116            Self::Auto | Self::FixedAuto => switch_lang!(
117                "japanese" => "自動",
118                "simplified_chinese" => "自动",
119                "traditional_chinese" => "自動",
120                "english" => "auto",
121            ),
122            Self::Builtin => switch_lang!(
123                "japanese" => "組み込み",
124                "simplified_chinese" => "内置",
125                "traditional_chinese" => "內置",
126                "english" => "builtin",
127            ),
128            Self::InstanceAttr => switch_lang!(
129                "japanese" => "インスタンス",
130                "simplified_chinese" => "实例",
131                "traditional_chinese" => "實例",
132                "english" => "instance",
133            ),
134            _ => "",
135        }
136    }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub struct AbsLocation {
141    pub module: Option<NormalizedPathBuf>,
142    pub loc: Location,
143}
144
145impl Immutable for AbsLocation {}
146
147impl fmt::Display for AbsLocation {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        if let Some(module) = &self.module {
150            write!(f, "{}@{}", module.display(), self.loc)
151        } else {
152            write!(f, "?@{}", self.loc)
153        }
154    }
155}
156
157impl std::str::FromStr for AbsLocation {
158    type Err = ();
159
160    fn from_str(s: &str) -> Result<Self, Self::Err> {
161        let mut split = s.split('@');
162        let module = split.next().map(NormalizedPathBuf::from);
163        let loc = split.next().ok_or(())?.parse().map_err(|_| ())?;
164        Ok(Self { module, loc })
165    }
166}
167
168impl AbsLocation {
169    pub const fn new(module: Option<NormalizedPathBuf>, loc: Location) -> Self {
170        Self { module, loc }
171    }
172
173    pub const fn unknown() -> Self {
174        Self::new(None, Location::Unknown)
175    }
176
177    pub fn is_unknown(&self) -> bool {
178        self.module.is_none() && self.loc.is_unknown()
179    }
180
181    pub fn is_real(&self) -> bool {
182        self.module.is_some() && self.loc.is_real()
183    }
184
185    pub fn starts_with(&self, path: impl AsRef<Path>) -> bool {
186        self.module.as_ref().is_some_and(|p| p.starts_with(path))
187    }
188
189    pub fn code(&self) -> Option<String> {
190        use std::io::{BufRead, BufReader};
191        self.module.as_ref().and_then(|module| {
192            let file = std::fs::File::open(module).ok()?;
193            let reader = BufReader::new(file);
194            reader
195                .lines()
196                .nth(
197                    self.loc
198                        .ln_begin()
199                        .map(|l| l.saturating_sub(1))
200                        .unwrap_or(0) as usize,
201                )
202                .and_then(|res| {
203                    let res = res.ok()?;
204                    let begin = self.loc.col_begin().unwrap_or(0) as usize;
205                    let end = self.loc.col_end().unwrap_or(0) as usize;
206                    if begin > res.len() || end > res.len() || begin > end {
207                        return None;
208                    }
209                    let end = end.min(res.len());
210                    let res = res[begin..end].to_string();
211                    Some(res)
212                })
213        })
214    }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Hash)]
218pub struct AliasInfo {
219    pub name: Str,
220    pub loc: AbsLocation,
221}
222
223impl AliasInfo {
224    pub const fn new(name: Str, loc: AbsLocation) -> Self {
225        Self { name, loc }
226    }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, Hash)]
231pub struct VarInfo {
232    pub t: Type,
233    pub muty: Mutability,
234    pub vis: Visibility,
235    pub kind: VarKind,
236    pub comptime_decos: Option<Set<Str>>,
237    pub ctx: ContextKind,
238    pub py_name: Option<Str>,
239    pub def_loc: AbsLocation,
240    pub alias_of: Option<Box<AliasInfo>>,
241}
242
243impl fmt::Display for VarInfo {
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245        write!(
246            f,
247            "VarInfo{{t: {}, muty: {:?}, vis: {:?}, kind: {:?}, py_name: {:?}, def_loc: {} }}",
248            self.t, self.muty, self.vis, self.kind, self.py_name, self.def_loc
249        )
250    }
251}
252
253impl HasType for VarInfo {
254    #[inline]
255    fn ref_t(&self) -> &Type {
256        &self.t
257    }
258    #[inline]
259    fn ref_mut_t(&mut self) -> Option<&mut Type> {
260        Some(&mut self.t)
261    }
262    #[inline]
263    fn signature_t(&self) -> Option<&Type> {
264        None
265    }
266    #[inline]
267    fn signature_mut_t(&mut self) -> Option<&mut Type> {
268        None
269    }
270}
271
272impl Default for VarInfo {
273    fn default() -> Self {
274        Self::const_default_private()
275    }
276}
277
278impl VarInfo {
279    pub const ILLEGAL: Self = Self::const_default_private();
280
281    pub const fn const_default_private() -> Self {
282        Self::new(
283            Type::Failure,
284            Immutable,
285            Visibility::DUMMY_PRIVATE,
286            VarKind::DoesNotExist,
287            None,
288            ContextKind::Dummy,
289            None,
290            AbsLocation::unknown(),
291        )
292    }
293
294    pub const fn const_default_public() -> Self {
295        Self::new(
296            Type::Failure,
297            Immutable,
298            Visibility::DUMMY_PUBLIC,
299            VarKind::DoesNotExist,
300            None,
301            ContextKind::Dummy,
302            None,
303            AbsLocation::unknown(),
304        )
305    }
306
307    #[allow(clippy::too_many_arguments)]
308    pub const fn new(
309        t: Type,
310        muty: Mutability,
311        vis: Visibility,
312        kind: VarKind,
313        comptime_decos: Option<Set<Str>>,
314        ctx: ContextKind,
315        py_name: Option<Str>,
316        def_loc: AbsLocation,
317    ) -> Self {
318        Self {
319            t,
320            muty,
321            vis,
322            kind,
323            comptime_decos,
324            ctx,
325            py_name,
326            def_loc,
327            alias_of: None,
328        }
329    }
330
331    #[allow(clippy::too_many_arguments)]
332    pub fn maybe_alias(
333        t: Type,
334        muty: Mutability,
335        vis: Visibility,
336        kind: VarKind,
337        comptime_decos: Option<Set<Str>>,
338        ctx: ContextKind,
339        py_name: Option<Str>,
340        def_loc: AbsLocation,
341        alias_of: Option<AliasInfo>,
342    ) -> Self {
343        Self {
344            t,
345            muty,
346            vis,
347            kind,
348            comptime_decos,
349            ctx,
350            py_name,
351            def_loc,
352            alias_of: alias_of.map(Box::new),
353        }
354    }
355
356    pub fn same_id_as(&self, id: DefId) -> bool {
357        match self.kind {
358            VarKind::Defined(i) | VarKind::Parameter { def_id: i, .. } => id == i,
359            _ => false,
360        }
361    }
362
363    pub fn nd_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
364        let kind = VarKind::Parameter {
365            def_id: DefId(0),
366            var: false,
367            default: DefaultInfo::NonDefault,
368        };
369        Self::new(
370            t,
371            Immutable,
372            Visibility::private(namespace),
373            kind,
374            None,
375            ContextKind::Func,
376            None,
377            def_loc,
378        )
379    }
380
381    pub fn d_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
382        let kind = VarKind::Parameter {
383            def_id: DefId(0),
384            var: false,
385            default: DefaultInfo::WithDefault,
386        };
387        Self::new(
388            t,
389            Immutable,
390            Visibility::private(namespace),
391            kind,
392            None,
393            ContextKind::Func,
394            None,
395            def_loc,
396        )
397    }
398
399    pub fn instance_attr(
400        field: Field,
401        t: Type,
402        kind: ContextKind,
403        namespace: Str,
404        loc: AbsLocation,
405    ) -> Self {
406        let muty = if field.is_const() {
407            Mutability::Const
408        } else {
409            Mutability::Immutable
410        };
411        Self::new(
412            t,
413            muty,
414            Visibility::new(field.vis, namespace),
415            VarKind::InstanceAttr,
416            None,
417            kind,
418            None,
419            loc,
420        )
421    }
422
423    pub fn type_var(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
424        Self::new(
425            t,
426            Const,
427            Visibility::private(namespace),
428            VarKind::Declared,
429            None,
430            ContextKind::Dummy,
431            None,
432            def_loc,
433        )
434    }
435
436    pub fn record_field(t: Type, def_loc: AbsLocation, vis: Visibility) -> Self {
437        Self::new(
438            t,
439            Immutable,
440            vis,
441            VarKind::Declared,
442            None,
443            ContextKind::Dummy,
444            None,
445            def_loc,
446        )
447    }
448
449    pub fn is_ambiguously_typed_parameter(&self) -> bool {
450        self.kind.is_parameter()
451            && self
452                .t
453                .get_super()
454                .is_some_and(|sup| sup == Type::Obj || sup.is_structural())
455    }
456
457    pub const fn is_parameter(&self) -> bool {
458        self.kind.is_parameter()
459    }
460
461    pub fn impl_of(&self) -> Option<&Type> {
462        match &self.ctx {
463            ContextKind::MethodDefs(ty) => ty.as_ref(),
464            _ => None,
465        }
466    }
467
468    pub fn def_namespace(&self) -> &Str {
469        &self.vis.def_namespace
470    }
471
472    pub fn is_toplevel(&self) -> bool {
473        let ns = Str::rc(self.vis.def_namespace.trim_start_matches("./"));
474        ns.split_with(&[".", "::"]).len() == 1
475    }
476
477    pub fn is_fast_value(&self) -> bool {
478        !self.is_toplevel()
479            && !self.is_parameter()
480            && self.ctx.control_kind().is_none()
481            && (self.ctx.is_subr() || self.ctx.is_instant())
482    }
483}