mirror of
https://github.com/erg-lang/erg.git
synced 2025-07-07 21:25:31 +00:00
483 lines
12 KiB
Rust
483 lines
12 KiB
Rust
use std::fmt;
|
|
use std::path::Path;
|
|
|
|
use erg_common::error::Location;
|
|
use erg_common::pathutil::NormalizedPathBuf;
|
|
use erg_common::set::Set;
|
|
use erg_common::traits::Immutable;
|
|
use erg_common::{switch_lang, Str};
|
|
|
|
use erg_parser::ast::DefId;
|
|
|
|
use crate::context::{ContextKind, DefaultInfo};
|
|
use crate::ty::{Field, HasType, Type, Visibility};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
#[repr(u8)]
|
|
pub enum Mutability {
|
|
Immutable,
|
|
Const,
|
|
}
|
|
|
|
impl From<&str> for Mutability {
|
|
fn from(item: &str) -> Self {
|
|
if item.chars().next().is_some_and(|c| c.is_uppercase()) {
|
|
Self::Const
|
|
} else {
|
|
Self::Immutable
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mutability {
|
|
pub const fn is_const(&self) -> bool {
|
|
matches!(self, Self::Const)
|
|
}
|
|
}
|
|
|
|
use Mutability::*;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum VarKind {
|
|
Defined(DefId),
|
|
Declared,
|
|
InstanceAttr,
|
|
Parameter {
|
|
def_id: DefId,
|
|
var: bool,
|
|
default: DefaultInfo,
|
|
},
|
|
Auto,
|
|
FixedAuto,
|
|
DoesNotExist,
|
|
Builtin,
|
|
}
|
|
|
|
impl VarKind {
|
|
pub const fn parameter(def_id: DefId, var: bool, default: DefaultInfo) -> Self {
|
|
Self::Parameter {
|
|
def_id,
|
|
var,
|
|
default,
|
|
}
|
|
}
|
|
|
|
pub const fn nd_parameter(def_id: DefId) -> Self {
|
|
Self::parameter(def_id, false, DefaultInfo::NonDefault)
|
|
}
|
|
|
|
pub const fn has_default(&self) -> bool {
|
|
match self {
|
|
Self::Parameter { default, .. } => default.has_default(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub const fn is_parameter(&self) -> bool {
|
|
matches!(self, Self::Parameter { .. })
|
|
}
|
|
|
|
pub const fn is_var_params(&self) -> bool {
|
|
match self {
|
|
Self::Parameter { var, .. } => *var,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub const fn is_defined(&self) -> bool {
|
|
matches!(self, Self::Defined(_))
|
|
}
|
|
|
|
pub const fn can_capture(&self) -> bool {
|
|
matches!(
|
|
self,
|
|
Self::Defined(_) | Self::Declared | Self::Parameter { .. }
|
|
)
|
|
}
|
|
|
|
pub const fn does_not_exist(&self) -> bool {
|
|
matches!(self, Self::DoesNotExist)
|
|
}
|
|
|
|
pub const fn is_builtin(&self) -> bool {
|
|
matches!(self, Self::Builtin)
|
|
}
|
|
|
|
pub const fn is_auto(&self) -> bool {
|
|
matches!(self, Self::Auto)
|
|
}
|
|
|
|
pub const fn is_instance_attr(&self) -> bool {
|
|
matches!(self, Self::InstanceAttr)
|
|
}
|
|
|
|
pub const fn display(&self) -> &'static str {
|
|
match self {
|
|
Self::Auto | Self::FixedAuto => switch_lang!(
|
|
"japanese" => "自動",
|
|
"simplified_chinese" => "自动",
|
|
"traditional_chinese" => "自動",
|
|
"english" => "auto",
|
|
),
|
|
Self::Builtin => switch_lang!(
|
|
"japanese" => "組み込み",
|
|
"simplified_chinese" => "内置",
|
|
"traditional_chinese" => "內置",
|
|
"english" => "builtin",
|
|
),
|
|
Self::InstanceAttr => switch_lang!(
|
|
"japanese" => "インスタンス",
|
|
"simplified_chinese" => "实例",
|
|
"traditional_chinese" => "實例",
|
|
"english" => "instance",
|
|
),
|
|
_ => "",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct AbsLocation {
|
|
pub module: Option<NormalizedPathBuf>,
|
|
pub loc: Location,
|
|
}
|
|
|
|
impl Immutable for AbsLocation {}
|
|
|
|
impl fmt::Display for AbsLocation {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
if let Some(module) = &self.module {
|
|
write!(f, "{}@{}", module.display(), self.loc)
|
|
} else {
|
|
write!(f, "?@{}", self.loc)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for AbsLocation {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let mut split = s.split('@');
|
|
let module = split.next().map(NormalizedPathBuf::from);
|
|
let loc = split.next().ok_or(())?.parse().map_err(|_| ())?;
|
|
Ok(Self { module, loc })
|
|
}
|
|
}
|
|
|
|
impl AbsLocation {
|
|
pub const fn new(module: Option<NormalizedPathBuf>, loc: Location) -> Self {
|
|
Self { module, loc }
|
|
}
|
|
|
|
pub const fn unknown() -> Self {
|
|
Self::new(None, Location::Unknown)
|
|
}
|
|
|
|
pub fn is_unknown(&self) -> bool {
|
|
self.module.is_none() && self.loc.is_unknown()
|
|
}
|
|
|
|
pub fn is_real(&self) -> bool {
|
|
self.module.is_some() && self.loc.is_real()
|
|
}
|
|
|
|
pub fn starts_with(&self, path: impl AsRef<Path>) -> bool {
|
|
self.module.as_ref().is_some_and(|p| p.starts_with(path))
|
|
}
|
|
|
|
pub fn code(&self) -> Option<String> {
|
|
use std::io::{BufRead, BufReader};
|
|
self.module.as_ref().and_then(|module| {
|
|
let file = std::fs::File::open(module).ok()?;
|
|
let reader = BufReader::new(file);
|
|
reader
|
|
.lines()
|
|
.nth(
|
|
self.loc
|
|
.ln_begin()
|
|
.map(|l| l.saturating_sub(1))
|
|
.unwrap_or(0) as usize,
|
|
)
|
|
.and_then(|res| {
|
|
let res = res.ok()?;
|
|
let begin = self.loc.col_begin().unwrap_or(0) as usize;
|
|
let end = self.loc.col_end().unwrap_or(0) as usize;
|
|
if begin > res.len() || end > res.len() || begin > end {
|
|
return None;
|
|
}
|
|
let end = end.min(res.len());
|
|
let res = res[begin..end].to_string();
|
|
Some(res)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct AliasInfo {
|
|
pub name: Str,
|
|
pub loc: AbsLocation,
|
|
}
|
|
|
|
impl AliasInfo {
|
|
pub const fn new(name: Str, loc: AbsLocation) -> Self {
|
|
Self { name, loc }
|
|
}
|
|
}
|
|
|
|
/// Has information about the type, variability, visibility, and where the variable was defined (or declared, generated)
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct VarInfo {
|
|
pub t: Type,
|
|
pub muty: Mutability,
|
|
pub vis: Visibility,
|
|
pub kind: VarKind,
|
|
pub comptime_decos: Option<Set<Str>>,
|
|
pub ctx: ContextKind,
|
|
pub py_name: Option<Str>,
|
|
pub def_loc: AbsLocation,
|
|
pub alias_of: Option<AliasInfo>,
|
|
}
|
|
|
|
impl fmt::Display for VarInfo {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"VarInfo{{t: {}, muty: {:?}, vis: {:?}, kind: {:?}, py_name: {:?}, def_loc: {} }}",
|
|
self.t, self.muty, self.vis, self.kind, self.py_name, self.def_loc
|
|
)
|
|
}
|
|
}
|
|
|
|
impl HasType for VarInfo {
|
|
#[inline]
|
|
fn ref_t(&self) -> &Type {
|
|
&self.t
|
|
}
|
|
#[inline]
|
|
fn ref_mut_t(&mut self) -> Option<&mut Type> {
|
|
Some(&mut self.t)
|
|
}
|
|
#[inline]
|
|
fn signature_t(&self) -> Option<&Type> {
|
|
None
|
|
}
|
|
#[inline]
|
|
fn signature_mut_t(&mut self) -> Option<&mut Type> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl Default for VarInfo {
|
|
fn default() -> Self {
|
|
Self::const_default_private()
|
|
}
|
|
}
|
|
|
|
impl VarInfo {
|
|
pub const ILLEGAL: Self = Self::const_default_private();
|
|
|
|
pub const fn const_default_private() -> Self {
|
|
Self::new(
|
|
Type::Failure,
|
|
Immutable,
|
|
Visibility::DUMMY_PRIVATE,
|
|
VarKind::DoesNotExist,
|
|
None,
|
|
ContextKind::Dummy,
|
|
None,
|
|
AbsLocation::unknown(),
|
|
)
|
|
}
|
|
|
|
pub const fn const_default_public() -> Self {
|
|
Self::new(
|
|
Type::Failure,
|
|
Immutable,
|
|
Visibility::DUMMY_PUBLIC,
|
|
VarKind::DoesNotExist,
|
|
None,
|
|
ContextKind::Dummy,
|
|
None,
|
|
AbsLocation::unknown(),
|
|
)
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub const fn new(
|
|
t: Type,
|
|
muty: Mutability,
|
|
vis: Visibility,
|
|
kind: VarKind,
|
|
comptime_decos: Option<Set<Str>>,
|
|
ctx: ContextKind,
|
|
py_name: Option<Str>,
|
|
def_loc: AbsLocation,
|
|
) -> Self {
|
|
Self {
|
|
t,
|
|
muty,
|
|
vis,
|
|
kind,
|
|
comptime_decos,
|
|
ctx,
|
|
py_name,
|
|
def_loc,
|
|
alias_of: None,
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub const fn maybe_alias(
|
|
t: Type,
|
|
muty: Mutability,
|
|
vis: Visibility,
|
|
kind: VarKind,
|
|
comptime_decos: Option<Set<Str>>,
|
|
ctx: ContextKind,
|
|
py_name: Option<Str>,
|
|
def_loc: AbsLocation,
|
|
alias_of: Option<AliasInfo>,
|
|
) -> Self {
|
|
Self {
|
|
t,
|
|
muty,
|
|
vis,
|
|
kind,
|
|
comptime_decos,
|
|
ctx,
|
|
py_name,
|
|
def_loc,
|
|
alias_of,
|
|
}
|
|
}
|
|
|
|
pub fn same_id_as(&self, id: DefId) -> bool {
|
|
match self.kind {
|
|
VarKind::Defined(i) | VarKind::Parameter { def_id: i, .. } => id == i,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn nd_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
|
|
let kind = VarKind::Parameter {
|
|
def_id: DefId(0),
|
|
var: false,
|
|
default: DefaultInfo::NonDefault,
|
|
};
|
|
Self::new(
|
|
t,
|
|
Immutable,
|
|
Visibility::private(namespace),
|
|
kind,
|
|
None,
|
|
ContextKind::Func,
|
|
None,
|
|
def_loc,
|
|
)
|
|
}
|
|
|
|
pub fn d_parameter(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
|
|
let kind = VarKind::Parameter {
|
|
def_id: DefId(0),
|
|
var: false,
|
|
default: DefaultInfo::WithDefault,
|
|
};
|
|
Self::new(
|
|
t,
|
|
Immutable,
|
|
Visibility::private(namespace),
|
|
kind,
|
|
None,
|
|
ContextKind::Func,
|
|
None,
|
|
def_loc,
|
|
)
|
|
}
|
|
|
|
pub fn instance_attr(
|
|
field: Field,
|
|
t: Type,
|
|
kind: ContextKind,
|
|
namespace: Str,
|
|
loc: AbsLocation,
|
|
) -> Self {
|
|
let muty = if field.is_const() {
|
|
Mutability::Const
|
|
} else {
|
|
Mutability::Immutable
|
|
};
|
|
Self::new(
|
|
t,
|
|
muty,
|
|
Visibility::new(field.vis, namespace),
|
|
VarKind::InstanceAttr,
|
|
None,
|
|
kind,
|
|
None,
|
|
loc,
|
|
)
|
|
}
|
|
|
|
pub fn type_var(t: Type, def_loc: AbsLocation, namespace: Str) -> Self {
|
|
Self::new(
|
|
t,
|
|
Const,
|
|
Visibility::private(namespace),
|
|
VarKind::Declared,
|
|
None,
|
|
ContextKind::Dummy,
|
|
None,
|
|
def_loc,
|
|
)
|
|
}
|
|
|
|
pub fn record_field(t: Type, def_loc: AbsLocation, vis: Visibility) -> Self {
|
|
Self::new(
|
|
t,
|
|
Immutable,
|
|
vis,
|
|
VarKind::Declared,
|
|
None,
|
|
ContextKind::Dummy,
|
|
None,
|
|
def_loc,
|
|
)
|
|
}
|
|
|
|
pub fn is_ambiguously_typed_parameter(&self) -> bool {
|
|
self.kind.is_parameter()
|
|
&& self
|
|
.t
|
|
.get_super()
|
|
.is_some_and(|sup| sup == Type::Obj || sup.is_structural())
|
|
}
|
|
|
|
pub const fn is_parameter(&self) -> bool {
|
|
self.kind.is_parameter()
|
|
}
|
|
|
|
pub fn impl_of(&self) -> Option<&Type> {
|
|
match &self.ctx {
|
|
ContextKind::MethodDefs(ty) => ty.as_ref(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn def_namespace(&self) -> &Str {
|
|
&self.vis.def_namespace
|
|
}
|
|
|
|
pub fn is_toplevel(&self) -> bool {
|
|
let ns = Str::rc(self.vis.def_namespace.trim_start_matches("./"));
|
|
ns.split_with(&[".", "::"]).len() == 1
|
|
}
|
|
|
|
pub fn is_fast_value(&self) -> bool {
|
|
!self.is_toplevel()
|
|
&& !self.is_parameter()
|
|
&& self.ctx.control_kind().is_none()
|
|
&& (self.ctx.is_subr() || self.ctx.is_instant())
|
|
}
|
|
}
|