Rename dir: compiler -> crates

This commit is contained in:
Shunsuke Shibayama 2023-01-15 12:03:19 +09:00
parent e1004b538d
commit a127564b31
221 changed files with 17 additions and 19 deletions

18
crates/erg_compiler/.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
*.pyc
/.vscode/
/.VSCodeCounter/
/.vs/
/.DS_Store
/*/.DS_Store
/.idea/
/timeit.dat

View file

@ -0,0 +1,52 @@
[package]
name = "erg_compiler"
description = "Centimetre: the Erg compiler"
documentation = "http://docs.rs/erg_compiler"
build = "build.rs"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
homepage.workspace = true
[features]
# when "debug" feature is turned on, that of parser will also be turned on.
debug = ["erg_common/debug", "erg_parser/debug"]
japanese = ["erg_common/japanese", "erg_parser/japanese"]
simplified_chinese = [
"erg_common/simplified_chinese",
"erg_parser/simplified_chinese",
]
traditional_chinese = [
"erg_common/traditional_chinese",
"erg_parser/traditional_chinese",
]
unicode = [
"erg_common/unicode",
"erg_parser/unicode",
]
pretty = [
"erg_common/pretty",
"erg_parser/pretty"
]
large_thread = [
"erg_common/large_thread",
"erg_parser/large_thread",
]
py_compatible = ["erg_common/py_compatible"]
els = ["erg_common/els"]
[dependencies]
erg_common = { workspace = true, path = "../erg_common" }
erg_parser = { workspace = true, path = "../erg_parser" }
[build-dependencies]
erg_common = { workspace = true, path = "../erg_common" }
[lib]
path = "lib.rs"
[[bin]]
name = "cm"
path = "main.rs"

View file

@ -0,0 +1,3 @@
# The Erg compiler (codename: Centimetre)
The overall structure is described in detail in [architecture.md(English)](../../doc/EN/compiler/architecture.md).For other language translations of architecture.md, please check them out by yourself.

View file

@ -0,0 +1,96 @@
use std::fmt;
use erg_common::config::ErgConfig;
use erg_common::traits::{Runnable, Stream};
use crate::context::ModuleContext;
use crate::error::CompileErrors;
use crate::hir::HIR;
use crate::module::SharedCompilerResource;
#[derive(Debug)]
pub struct CompleteArtifact<Inner = HIR> {
pub object: Inner,
pub warns: CompileErrors,
}
impl<Inner> CompleteArtifact<Inner> {
pub const fn new(object: Inner, warns: CompileErrors) -> Self {
Self { object, warns }
}
}
#[derive(Debug)]
pub struct IncompleteArtifact<Inner = HIR> {
pub object: Option<Inner>,
pub errors: CompileErrors,
pub warns: CompileErrors,
}
impl fmt::Display for IncompleteArtifact {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.warns.is_empty() {
writeln!(f, "{}", self.warns)?;
}
write!(f, "{}", self.errors)
}
}
impl std::error::Error for IncompleteArtifact {}
impl<Inner> IncompleteArtifact<Inner> {
pub const fn new(object: Option<Inner>, errors: CompileErrors, warns: CompileErrors) -> Self {
Self {
object,
errors,
warns,
}
}
}
#[derive(Debug)]
pub struct ErrorArtifact {
pub errors: CompileErrors,
pub warns: CompileErrors,
}
impl fmt::Display for ErrorArtifact {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.warns.is_empty() {
writeln!(f, "{}", self.warns)?;
}
write!(f, "{}", self.errors)
}
}
impl std::error::Error for ErrorArtifact {}
impl From<IncompleteArtifact> for ErrorArtifact {
fn from(artifact: IncompleteArtifact) -> Self {
Self {
errors: artifact.errors,
warns: artifact.warns,
}
}
}
impl ErrorArtifact {
pub const fn new(errors: CompileErrors, warns: CompileErrors) -> Self {
Self { errors, warns }
}
}
pub trait Buildable<T = HIR> {
fn inherit(cfg: ErgConfig, shared: SharedCompilerResource) -> Self
where
Self: Sized;
fn build(
&mut self,
src: String,
mode: &str,
) -> Result<CompleteArtifact<T>, IncompleteArtifact<T>>;
fn pop_context(&mut self) -> Option<ModuleContext>;
fn get_context(&self) -> Option<&ModuleContext>;
}
pub trait BuildRunnable<T = HIR>: Buildable<T> + Runnable {}

View file

@ -0,0 +1,62 @@
#![allow(deprecated)]
use std::env;
use std::fs;
use std::path;
use erg_common::erg_util::env_erg_version;
fn main() -> std::io::Result<()> {
let current_erg_version = env_erg_version();
if current_erg_version.as_ref().map(|s| &s[..]) == Some(env!("CARGO_PKG_VERSION"))
&& env::var("ERG_PATH").is_ok()
&& !cfg!(feature = "debug")
{
return Ok(());
}
// Create a ".erg" directory
let erg_path = env::home_dir()
.expect("failed to get the location of the home dir")
.to_str()
.expect("invalid encoding of the home dir name")
.to_string()
+ "/.erg";
if !path::Path::new(&erg_path).exists() {
fs::create_dir(&erg_path).unwrap_or_else(|_| {
eprintln!("failed to create the directory: {erg_path}");
});
}
println!("cargo:rustc-env=CARGO_ERG_PATH={erg_path}");
// create a std library in ".erg"
copy_dir(&erg_path, "lib").unwrap_or_else(|_| {
eprintln!("failed to copy the std library to {erg_path}");
});
Ok(())
}
fn copy_dir(erg_path: &str, path: &str) -> std::io::Result<()> {
let full_path = format!("{erg_path}/{path}");
if !path::Path::new(&full_path).exists() {
fs::create_dir(&full_path)?;
}
let mut dirs = vec![];
for res in fs::read_dir(path)? {
let entry = res?;
let entry_path = entry.path();
if entry_path.is_dir() {
dirs.push(entry);
} else {
let filename = entry_path
.file_name()
.expect("this is not a file")
.to_str()
.unwrap();
let filename = format!("{full_path}/{filename}");
fs::copy(&entry_path, filename)?;
}
}
for dir in dirs {
copy_dir(erg_path, dir.path().to_str().unwrap())?;
}
Ok(())
}

View file

@ -0,0 +1,171 @@
use erg_common::config::ErgConfig;
use erg_common::error::MultiErrorDisplay;
use erg_common::traits::Runnable;
use erg_common::Str;
use erg_parser::ast::{VarName, AST};
use erg_parser::build_ast::ASTBuilder;
use crate::artifact::{BuildRunnable, Buildable, CompleteArtifact, IncompleteArtifact};
use crate::context::{Context, ContextProvider, ModuleContext};
use crate::effectcheck::SideEffectChecker;
use crate::error::{CompileError, CompileErrors};
use crate::lower::ASTLowerer;
use crate::module::SharedCompilerResource;
use crate::ownercheck::OwnershipChecker;
use crate::varinfo::VarInfo;
/// Summarize lowering, side-effect checking, and ownership checking
#[derive(Debug)]
pub struct HIRBuilder {
lowerer: ASTLowerer,
ownership_checker: OwnershipChecker,
}
impl Default for HIRBuilder {
fn default() -> Self {
HIRBuilder::new(ErgConfig::default())
}
}
impl Runnable for HIRBuilder {
type Err = CompileError;
type Errs = CompileErrors;
const NAME: &'static str = "Erg HIR builder";
fn new(cfg: ErgConfig) -> Self {
HIRBuilder::new_with_cache(
cfg.copy(),
Str::ever("<module>"),
SharedCompilerResource::new(cfg),
)
}
#[inline]
fn cfg(&self) -> &ErgConfig {
self.lowerer.cfg()
}
#[inline]
fn cfg_mut(&mut self) -> &mut ErgConfig {
self.lowerer.cfg_mut()
}
#[inline]
fn finish(&mut self) {}
fn initialize(&mut self) {
self.lowerer.initialize();
self.ownership_checker = OwnershipChecker::new(self.cfg().copy());
}
fn clear(&mut self) {
self.lowerer.clear();
// don't initialize the ownership checker
}
fn exec(&mut self) -> Result<i32, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(self.input().read())?;
let artifact = self.check(ast, "exec").map_err(|arti| arti.errors)?;
artifact.warns.fmt_all_stderr();
println!("{}", artifact.object);
Ok(0)
}
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(src)?;
let artifact = self.check(ast, "eval").map_err(|arti| arti.errors)?;
artifact.warns.fmt_all_stderr();
Ok(artifact.object.to_string())
}
}
impl Buildable for HIRBuilder {
fn inherit(cfg: ErgConfig, shared: SharedCompilerResource) -> Self {
let mod_name = Str::rc(cfg.input.file_stem());
Self::new_with_cache(cfg, mod_name, shared)
}
fn build(&mut self, src: String, mode: &str) -> Result<CompleteArtifact, IncompleteArtifact> {
self.build(src, mode)
}
fn pop_context(&mut self) -> Option<ModuleContext> {
self.pop_mod_ctx()
}
fn get_context(&self) -> Option<&ModuleContext> {
Some(&self.lowerer.module)
}
}
impl BuildRunnable for HIRBuilder {}
impl ContextProvider for HIRBuilder {
fn dir(&self) -> Vec<(&VarName, &VarInfo)> {
self.lowerer.dir()
}
fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
self.lowerer.get_receiver_ctx(receiver_name)
}
fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
self.lowerer.get_var_info(name)
}
}
impl HIRBuilder {
pub fn new_with_cache<S: Into<Str>>(
cfg: ErgConfig,
mod_name: S,
shared: SharedCompilerResource,
) -> Self {
Self {
lowerer: ASTLowerer::new_with_cache(cfg.copy(), mod_name, shared),
ownership_checker: OwnershipChecker::new(cfg),
}
}
pub fn check(&mut self, ast: AST, mode: &str) -> Result<CompleteArtifact, IncompleteArtifact> {
let artifact = self.lowerer.lower(ast, mode)?;
let effect_checker = SideEffectChecker::new(self.cfg().clone());
let hir = effect_checker
.check(artifact.object)
.map_err(|(hir, errs)| {
self.lowerer.module.context.clear_invalid_vars();
IncompleteArtifact::new(Some(hir), errs, artifact.warns.clone())
})?;
let hir = self.ownership_checker.check(hir).map_err(|(hir, errs)| {
self.lowerer.module.context.clear_invalid_vars();
IncompleteArtifact::new(Some(hir), errs, artifact.warns.clone())
})?;
Ok(CompleteArtifact::new(hir, artifact.warns))
}
pub fn build(
&mut self,
src: String,
mode: &str,
) -> Result<CompleteArtifact, IncompleteArtifact> {
let mut ast_builder = ASTBuilder::new(self.cfg().copy());
let ast = ast_builder.build(src).map_err(|errs| {
IncompleteArtifact::new(None, CompileErrors::from(errs), CompileErrors::empty())
})?;
self.check(ast, mode)
}
pub fn pop_mod_ctx(&mut self) -> Option<ModuleContext> {
self.lowerer.pop_mod_ctx()
}
pub fn dir(&mut self) -> Vec<(&VarName, &VarInfo)> {
ContextProvider::dir(self)
}
pub fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
ContextProvider::get_receiver_ctx(self, receiver_name)
}
pub fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
ContextProvider::get_var_info(self, name)
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,254 @@
//! defines `Compiler`.
//!
//! コンパイラーを定義する
use std::path::Path;
use erg_common::config::ErgConfig;
use erg_common::error::MultiErrorDisplay;
use erg_common::log;
use erg_common::traits::{Runnable, Stream};
use crate::artifact::{CompleteArtifact, ErrorArtifact};
use crate::context::ContextProvider;
use crate::ty::codeobj::CodeObj;
use crate::build_hir::HIRBuilder;
use crate::codegen::PyCodeGenerator;
use crate::desugar_hir::HIRDesugarer;
use crate::error::{CompileError, CompileErrors, CompileWarnings};
use crate::hir::Expr;
use crate::link::Linker;
use crate::module::{SharedCompilerResource, SharedModuleCache};
/// * registered as global -> Global
/// * defined in the toplevel scope (and called in the inner scope) -> Global
/// * defined and called in the toplevel scope -> Local
/// * not defined in the toplevel and called in the inner scope -> Deref
/// * defined and called in the current scope (except the toplevel) -> Fast
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoreLoadKind {
Local,
LocalConst,
Global,
GlobalConst,
Deref,
DerefConst,
Fast,
FastConst,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Name {
pub kind: StoreLoadKind,
pub idx: usize,
}
impl Name {
pub const fn new(kind: StoreLoadKind, idx: usize) -> Self {
Self { kind, idx }
}
pub const fn local(idx: usize) -> Self {
Self {
kind: StoreLoadKind::Local,
idx,
}
}
pub const fn global(idx: usize) -> Self {
Self {
kind: StoreLoadKind::Global,
idx,
}
}
pub const fn deref(idx: usize) -> Self {
Self {
kind: StoreLoadKind::Deref,
idx,
}
}
pub const fn fast(idx: usize) -> Self {
Self {
kind: StoreLoadKind::Fast,
idx,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessKind {
Name,
Attr,
Method,
}
impl AccessKind {
pub const fn is_local(&self) -> bool {
matches!(self, Self::Name)
}
pub const fn is_attr(&self) -> bool {
matches!(self, Self::Attr)
}
pub const fn is_method(&self) -> bool {
matches!(self, Self::Method)
}
}
/// Generates a `CodeObj` from an String or other File inputs.
#[derive(Debug)]
pub struct Compiler {
pub cfg: ErgConfig,
builder: HIRBuilder,
mod_cache: SharedModuleCache,
code_generator: PyCodeGenerator,
}
impl Default for Compiler {
fn default() -> Self {
Self::new(ErgConfig::default())
}
}
impl Runnable for Compiler {
type Err = CompileError;
type Errs = CompileErrors;
const NAME: &'static str = "Erg compiler";
fn new(cfg: ErgConfig) -> Self {
let shared = SharedCompilerResource::new(cfg.copy());
Self {
mod_cache: shared.mod_cache.clone(),
builder: HIRBuilder::new_with_cache(cfg.copy(), "<module>", shared),
code_generator: PyCodeGenerator::new(cfg.copy()),
cfg,
}
}
#[inline]
fn cfg(&self) -> &ErgConfig {
&self.cfg
}
#[inline]
fn cfg_mut(&mut self) -> &mut ErgConfig {
&mut self.cfg
}
#[inline]
fn finish(&mut self) {}
fn initialize(&mut self) {
self.builder.initialize();
self.code_generator.clear();
// .mod_cache will be initialized in .builder
}
fn clear(&mut self) {
self.builder.clear();
self.code_generator.clear();
}
fn exec(&mut self) -> Result<i32, Self::Errs> {
let path = self.cfg.dump_pyc_path();
let warns = self
.compile_and_dump_as_pyc(path, self.input().read(), "exec")
.map_err(|eart| {
eart.warns.fmt_all_stderr();
eart.errors
})?;
warns.fmt_all_stderr();
Ok(0)
}
fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
let arti = self.compile(src, "eval").map_err(|eart| {
eart.warns.fmt_all_stderr();
eart.errors
})?;
arti.warns.fmt_all_stderr();
Ok(arti.object.code_info(Some(self.code_generator.py_version)))
}
}
use crate::context::Context;
use crate::varinfo::VarInfo;
use erg_parser::ast::VarName;
impl ContextProvider for Compiler {
fn dir(&self) -> Vec<(&VarName, &VarInfo)> {
self.builder.dir()
}
fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
self.builder.get_receiver_ctx(receiver_name)
}
fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
self.builder.get_var_info(name)
}
}
impl Compiler {
pub fn compile_and_dump_as_pyc<P: AsRef<Path>>(
&mut self,
pyc_path: P,
src: String,
mode: &str,
) -> Result<CompileWarnings, ErrorArtifact> {
let arti = self.compile(src, mode)?;
arti.object
.dump_as_pyc(pyc_path, self.cfg.py_magic_num)
.expect("failed to dump a .pyc file (maybe permission denied)");
Ok(arti.warns)
}
pub fn eval_compile_and_dump_as_pyc<P: AsRef<Path>>(
&mut self,
pyc_path: P,
src: String,
mode: &str,
) -> Result<CompleteArtifact<Option<Expr>>, ErrorArtifact> {
let arti = self.eval_compile(src, mode)?;
let (code, last) = arti.object;
code.dump_as_pyc(pyc_path, self.cfg.py_magic_num)
.expect("failed to dump a .pyc file (maybe permission denied)");
Ok(CompleteArtifact::new(last, arti.warns))
}
pub fn compile(
&mut self,
src: String,
mode: &str,
) -> Result<CompleteArtifact<CodeObj>, ErrorArtifact> {
log!(info "the compiling process has started.");
let arti = self.build_link_desugar(src, mode)?;
let codeobj = self.code_generator.emit(arti.object);
log!(info "code object:\n{}", codeobj.code_info(Some(self.code_generator.py_version)));
log!(info "the compiling process has completed");
Ok(CompleteArtifact::new(codeobj, arti.warns))
}
pub fn eval_compile(
&mut self,
src: String,
mode: &str,
) -> Result<CompleteArtifact<(CodeObj, Option<Expr>)>, ErrorArtifact> {
log!(info "the compiling process has started.");
let arti = self.build_link_desugar(src, mode)?;
let last = arti.object.module.last().cloned();
let codeobj = self.code_generator.emit(arti.object);
log!(info "code object:\n{}", codeobj.code_info(Some(self.code_generator.py_version)));
log!(info "the compiling process has completed");
Ok(CompleteArtifact::new((codeobj, last), arti.warns))
}
fn build_link_desugar(
&mut self,
src: String,
mode: &str,
) -> Result<CompleteArtifact, ErrorArtifact> {
let artifact = self.builder.build(src, mode)?;
let linker = Linker::new(&self.cfg, &self.mod_cache);
let hir = linker.link(artifact.object);
let desugared = HIRDesugarer::desugar(hir);
Ok(CompleteArtifact::new(desugared, artifact.warns))
}
}

View file

@ -0,0 +1,67 @@
use std::borrow::Borrow;
use std::cell::RefCell;
use std::hash::Hash;
use std::thread::LocalKey;
use erg_common::dict::Dict;
use crate::ty::Type;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SubtypePair {
pub sub: Type,
pub sup: Type,
}
impl SubtypePair {
pub const fn new(sub: Type, sup: Type) -> Self {
Self { sub, sup }
}
}
/// Caches type relationships.
/// The cost of searching for subtype relations of a class, for example, is not small.
/// Some relationships are cached because they tend to be queried many times.
#[derive(Debug, Default)]
pub struct TypeCmpCache {
cache: Dict<SubtypePair, bool>,
}
impl TypeCmpCache {
pub fn new() -> Self {
Self::default()
}
pub fn get<Q: Eq + Hash>(&self, pair: &Q) -> Option<bool>
where
SubtypePair: Borrow<Q>,
{
self.cache.get(pair).copied()
}
pub fn register(&mut self, pair: SubtypePair, b: bool) {
self.cache.insert(pair, b);
}
}
thread_local! {
static TYPE_CACHE: RefCell<TypeCmpCache> = RefCell::new(TypeCmpCache::default());
}
#[derive(Debug)]
pub struct GlobalTypeCmpCache(LocalKey<RefCell<TypeCmpCache>>);
pub static GLOBAL_TYPE_CACHE: GlobalTypeCmpCache = GlobalTypeCmpCache(TYPE_CACHE);
impl GlobalTypeCmpCache {
pub fn get<Q: Eq + Hash>(&'static self, pair: &Q) -> Option<bool>
where
SubtypePair: Borrow<Q>,
{
self.0.with(|s| s.borrow().get(pair))
}
pub fn register(&'static self, pair: SubtypePair, b: bool) {
self.0.with(|s| s.borrow_mut().register(pair, b));
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,948 @@
use std::mem;
use erg_common::error::Location;
use erg_common::traits::{Locational, Stream};
use erg_common::{assume_unreachable, fn_name};
use erg_common::{dict, set};
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
use crate::ty::constructors::*;
use crate::ty::free::{Constraint, FreeKind, HasLevel};
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::{HasType, Predicate, Type};
use crate::context::{Context, Variance};
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
use crate::{feature_error, hir};
use Type::*;
use Variance::*;
impl Context {
pub const TOP_LEVEL: usize = 1;
fn generalize_tp(&self, free: TyParam, variance: Variance, uninit: bool) -> TyParam {
match free {
TyParam::Type(t) => TyParam::t(self.generalize_t_inner(*t, variance, uninit)),
TyParam::FreeVar(fv) if fv.is_generalized() => TyParam::FreeVar(fv),
TyParam::FreeVar(fv) if fv.is_linked() => {
let fv_mut = unsafe { fv.as_ptr().as_mut().unwrap() };
if let FreeKind::Linked(tp) = fv_mut {
*tp = self.generalize_tp(tp.clone(), variance, uninit);
} else {
assume_unreachable!()
}
TyParam::FreeVar(fv)
}
// TODO: Polymorphic generalization
TyParam::FreeVar(fv) if fv.level() > Some(self.level) => {
let constr = self.generalize_constraint(&fv.crack_constraint(), variance);
fv.update_constraint(constr, true);
fv.generalize();
TyParam::FreeVar(fv)
}
TyParam::Array(tps) => TyParam::Array(
tps.into_iter()
.map(|tp| self.generalize_tp(tp, variance, uninit))
.collect(),
),
TyParam::Tuple(tps) => TyParam::Tuple(
tps.into_iter()
.map(|tp| self.generalize_tp(tp, variance, uninit))
.collect(),
),
TyParam::Dict(tps) => TyParam::Dict(
tps.into_iter()
.map(|(k, v)| {
(
self.generalize_tp(k, variance, uninit),
self.generalize_tp(v, variance, uninit),
)
})
.collect(),
),
TyParam::FreeVar(_) => free,
other if other.has_no_unbound_var() => other,
other => todo!("{other}"),
}
}
pub(crate) fn generalize_t(&self, free_type: Type) -> Type {
if cfg!(feature = "debug") && free_type.has_qvar() {
panic!("{free_type} has qvars")
}
let maybe_unbound_t = self.generalize_t_inner(free_type, Covariant, false);
if maybe_unbound_t.has_qvar() {
// NOTE: `?T(<: TraitX) -> Int` should be `TraitX -> Int`
// However, the current Erg cannot handle existential types, so it quantifies anyway
maybe_unbound_t.quantify()
} else {
maybe_unbound_t
}
}
/// see doc/LANG/compiler/inference.md#一般化 for details
/// ```python
/// generalize_t(?T) == 'T: Type
/// generalize_t(?T(<: Nat) -> ?T) == |'T <: Nat| 'T -> 'T
/// generalize_t(?T(<: Add(?T(<: Eq(?T(<: ...)))) -> ?T) == |'T <: Add('T)| 'T -> 'T
/// generalize_t(?T(<: TraitX) -> Int) == TraitX -> Int // 戻り値に現れないなら量化しない
/// ```
fn generalize_t_inner(&self, free_type: Type, variance: Variance, uninit: bool) -> Type {
match free_type {
FreeVar(fv) if fv.is_generalized() => Type::FreeVar(fv),
FreeVar(fv) if fv.is_linked() => {
let fv_mut = unsafe { fv.as_ptr().as_mut().unwrap() };
if let FreeKind::Linked(t) = fv_mut {
*t = self.generalize_t_inner(t.clone(), variance, uninit);
} else {
assume_unreachable!()
}
Type::FreeVar(fv)
}
// TODO: Polymorphic generalization
FreeVar(fv) if fv.level().unwrap() > self.level => {
if uninit {
// use crate::ty::free::GENERIC_LEVEL;
// return named_free_var(fv.unbound_name().unwrap(), GENERIC_LEVEL, Constraint::Uninited);
fv.generalize();
return Type::FreeVar(fv);
}
let constr = fv.constraint().unwrap();
if let Some((l, r)) = constr.get_sub_sup() {
// |Int <: T <: Int| T -> T ==> Int -> Int
if l == r
/*self.same_type_of(l, r)*/
{
fv.forced_link(l);
FreeVar(fv)
} else if r != &Obj && self.is_class(r) && variance == Contravariant {
// |T <: Bool| T -> Int ==> Bool -> Int
r.clone()
} else if l != &Never && self.is_class(l) && variance == Covariant {
// |T :> Int| X -> T ==> X -> Int
l.clone()
} else {
fv.update_constraint(
self.generalize_constraint(&fv.crack_constraint(), variance),
true,
);
fv.generalize();
Type::FreeVar(fv)
}
} else {
// ?S(: Str) => 'S
fv.update_constraint(
self.generalize_constraint(&fv.crack_constraint(), variance),
true,
);
fv.generalize();
Type::FreeVar(fv)
}
}
Subr(mut subr) => {
subr.non_default_params.iter_mut().for_each(|nd_param| {
*nd_param.typ_mut() = self.generalize_t_inner(
mem::take(nd_param.typ_mut()),
Contravariant,
uninit,
);
});
if let Some(var_args) = &mut subr.var_params {
*var_args.typ_mut() = self.generalize_t_inner(
mem::take(var_args.typ_mut()),
Contravariant,
uninit,
);
}
subr.default_params.iter_mut().for_each(|d_param| {
*d_param.typ_mut() = self.generalize_t_inner(
mem::take(d_param.typ_mut()),
Contravariant,
uninit,
);
});
let return_t = self.generalize_t_inner(*subr.return_t, Covariant, uninit);
subr_t(
subr.kind,
subr.non_default_params,
subr.var_params.map(|x| *x),
subr.default_params,
return_t,
)
}
Callable { .. } => todo!(),
Ref(t) => ref_(self.generalize_t_inner(*t, variance, uninit)),
RefMut { before, after } => {
let after = after.map(|aft| self.generalize_t_inner(*aft, variance, uninit));
ref_mut(self.generalize_t_inner(*before, variance, uninit), after)
}
Refinement(refine) => {
let t = self.generalize_t_inner(*refine.t, variance, uninit);
let preds = refine
.preds
.into_iter()
.map(|pred| self.generalize_pred(pred, variance, uninit))
.collect();
refinement(refine.var, t, preds)
}
Poly { name, mut params } => {
let params = params
.iter_mut()
.map(|p| self.generalize_tp(mem::take(p), variance, uninit))
.collect::<Vec<_>>();
poly(name, params)
}
Proj { lhs, rhs } => {
let lhs = self.generalize_t_inner(*lhs, variance, uninit);
proj(lhs, rhs)
}
ProjCall {
lhs,
attr_name,
mut args,
} => {
let lhs = self.generalize_tp(*lhs, variance, uninit);
for arg in args.iter_mut() {
*arg = self.generalize_tp(mem::take(arg), variance, uninit);
}
proj_call(lhs, attr_name, args)
}
And(l, r) => {
let l = self.generalize_t_inner(*l, variance, uninit);
let r = self.generalize_t_inner(*r, variance, uninit);
// not `self.intersection` because types are generalized
and(l, r)
}
Or(l, r) => {
let l = self.generalize_t_inner(*l, variance, uninit);
let r = self.generalize_t_inner(*r, variance, uninit);
// not `self.union` because types are generalized
or(l, r)
}
Not(l) => not(self.generalize_t_inner(*l, variance, uninit)),
// REVIEW: その他何でもそのまま通していいのか?
other => other,
}
}
fn generalize_constraint(&self, constraint: &Constraint, variance: Variance) -> Constraint {
match constraint {
Constraint::Sandwiched { sub, sup, .. } => {
let sub = self.generalize_t_inner(sub.clone(), variance, true);
let sup = self.generalize_t_inner(sup.clone(), variance, true);
Constraint::new_sandwiched(sub, sup)
}
Constraint::TypeOf(t) => {
let t = self.generalize_t_inner(t.clone(), variance, true);
Constraint::new_type_of(t)
}
Constraint::Uninited => unreachable!(),
}
}
fn generalize_pred(&self, pred: Predicate, variance: Variance, uninit: bool) -> Predicate {
match pred {
Predicate::Const(_) => pred,
Predicate::Value(ValueObj::Type(mut typ)) => {
*typ.typ_mut() =
self.generalize_t_inner(mem::take(typ.typ_mut()), variance, uninit);
Predicate::Value(ValueObj::Type(typ))
}
Predicate::Value(_) => pred,
Predicate::Equal { lhs, rhs } => {
let rhs = self.generalize_tp(rhs, variance, uninit);
Predicate::eq(lhs, rhs)
}
Predicate::GreaterEqual { lhs, rhs } => {
let rhs = self.generalize_tp(rhs, variance, uninit);
Predicate::ge(lhs, rhs)
}
Predicate::LessEqual { lhs, rhs } => {
let rhs = self.generalize_tp(rhs, variance, uninit);
Predicate::le(lhs, rhs)
}
Predicate::NotEqual { lhs, rhs } => {
let rhs = self.generalize_tp(rhs, variance, uninit);
Predicate::ne(lhs, rhs)
}
Predicate::And(lhs, rhs) => {
let lhs = self.generalize_pred(*lhs, variance, uninit);
let rhs = self.generalize_pred(*rhs, variance, uninit);
Predicate::and(lhs, rhs)
}
Predicate::Or(lhs, rhs) => {
let lhs = self.generalize_pred(*lhs, variance, uninit);
let rhs = self.generalize_pred(*rhs, variance, uninit);
Predicate::or(lhs, rhs)
}
Predicate::Not(pred) => {
let pred = self.generalize_pred(*pred, variance, uninit);
Predicate::not(pred)
}
}
}
pub(crate) fn deref_tp(
&self,
tp: TyParam,
variance: Variance,
loc: Location,
) -> TyCheckResult<TyParam> {
match tp {
TyParam::FreeVar(fv) if fv.is_linked() => {
let inner = fv.unwrap_linked();
self.deref_tp(inner, variance, loc)
}
TyParam::FreeVar(_fv) if self.level == 0 => Err(TyCheckErrors::from(
TyCheckError::dummy_infer_error(self.cfg.input.clone(), fn_name!(), line!()),
)),
TyParam::Type(t) => Ok(TyParam::t(self.deref_tyvar(*t, variance, loc)?)),
TyParam::App { name, mut args } => {
for param in args.iter_mut() {
*param = self.deref_tp(mem::take(param), variance, loc)?;
}
Ok(TyParam::App { name, args })
}
TyParam::BinOp { op, lhs, rhs } => {
let lhs = self.deref_tp(*lhs, variance, loc)?;
let rhs = self.deref_tp(*rhs, variance, loc)?;
Ok(TyParam::BinOp {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
})
}
TyParam::UnaryOp { op, val } => {
let val = self.deref_tp(*val, variance, loc)?;
Ok(TyParam::UnaryOp {
op,
val: Box::new(val),
})
}
TyParam::Array(tps) => {
let mut new_tps = vec![];
for tp in tps {
new_tps.push(self.deref_tp(tp, variance, loc)?);
}
Ok(TyParam::Array(new_tps))
}
TyParam::Tuple(tps) => {
let mut new_tps = vec![];
for tp in tps {
new_tps.push(self.deref_tp(tp, variance, loc)?);
}
Ok(TyParam::Tuple(new_tps))
}
TyParam::Dict(dic) => {
let mut new_dic = dict! {};
for (k, v) in dic.into_iter() {
new_dic.insert(
self.deref_tp(k, variance, loc)?,
self.deref_tp(v, variance, loc)?,
);
}
Ok(TyParam::Dict(new_dic))
}
TyParam::Set(set) => {
let mut new_set = set! {};
for v in set.into_iter() {
new_set.insert(self.deref_tp(v, variance, loc)?);
}
Ok(TyParam::Set(new_set))
}
TyParam::Proj { .. } | TyParam::Failure if self.level == 0 => Err(TyCheckErrors::from(
TyCheckError::dummy_infer_error(self.cfg.input.clone(), fn_name!(), line!()),
)),
t => Ok(t),
}
}
fn deref_constraint(
&self,
constraint: Constraint,
variance: Variance,
loc: Location,
) -> TyCheckResult<Constraint> {
match constraint {
Constraint::Sandwiched { sub, sup } => Ok(Constraint::new_sandwiched(
self.deref_tyvar(sub, variance, loc)?,
self.deref_tyvar(sup, variance, loc)?,
)),
Constraint::TypeOf(t) => {
Ok(Constraint::new_type_of(self.deref_tyvar(t, variance, loc)?))
}
_ => unreachable!(),
}
}
fn validate_subsup(
&self,
sub_t: Type,
super_t: Type,
variance: Variance,
loc: Location,
) -> TyCheckResult<Type> {
// TODO: Subr, ...
match (sub_t, super_t) {
// See tests\should_err\subtyping.er:8~13
(
Type::Poly {
name: ln,
params: lps,
},
Type::Poly {
name: rn,
params: rps,
},
) if ln == rn => {
let typ = poly(ln, lps.clone());
let (_, ctx) = self.get_nominal_type_ctx(&typ).ok_or_else(|| {
TyCheckError::type_not_found(
self.cfg.input.clone(),
line!() as usize,
loc,
self.caused_by(),
&typ,
)
})?;
let variances = ctx.type_params_variance();
let mut tps = vec![];
for ((lp, rp), variance) in lps
.into_iter()
.zip(rps.into_iter())
.zip(variances.into_iter())
{
self.sub_unify_tp(&lp, &rp, Some(variance), loc, false)?;
let param = if variance == Covariant { lp } else { rp };
tps.push(param);
}
Ok(poly(rn, tps))
}
(sub_t, super_t) => self.validate_simple_subsup(sub_t, super_t, variance, loc),
}
}
fn validate_simple_subsup(
&self,
sub_t: Type,
super_t: Type,
variance: Variance,
loc: Location,
) -> TyCheckResult<Type> {
if self.is_trait(&super_t) {
self.check_trait_impl(&sub_t, &super_t, loc)?;
}
// REVIEW: Even if type constraints can be satisfied, implementation may not exist
if self.subtype_of(&sub_t, &super_t) {
let sub_t = if cfg!(feature = "debug") {
sub_t
} else {
self.deref_tyvar(sub_t, variance, loc)?
};
let super_t = if cfg!(feature = "debug") {
super_t
} else {
self.deref_tyvar(super_t, variance, loc)?
};
match variance {
Variance::Covariant => Ok(sub_t),
Variance::Contravariant => Ok(super_t),
Variance::Invariant => {
// need to check if sub_t == super_t
if self.supertype_of(&sub_t, &super_t) {
Ok(sub_t)
} else {
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
&sub_t,
&super_t,
loc,
self.caused_by(),
)))
}
}
}
} else {
let sub_t = if cfg!(feature = "debug") {
sub_t
} else {
self.deref_tyvar(sub_t, variance, loc)?
};
let super_t = if cfg!(feature = "debug") {
super_t
} else {
self.deref_tyvar(super_t, variance, loc)?
};
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
&sub_t,
&super_t,
loc,
self.caused_by(),
)))
}
}
/// e.g.
/// ```python
// ?T(:> Nat, <: Int)[n] ==> Nat (self.level <= n)
// ?T(:> Nat, <: Sub(?U(:> {1}))) ==> Nat
// ?T(:> Nat, <: Sub(?U(:> {1}))) -> ?U ==> |U: Type, T <: Sub(U)| T -> U
// ?T(:> Nat, <: Sub(Str)) ==> Error!
// ?T(:> {1, "a"}, <: Eq(?T(:> {1, "a"}, ...)) ==> Error!
// ```
pub(crate) fn deref_tyvar(
&self,
t: Type,
variance: Variance,
loc: Location,
) -> TyCheckResult<Type> {
match t {
// ?T(:> Nat, <: Int)[n] ==> Nat (self.level <= n)
// ?T(:> Nat, <: Sub ?U(:> {1}))[n] ==> Nat
// ?T(<: Int, :> Add(?T)) ==> Int
// ?T(:> Nat, <: Sub(Str)) ==> Error!
// ?T(:> {1, "a"}, <: Eq(?T(:> {1, "a"}, ...)) ==> Error!
Type::FreeVar(fv) if fv.constraint_is_sandwiched() => {
let (sub_t, super_t) = fv.get_subsup().unwrap();
if self.level <= fv.level().unwrap() {
// if fv == ?T(<: Int, :> Add(?T)), deref_tyvar(super_t) will cause infinite loop
// so we need to force linking
fv.forced_undoable_link(&sub_t);
let res = self.validate_subsup(sub_t, super_t, variance, loc);
fv.undo();
res
} else {
// no dereference at this point
// drop(constraint);
Ok(Type::FreeVar(fv))
}
}
Type::FreeVar(fv) if fv.is_unbound() => {
if self.level == 0 {
#[allow(clippy::single_match)]
match &*fv.crack_constraint() {
Constraint::TypeOf(_) => {
return Err(TyCheckErrors::from(TyCheckError::dummy_infer_error(
self.cfg.input.clone(),
fn_name!(),
line!(),
)));
}
_ => {}
}
Ok(Type::FreeVar(fv))
} else {
let new_constraint = fv.crack_constraint().clone();
let new_constraint = self.deref_constraint(new_constraint, variance, loc)?;
fv.update_constraint(new_constraint, true);
Ok(Type::FreeVar(fv))
}
}
Type::FreeVar(fv) if fv.is_linked() => {
let t = fv.unwrap_linked();
self.deref_tyvar(t, variance, loc)
}
Type::Poly { name, mut params } => {
let typ = poly(&name, params.clone());
let (_, ctx) = self.get_nominal_type_ctx(&typ).ok_or_else(|| {
TyCheckError::type_not_found(
self.cfg.input.clone(),
line!() as usize,
loc,
self.caused_by(),
&typ,
)
})?;
let variances = ctx.type_params_variance();
for (param, variance) in params.iter_mut().zip(variances.into_iter()) {
*param = self.deref_tp(mem::take(param), variance, loc)?;
}
Ok(Type::Poly { name, params })
}
Type::Subr(mut subr) => {
for param in subr.non_default_params.iter_mut() {
*param.typ_mut() =
self.deref_tyvar(mem::take(param.typ_mut()), Contravariant, loc)?;
}
if let Some(var_args) = &mut subr.var_params {
*var_args.typ_mut() =
self.deref_tyvar(mem::take(var_args.typ_mut()), Contravariant, loc)?;
}
for d_param in subr.default_params.iter_mut() {
*d_param.typ_mut() =
self.deref_tyvar(mem::take(d_param.typ_mut()), Contravariant, loc)?;
}
subr.return_t =
Box::new(self.deref_tyvar(mem::take(&mut subr.return_t), Covariant, loc)?);
Ok(Type::Subr(subr))
}
Type::Quantified(subr)
if subr.return_t().map(|ret| !ret.has_qvar()).unwrap_or(false) =>
{
let subr = self.deref_tyvar(*subr, variance, loc)?;
Ok(subr)
}
Type::Ref(t) => {
let t = self.deref_tyvar(*t, variance, loc)?;
Ok(ref_(t))
}
Type::RefMut { before, after } => {
let before = self.deref_tyvar(*before, variance, loc)?;
let after = if let Some(after) = after {
Some(self.deref_tyvar(*after, variance, loc)?)
} else {
None
};
Ok(ref_mut(before, after))
}
// Type::Callable { .. } => todo!(),
Type::Record(mut rec) => {
for (_, field) in rec.iter_mut() {
*field = self.deref_tyvar(mem::take(field), variance, loc)?;
}
Ok(Type::Record(rec))
}
Type::Refinement(refine) => {
let t = self.deref_tyvar(*refine.t, variance, loc)?;
// TODO: deref_predicate
Ok(refinement(refine.var, t, refine.preds))
}
Type::And(l, r) => {
let l = self.deref_tyvar(*l, variance, loc)?;
let r = self.deref_tyvar(*r, variance, loc)?;
Ok(self.intersection(&l, &r))
}
Type::Or(l, r) => {
let l = self.deref_tyvar(*l, variance, loc)?;
let r = self.deref_tyvar(*r, variance, loc)?;
Ok(self.union(&l, &r))
}
Type::Not(ty) => {
let ty = self.deref_tyvar(*ty, variance, loc)?;
Ok(self.complement(&ty))
}
Type::Proj { lhs, rhs } => {
let lhs = self.deref_tyvar(*lhs, variance, loc)?;
self.eval_proj(lhs, rhs, self.level, loc)
}
Type::ProjCall {
lhs,
attr_name,
args,
} => {
let lhs = self.deref_tp(*lhs, variance, loc)?;
let mut new_args = vec![];
for arg in args.into_iter() {
new_args.push(self.deref_tp(arg, variance, loc)?);
}
self.eval_proj_call(lhs, attr_name, new_args, self.level, loc)
}
t => Ok(t),
}
}
pub fn readable_type(&self, t: Type, is_parameter: bool) -> Type {
let variance = if is_parameter {
Contravariant
} else {
Covariant
};
self.deref_tyvar(t.clone(), variance, Location::Unknown)
.unwrap_or(t)
}
pub(crate) fn trait_impl_exists(&self, class: &Type, trait_: &Type) -> bool {
if class.is_monomorphic() {
self.mono_class_trait_impl_exist(class, trait_)
} else {
self.poly_class_trait_impl_exists(class, trait_)
}
}
fn mono_class_trait_impl_exist(&self, class: &Type, trait_: &Type) -> bool {
let mut super_exists = false;
for inst in self.get_trait_impls(trait_).into_iter() {
if self.supertype_of(&inst.sub_type, class)
&& self.supertype_of(&inst.sup_trait, trait_)
{
super_exists = true;
break;
}
}
super_exists
}
fn poly_class_trait_impl_exists(&self, class: &Type, trait_: &Type) -> bool {
let mut super_exists = false;
for inst in self.get_trait_impls(trait_).into_iter() {
if self.supertype_of(&inst.sub_type, class)
&& self.supertype_of(&inst.sup_trait, trait_)
{
super_exists = true;
break;
}
}
super_exists
}
fn check_trait_impl(&self, class: &Type, trait_: &Type, loc: Location) -> TyCheckResult<()> {
if !self.trait_impl_exists(class, trait_) {
let class = if cfg!(feature = "debug") {
class.clone()
} else {
self.deref_tyvar(class.clone(), Variance::Covariant, loc)?
};
let trait_ = if cfg!(feature = "debug") {
trait_.clone()
} else {
self.deref_tyvar(trait_.clone(), Variance::Covariant, loc)?
};
Err(TyCheckErrors::from(TyCheckError::no_trait_impl_error(
self.cfg.input.clone(),
line!() as usize,
&class,
&trait_,
loc,
self.caused_by(),
self.get_simple_type_mismatch_hint(&trait_, &class),
)))
} else {
Ok(())
}
}
/// Check if all types are resolvable (if traits, check if an implementation exists)
/// And replace them if resolvable
pub(crate) fn resolve(
&mut self,
mut hir: hir::HIR,
) -> Result<hir::HIR, (hir::HIR, TyCheckErrors)> {
self.level = 0;
let mut errs = TyCheckErrors::empty();
for chunk in hir.module.iter_mut() {
if let Err(es) = self.resolve_expr_t(chunk) {
errs.extend(es);
}
}
self.resolve_ctx_vars();
if errs.is_empty() {
Ok(hir)
} else {
Err((hir, errs))
}
}
fn resolve_ctx_vars(&mut self) {
let mut locals = mem::take(&mut self.locals);
let mut params = mem::take(&mut self.params);
let mut methods_list = mem::take(&mut self.methods_list);
for (name, vi) in locals.iter_mut() {
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Variance::Covariant, name.loc()) {
vi.t = t;
}
}
for (name, vi) in params.iter_mut() {
let loc = name.as_ref().map(|n| n.loc()).unwrap_or_default();
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Variance::Covariant, loc) {
vi.t = t;
}
}
for (_, methods) in methods_list.iter_mut() {
methods.resolve_ctx_vars();
}
self.locals = locals;
self.params = params;
self.methods_list = methods_list;
}
fn resolve_expr_t(&self, expr: &mut hir::Expr) -> TyCheckResult<()> {
match expr {
hir::Expr::Lit(_) => Ok(()),
hir::Expr::Accessor(acc) => {
let variance = if acc.var_info().kind.is_parameter() {
Contravariant
} else {
Covariant
};
let loc = acc.loc();
let t = acc.ref_mut_t();
*t = self.deref_tyvar(mem::take(t), variance, loc)?;
if let hir::Accessor::Attr(attr) = acc {
self.resolve_expr_t(&mut attr.obj)?;
}
Ok(())
}
hir::Expr::Array(array) => match array {
hir::Array::Normal(arr) => {
arr.t = self.deref_tyvar(mem::take(&mut arr.t), Covariant, arr.loc())?;
for elem in arr.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
hir::Array::WithLength(arr) => {
arr.t = self.deref_tyvar(mem::take(&mut arr.t), Covariant, arr.loc())?;
self.resolve_expr_t(&mut arr.elem)?;
self.resolve_expr_t(&mut arr.len)?;
Ok(())
}
other => feature_error!(
TyCheckErrors,
TyCheckError,
self,
other.loc(),
"resolve types of array comprehension"
),
},
hir::Expr::Tuple(tuple) => match tuple {
hir::Tuple::Normal(tup) => {
tup.t = self.deref_tyvar(mem::take(&mut tup.t), Covariant, tup.loc())?;
for elem in tup.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
},
hir::Expr::Set(set) => match set {
hir::Set::Normal(st) => {
st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, st.loc())?;
for elem in st.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
hir::Set::WithLength(st) => {
st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, st.loc())?;
self.resolve_expr_t(&mut st.elem)?;
self.resolve_expr_t(&mut st.len)?;
Ok(())
}
},
hir::Expr::Dict(dict) => match dict {
hir::Dict::Normal(dic) => {
dic.t = self.deref_tyvar(mem::take(&mut dic.t), Covariant, dic.loc())?;
for kv in dic.kvs.iter_mut() {
self.resolve_expr_t(&mut kv.key)?;
self.resolve_expr_t(&mut kv.value)?;
}
Ok(())
}
other => feature_error!(
TyCheckErrors,
TyCheckError,
self,
other.loc(),
"resolve types of dict comprehension"
),
},
hir::Expr::Record(record) => {
record.t = self.deref_tyvar(mem::take(&mut record.t), Covariant, record.loc())?;
for attr in record.attrs.iter_mut() {
match &mut attr.sig {
hir::Signature::Var(var) => {
*var.ref_mut_t() =
self.deref_tyvar(mem::take(var.ref_mut_t()), Covariant, var.loc())?;
}
hir::Signature::Subr(subr) => {
*subr.ref_mut_t() = self.deref_tyvar(
mem::take(subr.ref_mut_t()),
Covariant,
subr.loc(),
)?;
}
}
for chunk in attr.body.block.iter_mut() {
self.resolve_expr_t(chunk)?;
}
}
Ok(())
}
hir::Expr::BinOp(binop) => {
let loc = binop.loc();
let t = binop.signature_mut_t().unwrap();
*t = self.deref_tyvar(mem::take(t), Covariant, loc)?;
self.resolve_expr_t(&mut binop.lhs)?;
self.resolve_expr_t(&mut binop.rhs)?;
Ok(())
}
hir::Expr::UnaryOp(unaryop) => {
let loc = unaryop.loc();
let t = unaryop.signature_mut_t().unwrap();
*t = self.deref_tyvar(mem::take(t), Covariant, loc)?;
self.resolve_expr_t(&mut unaryop.expr)?;
Ok(())
}
hir::Expr::Call(call) => {
let loc = call.loc();
if let Some(t) = call.signature_mut_t() {
*t = self.deref_tyvar(mem::take(t), Covariant, loc)?;
}
self.resolve_expr_t(&mut call.obj)?;
for arg in call.args.pos_args.iter_mut() {
self.resolve_expr_t(&mut arg.expr)?;
}
if let Some(var_args) = &mut call.args.var_args {
self.resolve_expr_t(&mut var_args.expr)?;
}
for arg in call.args.kw_args.iter_mut() {
self.resolve_expr_t(&mut arg.expr)?;
}
Ok(())
}
hir::Expr::Def(def) => {
*def.sig.ref_mut_t() =
self.deref_tyvar(mem::take(def.sig.ref_mut_t()), Covariant, def.sig.loc())?;
for chunk in def.body.block.iter_mut() {
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::Lambda(lambda) => {
lambda.t = self.deref_tyvar(mem::take(&mut lambda.t), Covariant, lambda.loc())?;
for chunk in lambda.body.iter_mut() {
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::ClassDef(class_def) => {
for def in class_def.methods.iter_mut() {
self.resolve_expr_t(def)?;
}
Ok(())
}
hir::Expr::PatchDef(patch_def) => {
for def in patch_def.methods.iter_mut() {
self.resolve_expr_t(def)?;
}
Ok(())
}
hir::Expr::ReDef(redef) => {
// REVIEW: redef.attr is not dereferenced
for chunk in redef.block.iter_mut() {
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::TypeAsc(tasc) => self.resolve_expr_t(&mut tasc.expr),
hir::Expr::Code(chunks) | hir::Expr::Compound(chunks) => {
for chunk in chunks.iter_mut() {
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::Dummy(chunks) => {
for chunk in chunks.iter_mut() {
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::Import(_) => unreachable!(),
}
}
}

View file

@ -0,0 +1,191 @@
use erg_common::style::{Attribute, Color, StyledStrings, THEME};
use erg_common::{option_enum_unwrap, switch_lang};
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::{HasType, Predicate, SubrKind, Type};
use crate::context::Context;
const HINT: Color = THEME.colors.hint;
const ERR: Color = THEME.colors.error;
#[cfg(not(feature = "pretty"))]
const ATTR: Attribute = Attribute::Bold;
#[cfg(feature = "pretty")]
const ATTR: Attribute = Attribute::Underline;
#[derive(PartialEq, Eq)]
enum Sequence {
Forward,
Backward,
}
// TODO: these should not be in Context
impl Context {
/// TODO: custom types
fn get_verb_and_preposition(trait_: &Type) -> Option<(&str, &str, Sequence)> {
match &trait_.qual_name()[..] {
"Add" => Some(("add", "and", Sequence::Forward)),
"Sub" => Some(("subtract", "from", Sequence::Backward)),
"Mul" => Some(("multiply", "and", Sequence::Forward)),
"Div" => Some(("divide", "by", Sequence::Forward)),
"Eq" => Some(("compare", "and", Sequence::Forward)),
"Ord" => Some(("compare", "and", Sequence::Forward)),
_ => None,
}
}
pub(crate) fn get_call_type_mismatch_hint(
&self,
callee_t: &Type,
attr: Option<&str>,
nth: usize,
expected: &Type,
found: &Type,
) -> Option<String> {
if &callee_t.qual_name()[..] == "Array" && attr == Some("__getitem__") && nth == 1 {
let len = &callee_t.typarams().get(1).cloned()?;
let (_, _, preds) = found.clone().deconstruct_refinement().ok()?;
if let Predicate::Equal {
lhs: _,
rhs: accessed,
} = preds.iter().next()?
{
let accessed = if let TyParam::Value(value) = accessed {
value
.clone()
.try_add(ValueObj::Nat(1))
.map(TyParam::Value)
.unwrap_or_else(|| accessed.clone())
} else {
accessed.clone()
};
return Some(switch_lang! {
"japanese" => format!("配列の長さは{len}ですが、{accessed}番目の要素にアクセスしようとしています"),
"simplified_chinese" => format!("数组长度为{len}但尝试访问第{accessed}个元素"),
"traditional_chinese" => format!("陣列長度為{len}但嘗試訪問第{accessed}個元素"),
"english" => format!("Array length is {len} but tried to access the {accessed}th element"),
});
}
}
self.get_simple_type_mismatch_hint(expected, found)
}
pub(crate) fn get_simple_type_mismatch_hint(
&self,
expected: &Type,
found: &Type,
) -> Option<String> {
let expected = if let Type::FreeVar(fv) = expected {
if fv.is_linked() {
fv.crack().clone()
} else {
let (_sub, sup) = fv.get_subsup()?;
sup
}
} else {
expected.clone()
};
let mut hint = StyledStrings::default();
if let (Type::Subr(expt), Type::Subr(fnd)) = (&expected, &found) {
if let (SubrKind::Func, SubrKind::Proc) = (expt.kind, fnd.kind) {
switch_lang!(
"japanese" => {
hint.push_str("この仮引数は(副作用のない)関数を受け取りますが、プロシージャは副作用があるため受け取りません。副作用を取り除き、");
hint.push_str_with_color_and_attribute("=>", ERR, ATTR);
hint.push_str("の代わりに");
hint.push_str_with_color_and_attribute("->", HINT, ATTR);
hint.push_str("を使用する必要があります");
},
"simplified_chinese" => {
hint.push_str("此参数接受func无副作用但由于副作用而不接受proc。你应该使用 ");
hint.push_str_with_color_and_attribute("->", HINT, ATTR);
hint.push_str("而不是 ");
hint.push_str_with_color_and_attribute("=>", ERR, ATTR);
},
"traditional_chinese" => {
hint.push_str("此参数接受 func无副作用但由于副作用而不接受proc。你應該使用 ");
hint.push_str_with_color_and_attribute("->", HINT, ATTR);
hint.push_str("而不是 ");
hint.push_str_with_color_and_attribute("=>", ERR, ATTR);
},
"english" => {
hint.push_str("This param accepts func (without side-effects) but not proc because of side-effects. You should use ");
hint.push_str_with_color_and_attribute("->", HINT, ATTR);
hint.push_str(" instead of ");
hint.push_str_with_color_and_attribute("=>", ERR, ATTR);
},
);
return Some(hint.to_string());
}
}
match (&expected.qual_name()[..], &found.qual_name()[..]) {
("Eq", "Float") => {
switch_lang!(
"japanese" => {
hint.push_str("Floatは等価関係が定義されていません。");
hint.push_str_with_color_and_attribute("l == R", ERR, ATTR);
hint.push_str("ではなく、");
hint.push_str_with_color_and_attribute("l - r <= Float.EPSILON", HINT, ATTR);
hint.push_str("を使用してください");
},
"simplified_chinese" => {
hint.push_str("Float没有定义等价关系。你应该使用");
hint.push_str_with_color_and_attribute("l == R", ERR, ATTR);
hint.push_str("而不是");
hint.push_str_with_color_and_attribute("l - r <= Float.EPSILON", HINT, ATTR);
},
"traditional_chinese" => {
hint.push_str("Float沒有定義等價關係。你應該使用");
hint.push_str_with_color_and_attribute("l == R", ERR, ATTR);
hint.push_str(" instead of ");
hint.push_str_with_color_and_attribute("l - r <= Float.EPSILON", HINT, ATTR);
},
"english" => {
hint.push_str("Float has no equivalence relation defined. you should use ");
hint.push_str_with_color_and_attribute("l == R", ERR, ATTR);
hint.push_str(" instead of ");
hint.push_str_with_color_and_attribute("l - r <= Float.EPSILON", HINT, ATTR);
},
);
Some(hint.to_string())
}
_ => {
let (verb, preposition, _sequence) = Self::get_verb_and_preposition(&expected)?;
found
.union_types()
.map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}"))
.or_else(|| {
expected.inner_ts().get(0).map(|expected_inner| {
let expected_inner = self.readable_type(expected_inner.clone(), false);
format!("cannot {verb} {found} {preposition} {expected_inner}")
})
})
}
}
}
pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option<String> {
match proj {
Type::Proj { lhs, rhs: _ } => {
if let Type::FreeVar(fv) = lhs.as_ref() {
let (sub, sup) = fv.get_subsup()?;
let (verb, preposition, sequence) = Self::get_verb_and_preposition(&sup)?;
let sup = *option_enum_unwrap!(sup.typarams().get(0)?.clone(), TyParam::Type)?;
let sup = self.readable_type(sup, false);
let (l, r) = if sequence == Sequence::Forward {
(sub, sup)
} else {
(sup, sub)
};
Some(format!("cannot {verb} {l} {preposition} {r}"))
} else {
None
}
}
_ => None,
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,300 @@
use std::mem;
use erg_common::enum_unwrap;
use crate::context::Context;
use crate::feature_error;
use crate::ty::constructors::{and, mono};
use crate::ty::value::{EvalValueError, EvalValueResult, GenTypeObj, TypeObj, ValueObj};
use crate::ty::ValueArgs;
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
use erg_common::style::{Color, StyledStr, StyledString, THEME};
const ERR: Color = THEME.colors.error;
const WARN: Color = THEME.colors.warning;
const SUP_ERR: StyledStr = StyledStr::new("Super", Some(ERR), None);
const SUP_WARN: StyledStr = StyledStr::new("Super", Some(WARN), None);
const CLASS_ERR: StyledStr = StyledStr::new("Class", Some(ERR), None);
const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None);
const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None);
const BASE_ERR: StyledStr = StyledStr::new("Base", Some(ERR), None);
const BASE_WARN: StyledStr = StyledStr::new("Base", Some(WARN), None);
/// Base := Type or NoneType, Impl := Type -> ClassType
pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let base = args.remove_left_or_key("Base");
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
match base {
Some(value) => {
if let Some(base) = value.as_type() {
Ok(ValueObj::gen_t(GenTypeObj::class(t, Some(base), impls)))
} else {
let base = StyledString::new(format!("{value}"), Some(ERR), None);
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("non-type object {base} is passed to {BASE_WARN}",),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
)
.into())
}
}
None => Ok(ValueObj::gen_t(GenTypeObj::class(t, None, impls))),
}
}
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType
pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
let sup = StyledStr::new("Super", Some(ERR), None);
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{sup} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
let Some(sup) = sup.as_type() else {
let sup_ty = StyledString::new(format!("{sup}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-class object {sup_ty} is passed to {SUP_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let additional = args.remove_left_or_key("Additional");
let additional = additional.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::inherited(
t, sup, impls, additional,
)))
}
/// Class: ClassType -> ClassType (with `InheritableType`)
/// This function is used by the compiler to mark a class as inheritable and does nothing in terms of actual operation.
pub fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
let class = args.remove_left_or_key("Class").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{CLASS_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
match class {
ValueObj::Type(TypeObj::Generated(mut gen)) => {
if let Some(typ) = gen.impls_mut() {
match typ.as_mut().map(|x| x.as_mut()) {
Some(TypeObj::Generated(gen)) => {
*gen.typ_mut() = and(mem::take(gen.typ_mut()), mono("InheritableType"));
}
Some(TypeObj::Builtin(t)) => {
*t = and(mem::take(t), mono("InheritableType"));
}
_ => {
*typ = Some(Box::new(TypeObj::Builtin(mono("InheritableType"))));
}
}
}
Ok(ValueObj::Type(TypeObj::Generated(gen)))
}
other => feature_error!(
EvalValueError,
_ctx,
Location::Unknown,
&format!("Inheritable {other}")
),
}
}
/// Base: Type, Impl := Type -> TraitType
pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let req = args.remove_left_or_key("Requirement").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{REQ_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
let Some(req) = req.as_type() else {
let req = StyledString::new(format!("{req}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-type object {req} is passed to {REQ_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, req, impls)))
}
/// Base: Type, Impl := Type -> Patch
pub fn patch_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let base = args.remove_left_or_key("Base").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{BASE_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
let Some(base) = base.as_type() else {
let base = StyledString::new(format!("{base}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-type object {base} is passed to {BASE_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::patch(t, base, impls)))
}
/// Super: TraitType, Impl := Type, Additional := Type -> TraitType
pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{SUP_ERR} is not passed"),
line!() as usize,
ErrorKind::KeyError,
Location::Unknown,
)
})?;
let Some(sup) = sup.as_type() else {
let sup = StyledString::new(format!("{sup}"), Some(ERR), None);
return Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"non-trait object {sup} is passed to {SUP_WARN}",
),
line!() as usize,
ErrorKind::TypeError,
Location::Unknown,
).into());
};
let impls = args.remove_left_or_key("Impl");
let impls = impls.map(|v| v.as_type().unwrap());
let additional = args.remove_left_or_key("Additional");
let additional = additional.map(|v| v.as_type().unwrap());
let t = mono(ctx.name.clone());
Ok(ValueObj::gen_t(GenTypeObj::subsumed(
t, sup, impls, additional,
)))
}
pub fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = ctx
.convert_value_into_array(args.remove_left_or_key("Self").unwrap())
.unwrap();
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
if let Some(v) = slf.get(index as usize) {
Ok(v.clone())
} else {
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!(
"[{}] has {} elements, but accessed {}th element",
erg_common::fmt_vec(&slf),
slf.len(),
index
),
line!() as usize,
ErrorKind::IndexError,
Location::Unknown,
)
.into())
}
}
pub fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
let slf = args.remove_left_or_key("Self").unwrap();
let slf = enum_unwrap!(slf, ValueObj::Dict);
let index = args.remove_left_or_key("Index").unwrap();
if let Some(v) = slf.get(&index).or_else(|| {
for (k, v) in slf.iter() {
match (&index, k) {
(ValueObj::Type(idx), ValueObj::Type(kt)) => {
if ctx.subtype_of(idx.typ(), kt.typ()) {
return Some(v);
}
}
(idx, k) => {
if idx == k {
return Some(v);
}
}
}
}
None
}) {
Ok(v.clone())
} else {
let index = if let ValueObj::Type(t) = &index {
let derefed = ctx.readable_type(t.typ().clone(), false);
ValueObj::builtin_t(derefed)
} else {
index
};
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("{slf} has no key {index}"),
line!() as usize,
ErrorKind::IndexError,
Location::Unknown,
)
.into())
}
}
pub fn __range_getitem__(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
let (_name, fields) = enum_unwrap!(
args.remove_left_or_key("Self").unwrap(),
ValueObj::DataClass { name, fields }
);
let index = enum_unwrap!(args.remove_left_or_key("Index").unwrap(), ValueObj::Nat);
let start = fields.get("start").unwrap();
let start = *enum_unwrap!(start, ValueObj::Nat);
let end = fields.get("end").unwrap();
let end = *enum_unwrap!(end, ValueObj::Nat);
// FIXME <= if inclusive
if start + index < end {
Ok(ValueObj::Nat(start + index))
} else {
Err(ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
format!("Index out of range: {}", index),
line!() as usize,
ErrorKind::IndexError,
Location::Unknown,
)
.into())
}
}

View file

@ -0,0 +1,476 @@
#[allow(unused_imports)]
use erg_common::log;
use erg_common::vis::Visibility;
use crate::ty::constructors::*;
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::Type;
use Type::*;
use crate::context::initialize::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(super) fn init_builtin_funcs(&mut self) {
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
let T = mono_q("T", instanceof(Type));
let U = mono_q("U", instanceof(Type));
let Path = mono_q_tp("Path", instanceof(Str));
let t_abs = nd_func(vec![kw("n", mono(NUM))], None, Nat);
let t_all = func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(Bool)]))],
None,
vec![],
Bool,
);
let t_any = func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(Bool)]))],
None,
vec![],
Bool,
);
let t_ascii = nd_func(vec![kw("object", Obj)], None, Str);
let t_assert = func(
vec![kw("condition", Bool)],
None,
vec![kw("err_message", Str)],
NoneType,
);
let t_bin = nd_func(vec![kw("n", Int)], None, Str);
let t_bytes = nd_func(
vec![kw("str", Str), kw("encoding", Str)],
None,
mono("Bytes"),
);
let t_chr = nd_func(
vec![kw("i", Type::from(value(0usize)..=value(1_114_111usize)))],
None,
Str,
);
let t_classof = nd_func(vec![kw("old", Obj)], None, ClassType);
let t_compile = nd_func(vec![kw("src", Str)], None, Code);
let t_cond = nd_func(
vec![
kw("condition", Bool),
kw("then", T.clone()),
kw("else", T.clone()),
],
None,
T.clone(),
)
.quantify();
let t_discard = nd_func(vec![kw("obj", Obj)], None, NoneType);
let t_enumerate = func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(T.clone())]))],
None,
vec![kw("start", Int)],
poly("Enumerate", vec![ty_tp(T.clone())]),
)
.quantify();
let t_if = func(
vec![
kw("cond", Bool),
kw("then", nd_func(vec![], None, T.clone())),
],
None,
vec![kw_default(
"else",
nd_func(vec![], None, U.clone()),
nd_func(vec![], None, NoneType),
)],
or(T.clone(), U.clone()),
)
.quantify();
let t_int = nd_func(vec![kw("obj", Obj)], None, or(Int, NoneType));
let t_import = nd_func(
vec![anon(tp_enum(Str, set! {Path.clone()}))],
None,
module(Path.clone()),
)
.quantify();
let t_isinstance = nd_func(
vec![
kw("object", Obj),
kw("classinfo", ClassType), // TODO: => ClassInfo
],
None,
Bool,
);
let t_issubclass = nd_func(
vec![
kw("subclass", ClassType),
kw("classinfo", ClassType), // TODO: => ClassInfo
],
None,
Bool,
);
let I = mono_q("I", subtypeof(poly("Iterable", vec![ty_tp(T.clone())])));
let t_iter = nd_func(vec![kw("object", I.clone())], None, proj(I, "Iterator")).quantify();
let t_len = nd_func(
vec![kw("s", poly("Seq", vec![TyParam::erased(Type)]))],
None,
Nat,
);
let t_log = func(
vec![],
Some(kw("objects", ref_(Obj))),
vec![
kw("sep", Str),
kw("end", Str),
kw("file", mono("Write")),
kw("flush", Bool),
],
NoneType,
);
let t_map = nd_func(
vec![
kw("proc!", nd_proc(vec![anon(T.clone())], None, T.clone())),
kw("iterable", poly("Iterable", vec![ty_tp(T.clone())])),
],
None,
poly("Map", vec![ty_tp(T.clone())]),
)
.quantify();
let O = mono_q("O", subtypeof(mono("Ord")));
// TODO: iterable should be non-empty
let t_max = nd_func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(O.clone())]))],
None,
O.clone(),
)
.quantify();
let t_min = nd_func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(O.clone())]))],
None,
O,
)
.quantify();
let t_nat = nd_func(vec![kw("obj", Obj)], None, or(Nat, NoneType));
// e.g. not(b: Bool!): Bool!
let B = mono_q("B", subtypeof(Bool));
let t_not = nd_func(vec![kw("b", B.clone())], None, B).quantify();
let t_oct = nd_func(vec![kw("x", Int)], None, Str);
let t_ord = nd_func(vec![kw("c", Str)], None, Nat);
let t_panic = nd_func(vec![kw("err_message", Str)], None, Never);
let M = mono_q("M", Constraint::Uninited);
let M = mono_q("M", subtypeof(poly("Mul", vec![ty_tp(M)])));
// TODO: mod
let t_pow = nd_func(
vec![kw("base", M.clone()), kw("exp", M.clone())],
None,
proj(M, "Output"),
)
.quantify();
let t_pyimport = nd_func(
vec![anon(tp_enum(Str, set! {Path.clone()}))],
None,
py_module(Path),
)
.quantify();
let t_pycompile = nd_func(
vec![kw("src", Str), kw("filename", Str), kw("mode", Str)],
None,
Code,
);
let t_quit = func(vec![], None, vec![kw("code", Int)], Never);
let t_exit = t_quit.clone();
let t_repr = nd_func(vec![kw("object", Obj)], None, Str);
let t_reversed = nd_func(
vec![kw("seq", poly("Seq", vec![ty_tp(T.clone())]))],
None,
poly("Reversed", vec![ty_tp(T.clone())]),
)
.quantify();
let t_round = nd_func(vec![kw("number", Float)], None, Int);
let t_sorted = nd_func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(T.clone())]))],
None,
array_t(T.clone(), TyParam::erased(Nat)),
)
.quantify();
let t_str = nd_func(vec![kw("object", Obj)], None, Str);
let A = mono_q("A", Constraint::Uninited);
let A = mono_q("A", subtypeof(poly("Add", vec![ty_tp(A)])));
let t_sum = func(
vec![kw("iterable", poly("Iterable", vec![ty_tp(A.clone())]))],
None,
vec![kw_default("start", or(A.clone(), Int), Int)],
A,
)
.quantify();
let t_unreachable = nd_func(vec![], None, Never);
let t_zip = nd_func(
vec![
kw("iterable1", poly("Iterable", vec![ty_tp(T.clone())])),
kw("iterable2", poly("Iterable", vec![ty_tp(U.clone())])),
],
None,
poly("Zip", vec![ty_tp(T.clone()), ty_tp(U.clone())]),
)
.quantify();
self.register_builtin_py_impl("abs", t_abs, Immutable, vis, Some("abs"));
self.register_builtin_py_impl("all", t_all, Immutable, vis, Some("all"));
self.register_builtin_py_impl("any", t_any, Immutable, vis, Some("any"));
self.register_builtin_py_impl("ascii", t_ascii, Immutable, vis, Some("ascii"));
// Leave as `Const`, as it may negatively affect assert casting.
self.register_builtin_erg_impl("assert", t_assert, Const, vis);
self.register_builtin_py_impl("bin", t_bin, Immutable, vis, Some("bin"));
self.register_builtin_py_impl("bytes", t_bytes, Immutable, vis, Some("bytes"));
self.register_builtin_py_impl("chr", t_chr, Immutable, vis, Some("chr"));
self.register_builtin_py_impl("classof", t_classof, Immutable, vis, Some("type"));
self.register_builtin_py_impl("compile", t_compile, Immutable, vis, Some("compile"));
self.register_builtin_erg_impl("cond", t_cond, Immutable, vis);
self.register_builtin_py_impl("enumerate", t_enumerate, Immutable, vis, Some("enumerate"));
self.register_builtin_py_impl("exit", t_exit, Immutable, vis, Some("exit"));
self.register_builtin_py_impl(
"isinstance",
t_isinstance,
Immutable,
vis,
Some("isinstance"),
);
self.register_builtin_py_impl(
"issubclass",
t_issubclass,
Immutable,
vis,
Some("issubclass"),
);
self.register_builtin_py_impl("iter", t_iter, Immutable, vis, Some("iter"));
self.register_builtin_py_impl("len", t_len, Immutable, vis, Some("len"));
self.register_builtin_py_impl("map", t_map, Immutable, vis, Some("map"));
self.register_builtin_py_impl("max", t_max, Immutable, vis, Some("max"));
self.register_builtin_py_impl("min", t_min, Immutable, vis, Some("min"));
self.register_builtin_py_impl("not", t_not, Immutable, vis, None); // `not` is not a function in Python
self.register_builtin_py_impl("oct", t_oct, Immutable, vis, Some("oct"));
self.register_builtin_py_impl("ord", t_ord, Immutable, vis, Some("ord"));
self.register_builtin_py_impl("pow", t_pow, Immutable, vis, Some("pow"));
self.register_builtin_py_impl(
"pyimport",
t_pyimport.clone(),
Immutable,
vis,
Some("__import__"),
);
self.register_builtin_py_impl("quit", t_quit, Immutable, vis, Some("quit"));
self.register_builtin_py_impl("repr", t_repr, Immutable, vis, Some("repr"));
self.register_builtin_py_impl("reversed", t_reversed, Immutable, vis, Some("reversed"));
self.register_builtin_py_impl("round", t_round, Immutable, vis, Some("round"));
self.register_builtin_py_impl("sorted", t_sorted, Immutable, vis, Some("sorted"));
self.register_builtin_py_impl("str", t_str, Immutable, vis, Some("str"));
self.register_builtin_py_impl("sum", t_sum, Immutable, vis, Some("sum"));
self.register_builtin_py_impl("zip", t_zip, Immutable, vis, Some("zip"));
let name = if cfg!(feature = "py_compatible") {
"int"
} else {
"int__"
};
self.register_builtin_py_impl("int", t_int, Immutable, vis, Some(name));
if !cfg!(feature = "py_compatible") {
self.register_builtin_py_impl("if", t_if, Immutable, vis, Some("if__"));
self.register_builtin_py_impl("discard", t_discard, Immutable, vis, Some("discard__"));
self.register_builtin_py_impl("import", t_import, Immutable, vis, Some("__import__"));
self.register_builtin_py_impl("log", t_log, Immutable, vis, Some("print"));
self.register_builtin_py_impl("nat", t_nat, Immutable, vis, Some("nat__"));
self.register_builtin_py_impl("panic", t_panic, Immutable, vis, Some("quit"));
if cfg!(feature = "debug") {
self.register_builtin_py_impl("py", t_pyimport, Immutable, vis, Some("__import__"));
}
self.register_builtin_py_impl(
"pycompile",
t_pycompile,
Immutable,
vis,
Some("compile"),
);
// TODO: original implementation
self.register_builtin_py_impl(
"unreachable",
t_unreachable,
Immutable,
vis,
Some("exit"),
);
} else {
let t_range = func(
vec![kw("stop", or(Int, NoneType))],
None,
vec![
kw("start", or(Int, NoneType)),
kw("step", or(Int, NoneType)),
],
poly("Range", vec![ty_tp(Int)]),
);
self.register_builtin_py_impl("range", t_range, Immutable, vis, Some("range"));
let t_list = func(
vec![],
None,
vec![kw("iterable", poly("Iterable", vec![ty_tp(T.clone())]))],
poly("Array", vec![ty_tp(T.clone()), TyParam::erased(Nat)]),
)
.quantify();
self.register_builtin_py_impl("list", t_list, Immutable, vis, Some("list"));
let t_dict = func(
vec![],
None,
vec![kw(
"iterable",
poly("Iterable", vec![ty_tp(tuple_t(vec![T.clone(), U.clone()]))]),
)],
dict! { T => U }.into(),
)
.quantify();
self.register_builtin_py_impl("dict", t_dict, Immutable, vis, Some("dict"));
}
}
pub(super) fn init_builtin_const_funcs(&mut self) {
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
let class_t = func(
vec![],
None,
vec![kw("Requirement", or(Type, Ellipsis)), kw("Impl", Type)],
ClassType,
);
let class = ConstSubr::Builtin(BuiltinConstSubr::new("Class", class_func, class_t, None));
self.register_builtin_const("Class", vis, ValueObj::Subr(class));
let inherit_t = func(
vec![kw("Super", ClassType)],
None,
vec![kw("Impl", Type), kw("Additional", Type)],
ClassType,
);
let inherit = ConstSubr::Builtin(BuiltinConstSubr::new(
"Inherit",
inherit_func,
inherit_t,
None,
));
self.register_builtin_const("Inherit", vis, ValueObj::Subr(inherit));
let trait_t = func(
vec![kw("Requirement", Type)],
None,
vec![kw("Impl", Type)],
TraitType,
);
let trait_ = ConstSubr::Builtin(BuiltinConstSubr::new("Trait", trait_func, trait_t, None));
self.register_builtin_const("Trait", vis, ValueObj::Subr(trait_));
let subsume_t = func(
vec![kw("Super", TraitType)],
None,
vec![kw("Impl", Type), kw("Additional", Type)],
TraitType,
);
let subsume = ConstSubr::Builtin(BuiltinConstSubr::new(
"Subsume",
subsume_func,
subsume_t,
None,
));
self.register_builtin_const("Subsume", vis, ValueObj::Subr(subsume));
// decorators
let inheritable_t = func1(ClassType, ClassType);
let inheritable = ConstSubr::Builtin(BuiltinConstSubr::new(
"Inheritable",
inheritable_func,
inheritable_t,
None,
));
self.register_builtin_const("Inheritable", vis, ValueObj::Subr(inheritable));
// TODO: register Del function object
let t_del = nd_func(vec![kw("obj", Obj)], None, NoneType);
self.register_builtin_erg_impl("Del", t_del, Immutable, vis);
let patch_t = func(
vec![kw("Requirement", Type)],
None,
vec![kw("Impl", Type)],
TraitType,
);
let patch = ConstSubr::Builtin(BuiltinConstSubr::new("Patch", patch_func, patch_t, None));
self.register_builtin_const("Patch", vis, ValueObj::Subr(patch));
}
pub(super) fn init_builtin_operators(&mut self) {
/* binary */
let R = mono_q("R", instanceof(Type));
let params = vec![ty_tp(R.clone())];
let L = mono_q("L", subtypeof(poly("Add", params.clone())));
let op_t = nd_func(
vec![kw("lhs", L.clone()), kw("rhs", R.clone())],
None,
proj(L, "Output"),
)
.quantify();
self.register_builtin_erg_impl("__add__", op_t, Const, Private);
let L = mono_q("L", subtypeof(poly("Sub", params.clone())));
let op_t = bin_op(L.clone(), R.clone(), proj(L, "Output")).quantify();
self.register_builtin_erg_impl("__sub__", op_t, Const, Private);
let L = mono_q("L", subtypeof(poly("Mul", params.clone())));
let op_t = bin_op(L.clone(), R.clone(), proj(L, "Output")).quantify();
self.register_builtin_erg_impl("__mul__", op_t, Const, Private);
let L = mono_q("L", subtypeof(poly("Div", params.clone())));
let op_t = bin_op(L.clone(), R.clone(), proj(L, "Output")).quantify();
self.register_builtin_erg_impl("__div__", op_t, Const, Private);
let L = mono_q("L", subtypeof(poly("FloorDiv", params)));
let op_t = bin_op(L.clone(), R, proj(L, "Output")).quantify();
self.register_builtin_erg_impl("__floordiv__", op_t, Const, Private);
let P = mono_q("P", Constraint::Uninited);
let P = mono_q("P", subtypeof(poly("Mul", vec![ty_tp(P)])));
let op_t = bin_op(P.clone(), P.clone(), proj(P, "PowOutput")).quantify();
// TODO: add bound: M == M.Output
self.register_builtin_erg_impl("__pow__", op_t, Const, Private);
let M = mono_q("M", Constraint::Uninited);
let M = mono_q("M", subtypeof(poly("Div", vec![ty_tp(M)])));
let op_t = bin_op(M.clone(), M.clone(), proj(M, "ModOutput")).quantify();
self.register_builtin_erg_impl("__mod__", op_t, Const, Private);
let op_t = nd_proc(vec![kw("lhs", Obj), kw("rhs", Obj)], None, Bool);
self.register_builtin_erg_impl("__is__!", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__isnot__!", op_t, Const, Private);
let E = mono_q("E", subtypeof(mono("Eq")));
let op_t = bin_op(E.clone(), E, Bool).quantify();
self.register_builtin_erg_impl("__eq__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__ne__", op_t, Const, Private);
let O = mono_q("O", subtypeof(mono("Ord")));
let op_t = bin_op(O.clone(), O.clone(), Bool).quantify();
self.register_builtin_erg_impl("__lt__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__le__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__gt__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__ge__", op_t, Const, Private);
let BT = mono_q("BT", subtypeof(or(Bool, Type)));
let op_t = bin_op(BT.clone(), BT.clone(), BT).quantify();
self.register_builtin_erg_impl("__and__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__or__", op_t, Const, Private);
let op_t = bin_op(O.clone(), O.clone(), range(O)).quantify();
self.register_builtin_erg_decl("__rng__", op_t.clone(), Private);
self.register_builtin_erg_decl("__lorng__", op_t.clone(), Private);
self.register_builtin_erg_decl("__rorng__", op_t.clone(), Private);
self.register_builtin_erg_decl("__orng__", op_t, Private);
// TODO: use existential type: |T: Type| (T, In(T)) -> Bool
let T = mono_q("T", instanceof(Type));
let I = mono_q("I", subtypeof(poly("In", vec![ty_tp(T.clone())])));
let op_t = bin_op(I, T, Bool).quantify();
self.register_builtin_erg_impl("__in__", op_t.clone(), Const, Private);
self.register_builtin_erg_impl("__notin__", op_t, Const, Private);
/* unary */
// TODO: +/- Bool would like to be warned
let M = mono_q("M", subtypeof(mono("Mutizable")));
let op_t = func1(M.clone(), proj(M, "MutType!")).quantify();
self.register_builtin_erg_impl("__mutate__", op_t, Const, Private);
let N = mono_q("N", subtypeof(mono(NUM)));
let op_t = func1(N.clone(), N).quantify();
self.register_builtin_erg_decl("__pos__", op_t.clone(), Private);
self.register_builtin_erg_decl("__neg__", op_t, Private);
}
}

View file

@ -0,0 +1,784 @@
//! defines type information for builtin objects (in `Context`)
//!
//! 組み込みオブジェクトの型情報を(Contextに)定義
#![allow(non_snake_case)]
mod classes;
pub mod const_func;
mod funcs;
mod patches;
mod procs;
mod traits;
use std::path::PathBuf;
use erg_common::config::ErgConfig;
use erg_common::dict;
use erg_common::env::erg_pystd_path;
use erg_common::error::Location;
use erg_common::fresh::fresh_varname;
#[allow(unused_imports)]
use erg_common::log;
use erg_common::vis::Visibility;
use erg_common::Str;
use erg_common::{set, unique_in_place};
use erg_parser::ast::VarName;
use crate::context::initialize::const_func::*;
use crate::context::instantiate::ConstTemplate;
use crate::context::{
ClassDefType, Context, ContextKind, MethodInfo, ModuleContext, ParamSpec, TraitImpl,
};
use crate::module::{SharedCompilerResource, SharedModuleCache};
use crate::ty::free::Constraint;
use crate::ty::value::ValueObj;
use crate::ty::Type;
use crate::ty::{constructors::*, BuiltinConstSubr, ConstSubr, Predicate};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use Mutability::*;
use ParamSpec as PS;
use Type::*;
use VarKind::*;
use Visibility::*;
const NUM: &str = "Num";
const UNPACK: &str = "Unpack";
const INHERITABLE_TYPE: &str = "InheritableType";
const NAMED: &str = "Named";
const MUTABLE: &str = "Mutable";
const SELF: &str = "Self";
const IMMUTIZABLE: &str = "Immutizable";
const IMMUT_TYPE: &str = "ImmutType";
const PROC_UPDATE: &str = "update!";
const MUTIZABLE: &str = "Mutizable";
const MUTABLE_MUT_TYPE: &str = "MutType!";
const PATH_LIKE: &str = "PathLike";
const MUTABLE_READABLE: &str = "Readable!";
const READABLE: &str = "Readable";
const FUNC_READ: &str = "read";
const PROC_READ: &str = "read!";
const WRITABLE: &str = "Writable";
const MUTABLE_WRITABLE: &str = "Writable!";
const FUNC_WRITE: &str = "write";
const PROC_WRITE: &str = "write!";
const FILE_LIKE: &str = "FileLike";
const MUTABLE_FILE_LIKE: &str = "FileLike!";
const SHOW: &str = "Show";
const INPUT: &str = "Input";
const OUTPUT: &str = "Output";
const POW_OUTPUT: &str = "PowOutput";
const MOD_OUTPUT: &str = "ModOutput";
const IN: &str = "In";
const EQ: &str = "Eq";
const ORD: &str = "Ord";
const TO_STR: &str = "to_str";
const ORDERING: &str = "Ordering";
const SEQ: &str = "Seq";
const FUNC_LEN: &str = "len";
const FUNC_GET: &str = "get";
const ITERABLE: &str = "Iterable";
const ITERATOR: &str = "Iterator";
const STR_ITERATOR: &str = "StrIterator";
const FUNC_ITER: &str = "iter";
const ITER: &str = "Iter";
const ADD: &str = "Add";
const SUB: &str = "Sub";
const MUL: &str = "Mul";
const DIV: &str = "Div";
const FLOOR_DIV: &str = "FloorDiv";
const NEVER: &str = "Never";
const OBJ: &str = "Obj";
const MUTABLE_OBJ: &str = "Obj!";
const FUNC_CLONE: &str = "clone";
const BYTES: &str = "Bytes";
const FLOAT: &str = "Float";
const MUT_FLOAT: &str = "Float!";
const EPSILON: &str = "EPSILON";
const REAL: &str = "Real";
const FUNC_REAL: &str = "real";
const IMAG: &str = "Imag";
const FUNC_IMAG: &str = "imag";
const FUNC_CONJUGATE: &str = "conjugate";
const FUNC_IS_INTEGER: &str = "is_integer";
const FUNC_HEX: &str = "hex";
const FUNC_FROMHEX: &str = "fromhex";
const INT: &str = "Int";
const MUT_INT: &str = "Int!";
const RATIO: &str = "Ratio";
const MUT_RATIO: &str = "Raltio!";
const FUNC_ABS: &str = "abs";
const FUNC_SUCC: &str = "succ";
const FUNC_PRED: &str = "pred";
const FUNC_BIT_LENGTH: &str = "bit_length";
const FUNC_BIT_COUNT: &str = "bit_count";
const FUNC_BYTEORDER: &str = "byteorder";
const TOKEN_BIG_ENDIAN: &str = "big";
const TOKEN_LITTLE_ENDIAN: &str = "little";
const FUNC_FROM_BYTES: &str = "from_bytes";
const FUNC_TO_BYTES: &str = "to_bytes";
const NAT: &str = "Nat";
const MUT_NAT: &str = "Nat!";
const PROC_TIMES: &str = "times!";
const FUNC_TIMES: &str = "times";
const BOOL: &str = "Bool";
const MUT_BOOL: &str = "Bool!";
const STR: &str = "Str";
const MUT_STR: &str = "Str!";
const FUNC_REPLACE: &str = "replace";
const FUNC_ENCODE: &str = "encode";
const FUNC_FORMAT: &str = "format";
const FUNC_LOWER: &str = "lower";
const FUNC_UPPER: &str = "upper";
const FUNC_TO_INT: &str = "to_int";
const NONE_TYPE: &str = "NoneType";
const TYPE: &str = "Type";
const CLASS_TYPE: &str = "ClassType";
const TRAIT_TYPE: &str = "TraitType";
const CODE: &str = "Code";
const FUNC_MRO: &str = "mro";
const FUNC_CO_ARGCOUNT: &str = "co_argcount";
const FUNC_CO_VARNAMES: &str = "co_varnames";
const FUNC_CO_CONSTS: &str = "co_consts";
const FUNC_CO_NAMES: &str = "co_names";
const FUNC_CO_FREEVARS: &str = "co_freevars";
const FUNC_CO_CELLVARS: &str = "co_cellvars";
const FUNC_CO_FILENAME: &str = "co_filename";
const FUNC_CO_NAME: &str = "co_name";
const FUNC_CO_FIRSTLINENO: &str = "co_firstlineno";
const FUNC_CO_STACKSIZE: &str = "co_stacksize";
const FUNC_CO_FLAGS: &str = "co_flags";
const FUNC_CO_CODE: &str = "co_code";
const FUNC_CO_LNOTAB: &str = "co_lnotab";
const FUNC_CO_NLOCALS: &str = "co_nlocals";
const FUNC_CO_KWONLYARGCOUNT: &str = "co_kwonlyargcount";
const FUNC_CO_POSONLYARGCOUNT: &str = "co_posonlyargcount";
const GENERIC_MODULE: &str = "GenericModule";
const PATH: &str = "Path";
const MODULE: &str = "Module";
const PY_MODULE: &str = "PyModule";
const ARRAY: &str = "Array";
const MUT_ARRAY: &str = "Array!";
const FUNC_CONCAT: &str = "concat";
const FUNC_COUNT: &str = "count";
const FUNC_PUSH: &str = "push";
const PROC_PUSH: &str = "push!";
const ARRAY_ITERATOR: &str = "ArrayIterator";
const SET: &str = "Set";
const MUT_SET: &str = "Set!";
const GENERIC_DICT: &str = "GenericDict";
const DICT: &str = "Dict";
const FUNC_DECODE: &str = "decode";
const GENERIC_TUPLE: &str = "GenericTuple";
const TUPLE: &str = "Tuple";
const RECORD: &str = "Record";
const OR: &str = "Or";
const RANGE_ITERATOR: &str = "RangeIterator";
const ENUMERATE: &str = "Enumerate";
const FILTER: &str = "Filter";
const MAP: &str = "Map";
const REVERSED: &str = "Reversed";
const ZIP: &str = "Zip";
const FUNC_INC: &str = "inc";
const PROC_INC: &str = "inc!";
const FUNC_DEC: &str = "dec";
const PROC_DEC: &str = "dec!";
const MUT_FILE: &str = "File!";
const MUT_READABLE: &str = "Readable!";
const MUT_WRITABLE: &str = "Writable!";
const MUT_FILE_LIKE: &str = "FileLike!";
const FUNC_APPEND: &str = "append";
const FUNC_EXTEND: &str = "extend";
const PROC_EXTEND: &str = "extend!";
const FUNC_INSERT: &str = "insert";
const PROC_INSERT: &str = "insert!";
const FUNC_REMOVE: &str = "remove";
const PROC_REMOVE: &str = "remove!";
const FUNC_POP: &str = "pop";
const PROC_POP: &str = "pop!";
const FUNC_CLEAR: &str = "clear";
const PROC_CLEAR: &str = "clear!";
const FUNC_SORT: &str = "sort";
const PROC_SORT: &str = "sort!";
const FUNC_REVERSE: &str = "reverse";
const PROC_REVERSE: &str = "reverse!";
const PROC_STRICT_MAP: &str = "strict_map!";
const FUNC_ADD: &str = "add";
const PROC_ADD: &str = "add!";
const RANGE: &str = "Range";
const GENERIC_CALLABLE: &str = "GenericCallable";
const GENERIC_GENERATOR: &str = "GenericGenerator";
const FUNC_RETURN: &str = "return";
const FUNC_YIELD: &str = "yield";
const PROC: &str = "Proc";
const NAMED_PROC: &str = "NamedProc";
const NAMED_FUNC: &str = "NamedFunc";
const FUNC: &str = "Func";
const QUANTIFIED: &str = "Quantified";
const QUANTIFIED_FUNC: &str = "QuantifiedFunc";
const FUNC_OBJECT: &str = "object";
const FUNC_INT: &str = "int";
const FUNC_FLOAT: &str = "float";
const FUNC_BOOL: &str = "bool";
const FUNC_STR: &str = "str";
const FUNC_TYPE: &str = "type";
const CODE_TYPE: &str = "CodeType";
const MODULE_TYPE: &str = "ModuleType";
const FUNC_LIST: &str = "list";
const FUNC_SET: &str = "set";
const FUNC_DICT: &str = "dict";
const FUNC_TUPLE: &str = "tuple";
const UNION: &str = "Union";
const FUNC_STR_ITERATOR: &str = "str_iterator";
const FUNC_ARRAY_ITERATOR: &str = "array_iterator";
const FUNC_ENUMERATE: &str = "enumerate";
const FUNC_FILTER: &str = "filter";
const FUNC_MAP: &str = "map";
const FUNC_REVERSED: &str = "reversed";
const FUNC_ZIP: &str = "zip";
const FILE: &str = "File";
const CALLABLE: &str = "Callable";
const GENERATOR: &str = "Generator";
const FUNC_RANGE: &str = "range";
const OP_IN: &str = "__in__";
const OP_EQ: &str = "__eq__";
const OP_CMP: &str = "__cmp__";
const OP_ADD: &str = "__add__";
const OP_SUB: &str = "__sub__";
const OP_MUL: &str = "__mul__";
const OP_DIV: &str = "__div__";
const OP_FLOOR_DIV: &str = "__floordiv__";
const OP_ABS: &str = "__abs__";
const OP_PARTIAL_CMP: &str = "__partial_cmp__";
const OP_AND: &str = "__and__";
const OP_OR: &str = "__or__";
const FUNDAMENTAL_NAME: &str = "__name__";
const FUNDAMENTAL_STR: &str = "__str__";
const FUNDAMENTAL_ITER: &str = "__iter__";
const FUNDAMENTAL_MODULE: &str = "__module__";
const FUNDAMENTAL_SIZEOF: &str = "__sizeof__";
const FUNDAMENTAL_REPR: &str = "__repr__";
const FUNDAMENTAL_DICT: &str = "__dict__";
const FUNDAMENTAL_BYTES: &str = "__bytes__";
const FUNDAMENTAL_GETITEM: &str = "__getitem__";
const FUNDAMENTAL_TUPLE_GETITEM: &str = "__Tuple_getitem__";
const LICENSE: &str = "license";
const CREDITS: &str = "credits";
const COPYRIGHT: &str = "copyright";
const TRUE: &str = "True";
const FALSE: &str = "False";
const NONE: &str = "None";
const NOT_IMPLEMENTED: &str = "NotImplemented";
const ELLIPSIS: &str = "Ellipsis";
const SITEBUILTINS_PRINTER: &str = "_sitebuiltins._Printer";
const TY_D: &str = "D";
const TY_T: &str = "T";
const TY_TS: &str = "Ts";
const TY_I: &str = "I";
const TY_R: &str = "R";
const TY_U: &str = "U";
const TY_L: &str = "L";
const TY_N: &str = "N";
const TY_M: &str = "M";
const KW_OLD: &str = "old";
const KW_N: &str = "n";
const KW_S: &str = "s";
const KW_SELF: &str = "self";
const KW_LENGTH: &str = "length";
const KW_PROC: &str = "proc!";
const KW_PAT: &str = "pat";
const KW_INTO: &str = "into";
const KW_ENCODING: &str = "encoding";
const KW_ERRORS: &str = "errors";
const KW_ARGS: &str = "args";
const KW_IDX: &str = "idx";
const KW_RHS: &str = "rhs";
const KW_X: &str = "x";
const KW_ELEM: &str = "elem";
const KW_FUNC: &str = "func";
const KW_ITERABLE: &str = "iterable";
const KW_INDEX: &str = "index";
const KW_KEY: &str = "key";
pub fn builtins_path() -> PathBuf {
erg_pystd_path().join("builtins.d.er")
}
impl Context {
fn register_builtin_decl(
&mut self,
name: &'static str,
t: Type,
vis: Visibility,
py_name: Option<&'static str>,
) {
if cfg!(feature = "debug") {
if let Type::Subr(subr) = &t {
if subr.has_qvar() {
panic!("not quantified subr: {subr}");
}
}
}
let impl_of = if let ContextKind::MethodDefs(Some(tr)) = &self.kind {
Some(tr.clone())
} else {
None
};
let name = VarName::from_static(name);
if self.decls.get(&name).is_some() {
panic!("already registered: {} {name}", self.name);
} else {
let vi = VarInfo::new(
t,
Immutable,
vis,
Builtin,
None,
impl_of,
py_name.map(Str::ever),
AbsLocation::unknown(),
);
self.decls.insert(name, vi);
}
}
fn register_builtin_erg_decl(&mut self, name: &'static str, t: Type, vis: Visibility) {
self.register_builtin_decl(name, t, vis, None);
}
fn register_builtin_py_decl(
&mut self,
name: &'static str,
t: Type,
vis: Visibility,
py_name: Option<&'static str>,
) {
self.register_builtin_decl(name, t, vis, py_name);
}
fn register_builtin_impl(
&mut self,
name: VarName,
t: Type,
muty: Mutability,
vis: Visibility,
py_name: Option<&'static str>,
loc: AbsLocation,
) {
if cfg!(feature = "debug") {
if let Type::Subr(subr) = &t {
if subr.has_qvar() {
panic!("not quantified subr: {subr}");
}
}
}
let impl_of = if let ContextKind::MethodDefs(Some(tr)) = &self.kind {
Some(tr.clone())
} else {
None
};
let vi = VarInfo::new(
t,
muty,
vis,
Builtin,
None,
impl_of,
py_name.map(Str::ever),
loc,
);
if let Some(_vi) = self.locals.get(&name) {
if _vi != &vi {
panic!("already registered: {} {name}", self.name);
}
} else {
self.locals.insert(name, vi);
}
}
fn register_builtin_erg_impl(
&mut self,
name: &'static str,
t: Type,
muty: Mutability,
vis: Visibility,
) {
let name = VarName::from_static(name);
self.register_builtin_impl(name, t, muty, vis, None, AbsLocation::unknown());
}
// TODO: replace with `register_builtins`
fn register_builtin_py_impl(
&mut self,
name: &'static str,
t: Type,
muty: Mutability,
vis: Visibility,
py_name: Option<&'static str>,
) {
let name = if cfg!(feature = "py_compatible") {
if let Some(py_name) = py_name {
VarName::from_static(py_name)
} else {
VarName::from_static(name)
}
} else {
VarName::from_static(name)
};
self.register_builtin_impl(name, t, muty, vis, py_name, AbsLocation::unknown());
}
pub(crate) fn register_py_builtin(
&mut self,
name: &'static str,
t: Type,
py_name: Option<&'static str>,
lineno: u32,
) {
let name = if cfg!(feature = "py_compatible") {
if let Some(py_name) = py_name {
VarName::from_static(py_name)
} else {
VarName::from_static(name)
}
} else {
VarName::from_static(name)
};
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
let muty = Immutable;
let loc = Location::range(lineno, 0, lineno, name.inspect().len() as u32);
let abs_loc = AbsLocation::new(Some(builtins_path()), loc);
self.register_builtin_impl(name, t, muty, vis, py_name, abs_loc);
}
fn register_builtin_const(&mut self, name: &str, vis: Visibility, obj: ValueObj) {
if self.rec_get_const_obj(name).is_some() {
panic!("already registered: {} {name}", self.name);
} else {
let impl_of = if let ContextKind::MethodDefs(Some(tr)) = &self.kind {
Some(tr.clone())
} else {
None
};
// TODO: not all value objects are comparable
let vi = VarInfo::new(
v_enum(set! {obj.clone()}),
Const,
vis,
Builtin,
None,
impl_of,
None,
AbsLocation::unknown(),
);
self.consts.insert(VarName::from_str(Str::rc(name)), obj);
self.locals.insert(VarName::from_str(Str::rc(name)), vi);
}
}
fn register_const_param_defaults(&mut self, name: &'static str, params: Vec<ConstTemplate>) {
if self.const_param_defaults.get(name).is_some() {
panic!("already registered: {} {name}", self.name);
} else {
self.const_param_defaults.insert(Str::ever(name), params);
}
}
/// FIXME: トレイトの汎化型を指定するのにも使っているので、この名前は適当でない
pub(crate) fn register_superclass(&mut self, sup: Type, sup_ctx: &Context) {
self.super_classes.push(sup);
self.super_classes.extend(sup_ctx.super_classes.clone());
self.super_traits.extend(sup_ctx.super_traits.clone());
unique_in_place(&mut self.super_classes);
unique_in_place(&mut self.super_traits);
}
pub(crate) fn register_supertrait(&mut self, sup: Type, sup_ctx: &Context) {
self.super_traits.push(sup);
self.super_traits.extend(sup_ctx.super_traits.clone());
unique_in_place(&mut self.super_traits);
}
fn register_builtin_type(
&mut self,
t: Type,
ctx: Self,
vis: Visibility,
muty: Mutability,
py_name: Option<&'static str>,
) {
if t.typarams_len().is_none() {
self.register_mono_type(t, ctx, vis, muty, py_name);
} else {
self.register_poly_type(t, ctx, vis, muty, py_name);
}
}
fn register_mono_type(
&mut self,
t: Type,
ctx: Self,
vis: Visibility,
muty: Mutability,
py_name: Option<&'static str>,
) {
if self.rec_get_mono_type(&t.local_name()).is_some() {
panic!("{} has already been registered", t.local_name());
} else if self.rec_get_const_obj(&t.local_name()).is_some() {
panic!("{} has already been registered as const", t.local_name());
} else {
let name = VarName::from_str(t.local_name());
let meta_t = match ctx.kind {
ContextKind::Class => Type::ClassType,
ContextKind::Trait => Type::TraitType,
_ => Type::Type,
};
self.locals.insert(
name.clone(),
VarInfo::new(
meta_t,
muty,
vis,
Builtin,
None,
None,
py_name.map(Str::ever),
AbsLocation::unknown(),
),
);
self.consts
.insert(name.clone(), ValueObj::builtin_t(t.clone()));
for impl_trait in ctx.super_traits.iter() {
if let Some(impls) = self.trait_impls.get_mut(&impl_trait.qual_name()) {
impls.insert(TraitImpl::new(t.clone(), impl_trait.clone()));
} else {
self.trait_impls.insert(
impl_trait.qual_name(),
set![TraitImpl::new(t.clone(), impl_trait.clone())],
);
}
}
for (trait_method, vi) in ctx.decls.iter() {
if let Some(types) = self.method_to_traits.get_mut(trait_method.inspect()) {
types.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_traits.insert(
trait_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
for (class_method, vi) in ctx.locals.iter() {
if let Some(types) = self.method_to_classes.get_mut(class_method.inspect()) {
types.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_classes.insert(
class_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
self.mono_types.insert(name, (t, ctx));
}
}
// FIXME: MethodDefsと再代入は違う
fn register_poly_type(
&mut self,
t: Type,
ctx: Self,
vis: Visibility,
muty: Mutability,
py_name: Option<&'static str>,
) {
// FIXME: panic
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.local_name()) {
root_ctx.methods_list.push((ClassDefType::Simple(t), ctx));
} else {
let name = VarName::from_str(t.local_name());
let meta_t = match ctx.kind {
ContextKind::Class => Type::ClassType,
ContextKind::Trait => Type::TraitType,
_ => Type::Type,
};
if !cfg!(feature = "py_compatible") {
self.locals.insert(
name.clone(),
VarInfo::new(
meta_t,
muty,
vis,
Builtin,
None,
None,
py_name.map(Str::ever),
AbsLocation::unknown(),
),
);
}
self.consts
.insert(name.clone(), ValueObj::builtin_t(t.clone()));
for impl_trait in ctx.super_traits.iter() {
if let Some(impls) = self.trait_impls.get_mut(&impl_trait.qual_name()) {
impls.insert(TraitImpl::new(t.clone(), impl_trait.clone()));
} else {
self.trait_impls.insert(
impl_trait.qual_name(),
set![TraitImpl::new(t.clone(), impl_trait.clone())],
);
}
}
for (trait_method, vi) in ctx.decls.iter() {
if let Some(traits) = self.method_to_traits.get_mut(trait_method.inspect()) {
traits.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_traits.insert(
trait_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
for (class_method, vi) in ctx.locals.iter() {
if let Some(types) = self.method_to_classes.get_mut(class_method.inspect()) {
types.push(MethodInfo::new(t.clone(), vi.clone()));
} else {
self.method_to_classes.insert(
class_method.inspect().clone(),
vec![MethodInfo::new(t.clone(), vi.clone())],
);
}
}
self.poly_types.insert(name, (t, ctx));
}
}
fn register_builtin_patch(
&mut self,
name: &'static str,
ctx: Self,
vis: Visibility,
muty: Mutability,
) {
if self.patches.contains_key(name) {
panic!("{} has already been registered", name);
} else {
let name = VarName::from_static(name);
let vi = VarInfo::new(
Patch,
muty,
vis,
Builtin,
None,
None,
None,
AbsLocation::unknown(),
);
self.locals.insert(name.clone(), vi);
for method_name in ctx.locals.keys() {
if let Some(patches) = self.method_impl_patches.get_mut(method_name) {
patches.push(name.clone());
} else {
self.method_impl_patches
.insert(method_name.clone(), vec![name.clone()]);
}
}
if let ContextKind::GluePatch(tr_inst) = &ctx.kind {
if let Some(impls) = self.trait_impls.get_mut(&tr_inst.sup_trait.qual_name()) {
impls.insert(tr_inst.clone());
} else {
self.trait_impls
.insert(tr_inst.sup_trait.qual_name(), set![tr_inst.clone()]);
}
}
self.patches.insert(name, ctx);
}
}
fn init_builtin_consts(&mut self) {
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
// TODO: this is not a const, but a special property
self.register_builtin_py_impl(
FUNDAMENTAL_NAME,
Str,
Immutable,
vis,
Some(FUNDAMENTAL_NAME),
);
self.register_builtin_py_impl(
LICENSE,
mono(SITEBUILTINS_PRINTER),
Immutable,
vis,
Some(LICENSE),
);
self.register_builtin_py_impl(
CREDITS,
mono(SITEBUILTINS_PRINTER),
Immutable,
vis,
Some(CREDITS),
);
self.register_builtin_py_impl(
COPYRIGHT,
mono(SITEBUILTINS_PRINTER),
Immutable,
vis,
Some(COPYRIGHT),
);
self.register_builtin_py_impl(TRUE, Bool, Const, Private, Some(TRUE));
self.register_builtin_py_impl(FALSE, Bool, Const, Private, Some(FALSE));
self.register_builtin_py_impl(NONE, NoneType, Const, Private, Some(NONE));
self.register_builtin_py_impl(
NOT_IMPLEMENTED,
NotImplementedType,
Const,
Private,
Some(NOT_IMPLEMENTED),
);
self.register_builtin_py_impl(ELLIPSIS, Ellipsis, Const, Private, Some(ELLIPSIS));
}
pub(crate) fn init_builtins(cfg: ErgConfig, mod_cache: &SharedModuleCache) {
let mut ctx = Context::builtin_module("<builtins>", cfg, 100);
ctx.init_builtin_consts();
ctx.init_builtin_funcs();
ctx.init_builtin_const_funcs();
ctx.init_builtin_procs();
ctx.init_builtin_operators();
ctx.init_builtin_traits();
ctx.init_builtin_classes();
ctx.init_builtin_patches();
let module = ModuleContext::new(ctx, dict! {});
mod_cache.register(PathBuf::from("<builtins>"), None, module);
}
pub fn new_module<S: Into<Str>>(
name: S,
cfg: ErgConfig,
shared: SharedCompilerResource,
) -> Self {
Context::new(
name.into(),
cfg,
ContextKind::Module,
vec![],
None,
Some(shared),
Context::TOP_LEVEL,
)
}
}

View file

@ -0,0 +1,80 @@
#[allow(unused_imports)]
use erg_common::log;
use erg_common::vis::Visibility;
use crate::ty::constructors::*;
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::Type;
use Type::*;
use crate::context::initialize::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(super) fn init_builtin_patches(&mut self) {
let m = mono_q_tp("M", instanceof(Int));
let n = mono_q_tp("N", instanceof(Int));
let o = mono_q_tp("O", instanceof(Int));
let p = mono_q_tp("P", instanceof(Int));
let params = vec![
PS::named_nd("M", Int),
PS::named_nd("N", Int),
PS::named_nd("O", Int),
PS::named_nd("P", Int),
];
let class = Type::from(&m..=&n);
let impls = poly("Add", vec![TyParam::from(&o..=&p)]);
// Interval is a bounding patch connecting M..N and (Add(O..P, M+O..N..P), Sub(O..P, M-P..N-O))
let mut interval =
Self::builtin_poly_glue_patch("Interval", class.clone(), impls.clone(), params, 2);
let op_t = fn1_met(
class.clone(),
Type::from(&o..=&p),
Type::from(m.clone() + o.clone()..=n.clone() + p.clone()),
)
.quantify();
let mut interval_add = Self::builtin_methods(Some(impls), 2);
interval_add.register_builtin_erg_impl("__add__", op_t, Const, Public);
interval_add.register_builtin_const(
"Output",
Public,
ValueObj::builtin_t(Type::from(m.clone() + o.clone()..=n.clone() + p.clone())),
);
interval.register_trait(class.clone(), interval_add);
let mut interval_sub =
Self::builtin_methods(Some(poly("Sub", vec![TyParam::from(&o..=&p)])), 2);
let op_t = fn1_met(
class.clone(),
Type::from(&o..=&p),
Type::from(m.clone() - p.clone()..=n.clone() - o.clone()),
)
.quantify();
interval_sub.register_builtin_erg_impl("__sub__", op_t, Const, Public);
interval_sub.register_builtin_const(
"Output",
Public,
ValueObj::builtin_t(Type::from(m - p..=n - o)),
);
interval.register_trait(class, interval_sub);
self.register_builtin_patch("Interval", interval, Private, Const);
// eq.register_impl("__ne__", op_t, Const, Public);
// ord.register_impl("__le__", op_t.clone(), Const, Public);
// ord.register_impl("__gt__", op_t.clone(), Const, Public);
// ord.register_impl("__ge__", op_t, Const, Public);
let E = mono_q("E", subtypeof(mono("Eq")));
let base = or(E, NoneType);
let impls = mono("Eq");
let params = vec![PS::named_nd("E", Type)];
let mut option_eq =
Self::builtin_poly_glue_patch("OptionEq", base.clone(), impls.clone(), params, 1);
let mut option_eq_impl = Self::builtin_methods(Some(impls), 1);
let op_t = fn1_met(base.clone(), base.clone(), Bool).quantify();
option_eq_impl.register_builtin_erg_impl("__eq__", op_t, Const, Public);
option_eq.register_trait(base, option_eq_impl);
self.register_builtin_patch("OptionEq", option_eq, Private, Const);
}
}

View file

@ -0,0 +1,151 @@
#[allow(unused_imports)]
use erg_common::log;
use erg_common::vis::Visibility;
use crate::ty::constructors::*;
use crate::ty::typaram::TyParam;
use crate::ty::Type;
use Type::*;
use crate::context::initialize::*;
use crate::context::Context;
use crate::varinfo::Mutability;
use Mutability::*;
use Visibility::*;
impl Context {
pub(super) fn init_builtin_procs(&mut self) {
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
let T = mono_q("T", instanceof(Type));
let U = mono_q("U", instanceof(Type));
let t_dir = proc(
vec![kw("obj", ref_(Obj))],
None,
vec![],
array_t(Str, TyParam::erased(Nat)),
);
let t_print = proc(
vec![],
Some(kw("objects", ref_(Obj))),
vec![
kw("sep", Str),
kw("end", Str),
kw("file", mono("Writable!")),
kw("flush", Bool),
],
NoneType,
);
let t_id = nd_func(vec![kw("old", Obj)], None, Nat);
let t_input = proc(vec![], None, vec![kw("msg", Str)], Str);
let t_if = proc(
vec![
kw("cond", Bool),
kw("then", nd_proc(vec![], None, T.clone())),
],
None,
vec![kw_default(
"else",
nd_proc(vec![], None, U.clone()),
nd_proc(vec![], None, NoneType),
)],
or(T.clone(), U.clone()),
)
.quantify();
let t_for = nd_proc(
vec![
kw("iterable", poly("Iterable", vec![ty_tp(T.clone())])),
kw("proc!", nd_proc(vec![anon(T.clone())], None, NoneType)),
],
None,
NoneType,
)
.quantify();
let t_globals = proc(vec![], None, vec![], dict! { Str => Obj }.into());
let t_locals = proc(vec![], None, vec![], dict! { Str => Obj }.into());
let t_next = nd_proc(
vec![kw(
"iterable",
ref_mut(poly("Iterable", vec![ty_tp(T.clone())]), None),
)],
None,
T.clone(),
)
.quantify();
let t_cond = if cfg!(feature = "py_compatible") {
Bool
} else {
// not Bool! type because `cond` may be the result of evaluation of a mutable object's method returns Bool.
nd_proc(vec![], None, Bool)
};
let t_while = nd_proc(
vec![
kw("cond!", t_cond),
kw("proc!", nd_proc(vec![], None, NoneType)),
],
None,
NoneType,
);
let P = mono_q("P", subtypeof(mono("PathLike")));
let t_open = proc(
vec![kw("file", P)],
None,
vec![
kw("mode", Str),
kw("buffering", Int),
kw("encoding", or(Str, NoneType)),
kw("errors", or(Str, NoneType)),
kw("newline", or(Str, NoneType)),
kw("closefd", Bool),
// param_t("opener", option),
],
mono("File!"),
)
.quantify();
// TODO: T <: With
let t_with = nd_proc(
vec![
kw("obj", T.clone()),
kw("proc!", nd_proc(vec![anon(T)], None, U.clone())),
],
None,
U,
)
.quantify();
self.register_builtin_py_impl("dir!", t_dir, Immutable, vis, Some("dir"));
self.register_py_builtin("print!", t_print, Some("print"), 27);
self.register_builtin_py_impl("id!", t_id, Immutable, vis, Some("id"));
self.register_builtin_py_impl("input!", t_input, Immutable, vis, Some("input"));
self.register_builtin_py_impl("globals!", t_globals, Immutable, vis, Some("globals"));
self.register_builtin_py_impl("locals!", t_locals, Immutable, vis, Some("locals"));
self.register_builtin_py_impl("next!", t_next, Immutable, vis, Some("next"));
self.register_py_builtin("open!", t_open, Some("open"), 144);
let name = if cfg!(feature = "py_compatible") {
"if"
} else {
"if__"
};
self.register_builtin_py_impl("if!", t_if, Immutable, vis, Some(name));
let name = if cfg!(feature = "py_compatible") {
"for"
} else {
"for__"
};
self.register_builtin_py_impl("for!", t_for, Immutable, vis, Some(name));
let name = if cfg!(feature = "py_compatible") {
"while"
} else {
"while__"
};
self.register_builtin_py_impl("while!", t_while, Immutable, vis, Some(name));
let name = if cfg!(feature = "py_compatible") {
"with"
} else {
"with__"
};
self.register_builtin_py_impl("with!", t_with, Immutable, vis, Some(name));
}
}

View file

@ -0,0 +1,239 @@
#[allow(unused_imports)]
use erg_common::log;
use erg_common::vis::Visibility;
use crate::ty::constructors::*;
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::Type;
use ParamSpec as PS;
use Type::*;
use crate::context::initialize::*;
use crate::context::{ConstTemplate, Context, DefaultInfo, ParamSpec};
use crate::varinfo::Mutability;
use DefaultInfo::*;
use Mutability::*;
use Visibility::*;
impl Context {
/// see std/prelude.er
/// All type boundaries are defined in each subroutine
/// `push_subtype_bound`, etc. are used for type boundary determination in user-defined APIs
// 型境界はすべて各サブルーチンで定義する
// push_subtype_boundなどはユーザー定義APIの型境界決定のために使用する
pub(super) fn init_builtin_traits(&mut self) {
let vis = if cfg!(feature = "py_compatible") {
Public
} else {
Private
};
let unpack = Self::builtin_mono_trait(UNPACK, 2);
let inheritable_type = Self::builtin_mono_trait(INHERITABLE_TYPE, 2);
let named = Self::builtin_mono_trait(NAMED, 2);
let mut mutable = Self::builtin_mono_trait(MUTABLE, 2);
let Slf = mono_q(SELF, subtypeof(mono(IMMUTIZABLE)));
let immut_t = proj(Slf.clone(), IMMUT_TYPE);
let f_t = func(vec![kw(KW_OLD, immut_t.clone())], None, vec![], immut_t);
let t = pr1_met(ref_mut(Slf, None), f_t, NoneType).quantify();
mutable.register_builtin_erg_decl(PROC_UPDATE, t, Public);
// REVIEW: Immutatable?
let mut immutizable = Self::builtin_mono_trait(IMMUTIZABLE, 2);
immutizable.register_superclass(mono(MUTABLE), &mutable);
immutizable.register_builtin_erg_decl(IMMUT_TYPE, Type, Public);
// REVIEW: Mutatable?
let mut mutizable = Self::builtin_mono_trait(MUTIZABLE, 2);
mutizable.register_builtin_erg_decl(MUTABLE_MUT_TYPE, Type, Public);
let pathlike = Self::builtin_mono_trait(PATH_LIKE, 2);
/* Readable */
let mut readable = Self::builtin_mono_trait(MUTABLE_READABLE, 2);
let Slf = mono_q(SELF, subtypeof(mono(MUTABLE_READABLE)));
let t_read = pr_met(ref_mut(Slf, None), vec![], None, vec![kw(KW_N, Int)], Str).quantify();
readable.register_builtin_py_decl(PROC_READ, t_read, Public, Some(FUNC_READ));
/* Writable */
let mut writable = Self::builtin_mono_trait(MUTABLE_WRITABLE, 2);
let Slf = mono_q(SELF, subtypeof(mono(MUTABLE_WRITABLE)));
let t_write = pr1_kw_met(ref_mut(Slf, None), kw("s", Str), Nat).quantify();
writable.register_builtin_py_decl(PROC_WRITE, t_write, Public, Some(FUNC_WRITE));
// TODO: Add required methods
let mut filelike = Self::builtin_mono_trait(FILE_LIKE, 2);
filelike.register_superclass(mono(READABLE), &readable);
let mut filelike_mut = Self::builtin_mono_trait(MUTABLE_FILE_LIKE, 2);
filelike_mut.register_superclass(mono(FILE_LIKE), &filelike);
filelike_mut.register_superclass(mono(MUTABLE_WRITABLE), &writable);
/* Show */
let mut show = Self::builtin_mono_trait(SHOW, 2);
let Slf = mono_q(SELF, subtypeof(mono(SHOW)));
let t_show = fn0_met(ref_(Slf), Str).quantify();
show.register_builtin_py_decl(TO_STR, t_show, Public, Some(FUNDAMENTAL_STR));
/* In */
let mut in_ = Self::builtin_poly_trait(IN, vec![PS::t(TY_T, NonDefault)], 2);
let params = vec![PS::t(TY_T, NonDefault)];
let input = Self::builtin_poly_trait(INPUT, params.clone(), 2);
let output = Self::builtin_poly_trait(OUTPUT, params, 2);
let T = mono_q(TY_T, instanceof(Type));
let I = mono_q(TY_I, subtypeof(poly(IN, vec![ty_tp(T.clone())])));
in_.register_superclass(poly(INPUT, vec![ty_tp(T.clone())]), &input);
let op_t = fn1_met(T.clone(), I, Bool).quantify();
in_.register_builtin_erg_decl(OP_IN, op_t, Public);
/* Eq */
// Erg does not have a trait equivalent to `PartialEq` in Rust
// This means, Erg's `Float` cannot be compared with other `Float`
// use `l - r < EPSILON` to check if two floats are almost equal
let mut eq = Self::builtin_mono_trait(EQ, 2);
let Slf = mono_q(SELF, subtypeof(mono(EQ)));
// __eq__: |Self <: Eq| (self: Self, other: Self) -> Bool
let op_t = fn1_met(Slf.clone(), Slf, Bool).quantify();
eq.register_builtin_erg_decl(OP_EQ, op_t, Public);
/* Ord */
let mut ord = Self::builtin_mono_trait(ORD, 2);
ord.register_superclass(mono(EQ), &eq);
let Slf = mono_q(SELF, subtypeof(mono(ORD)));
let op_t = fn1_met(Slf.clone(), Slf, or(mono(ORDERING), NoneType)).quantify();
ord.register_builtin_erg_decl(OP_CMP, op_t, Public);
// FIXME: poly trait
/* Num */
let num = Self::builtin_mono_trait(NUM, 2);
/* vec![
poly(ADD, vec![]),
poly(SUB, vec![]),
poly(MUL, vec![]),
], */
/* Seq */
let mut seq = Self::builtin_poly_trait(SEQ, vec![PS::t(TY_T, NonDefault)], 2);
seq.register_superclass(poly(OUTPUT, vec![ty_tp(T.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(SEQ, vec![TyParam::erased(Type)])));
let t = fn0_met(Slf.clone(), Nat).quantify();
seq.register_builtin_erg_decl(FUNC_LEN, t, Public);
let t = fn1_met(Slf, Nat, T.clone()).quantify();
// Seq.get: |Self <: Seq(T)| Self.(Nat) -> T
seq.register_builtin_erg_decl(FUNC_GET, t, Public);
/* Iterable */
let mut iterable = Self::builtin_poly_trait(ITERABLE, vec![PS::t(TY_T, NonDefault)], 2);
iterable.register_superclass(poly(OUTPUT, vec![ty_tp(T.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(ITERABLE, vec![ty_tp(T.clone())])));
let t = fn0_met(Slf.clone(), proj(Slf, ITER)).quantify();
iterable.register_builtin_py_decl(FUNC_ITER, t, Public, Some(FUNDAMENTAL_ITER));
iterable.register_builtin_erg_decl(ITER, Type, Public);
let R = mono_q(TY_R, instanceof(Type));
let params = vec![PS::t(TY_R, WithDefault)];
let ty_params = vec![ty_tp(R.clone())];
/* Num */
let mut add = Self::builtin_poly_trait(ADD, params.clone(), 2);
// Covariant with `R` (independent of the type of __add__)
add.register_superclass(poly(OUTPUT, vec![ty_tp(R.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(ADD, ty_params.clone())));
let op_t = fn1_met(Slf.clone(), R.clone(), proj(Slf, OUTPUT)).quantify();
add.register_builtin_erg_decl(OP_ADD, op_t, Public);
add.register_builtin_erg_decl(OUTPUT, Type, Public);
/* Sub */
let mut sub = Self::builtin_poly_trait(SUB, params.clone(), 2);
sub.register_superclass(poly(OUTPUT, vec![ty_tp(R.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(SUB, ty_params.clone())));
let op_t = fn1_met(Slf.clone(), R.clone(), proj(Slf, OUTPUT)).quantify();
sub.register_builtin_erg_decl(OP_SUB, op_t, Public);
sub.register_builtin_erg_decl(OUTPUT, Type, Public);
/* Mul */
let mut mul = Self::builtin_poly_trait(MUL, params.clone(), 2);
mul.register_superclass(poly(OUTPUT, vec![ty_tp(R.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(MUL, ty_params.clone())));
let op_t = fn1_met(Slf.clone(), R.clone(), proj(Slf, OUTPUT)).quantify();
mul.register_builtin_erg_decl(OP_MUL, op_t, Public);
mul.register_builtin_erg_decl(OUTPUT, Type, Public);
/* Div */
let mut div = Self::builtin_poly_trait(DIV, params.clone(), 2);
div.register_superclass(poly(OUTPUT, vec![ty_tp(R.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(DIV, ty_params.clone())));
let op_t = fn1_met(Slf.clone(), R.clone(), proj(Slf, OUTPUT)).quantify();
div.register_builtin_erg_decl(OP_DIV, op_t, Public);
div.register_builtin_erg_decl(OUTPUT, Type, Public);
/* FloorDiv */
let mut floor_div = Self::builtin_poly_trait(FLOOR_DIV, params, 2);
floor_div.register_superclass(poly(OUTPUT, vec![ty_tp(R.clone())]), &output);
let Slf = mono_q(SELF, subtypeof(poly(FLOOR_DIV, ty_params.clone())));
let op_t = fn1_met(Slf.clone(), R, proj(Slf.clone(), OUTPUT)).quantify();
floor_div.register_builtin_erg_decl(OP_FLOOR_DIV, op_t, Public);
floor_div.register_builtin_erg_decl(OUTPUT, Type, Public);
self.register_builtin_type(mono(UNPACK), unpack, vis, Const, None);
self.register_builtin_type(
mono(INHERITABLE_TYPE),
inheritable_type,
Private,
Const,
None,
);
self.register_builtin_type(mono(NAMED), named, vis, Const, None);
self.register_builtin_type(mono(MUTABLE), mutable, vis, Const, None);
self.register_builtin_type(mono(IMMUTIZABLE), immutizable, vis, Const, None);
self.register_builtin_type(mono(MUTIZABLE), mutizable, vis, Const, None);
self.register_builtin_type(mono(PATH_LIKE), pathlike, vis, Const, None);
self.register_builtin_type(
mono(MUTABLE_READABLE),
readable,
Private,
Const,
Some(READABLE),
);
self.register_builtin_type(
mono(MUTABLE_WRITABLE),
writable,
Private,
Const,
Some(WRITABLE),
);
self.register_builtin_type(mono(FILE_LIKE), filelike, vis, Const, None);
self.register_builtin_type(mono(MUTABLE_FILE_LIKE), filelike_mut, vis, Const, None);
self.register_builtin_type(mono(SHOW), show, vis, Const, None);
self.register_builtin_type(
poly(INPUT, vec![ty_tp(T.clone())]),
input,
Private,
Const,
None,
);
self.register_builtin_type(
poly(OUTPUT, vec![ty_tp(T.clone())]),
output,
Private,
Const,
None,
);
self.register_builtin_type(poly(IN, vec![ty_tp(T.clone())]), in_, Private, Const, None);
self.register_builtin_type(mono(EQ), eq, vis, Const, None);
self.register_builtin_type(mono(ORD), ord, vis, Const, None);
self.register_builtin_type(mono(NUM), num, vis, Const, None);
self.register_builtin_type(poly(SEQ, vec![ty_tp(T.clone())]), seq, Private, Const, None);
self.register_builtin_type(
poly(ITERABLE, vec![ty_tp(T)]),
iterable,
Private,
Const,
None,
);
self.register_builtin_type(poly(ADD, ty_params.clone()), add, vis, Const, None);
self.register_builtin_type(poly(SUB, ty_params.clone()), sub, vis, Const, None);
self.register_builtin_type(poly(MUL, ty_params.clone()), mul, vis, Const, None);
self.register_builtin_type(poly(DIV, ty_params.clone()), div, vis, Const, None);
self.register_builtin_type(poly(FLOOR_DIV, ty_params), floor_div, vis, Const, None);
self.register_const_param_defaults(
ADD,
vec![ConstTemplate::Obj(ValueObj::builtin_t(Slf.clone()))],
);
self.register_const_param_defaults(
SUB,
vec![ConstTemplate::Obj(ValueObj::builtin_t(Slf.clone()))],
);
self.register_const_param_defaults(
MUL,
vec![ConstTemplate::Obj(ValueObj::builtin_t(Slf.clone()))],
);
self.register_const_param_defaults(
DIV,
vec![ConstTemplate::Obj(ValueObj::builtin_t(Slf.clone()))],
);
self.register_const_param_defaults(
FLOOR_DIV,
vec![ConstTemplate::Obj(ValueObj::builtin_t(Slf))],
);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,993 @@
//! Defines `Context`.
//!
//! `Context` is used for type inference and type checking.
#![allow(clippy::result_unit_err)]
pub mod cache;
pub mod compare;
pub mod eval;
pub mod generalize;
pub mod hint;
pub mod initialize;
pub mod inquire;
pub mod instantiate;
pub mod register;
pub mod test;
pub mod unify;
use std::fmt;
use std::mem;
use std::option::Option; // conflicting to Type::Option
use std::path::{Path, PathBuf};
use erg_common::config::ErgConfig;
use erg_common::config::Input;
use erg_common::dict::Dict;
use erg_common::error::Location;
use erg_common::impl_display_from_debug;
use erg_common::set::Set;
use erg_common::traits::{Locational, Stream};
use erg_common::vis::Visibility;
use erg_common::Str;
use erg_common::{fn_name, get_hash, log};
use ast::{DefId, DefKind, VarName};
use erg_parser::ast;
use erg_parser::token::Token;
use crate::context::instantiate::{ConstTemplate, TyVarCache};
use crate::error::{TyCheckError, TyCheckErrors};
use crate::module::{SharedCompilerResource, SharedModuleCache};
use crate::ty::value::ValueObj;
use crate::ty::{Predicate, Type};
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
use Type::*;
use Visibility::*;
/// For implementing LSP or other IDE features
pub trait ContextProvider {
fn dir(&self) -> Vec<(&VarName, &VarInfo)>;
fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context>;
fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)>;
}
const BUILTINS: &Str = &Str::ever("<builtins>");
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitImpl {
pub sub_type: Type,
pub sup_trait: Type,
}
impl std::fmt::Display for TraitImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TraitImpl{{{} <: {}}}", self.sub_type, self.sup_trait)
}
}
impl TraitImpl {
pub const fn new(sub_type: Type, sup_trait: Type) -> Self {
Self {
sub_type,
sup_trait,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClassDefType {
Simple(Type),
ImplTrait { class: Type, impl_trait: Type },
}
impl std::fmt::Display for ClassDefType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ClassDefType::Simple(ty) => write!(f, "{ty}"),
ClassDefType::ImplTrait { class, impl_trait } => {
write!(f, "{class}|<: {impl_trait}|")
}
}
}
}
impl ClassDefType {
pub const fn impl_trait(class: Type, impl_trait: Type) -> Self {
ClassDefType::ImplTrait { class, impl_trait }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefaultInfo {
NonDefault,
WithDefault,
}
impl_display_from_debug!(DefaultInfo);
impl DefaultInfo {
pub const fn has_default(&self) -> bool {
matches!(self, DefaultInfo::WithDefault)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Variance {
/// Output(T)
Covariant, // 共変
/// Input(T)
Contravariant, // 反変
#[default]
Invariant, // 不変
}
impl_display_from_debug!(Variance);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParamSpec {
pub(crate) name: Option<&'static str>,
// TODO: `:` or `<:`
pub(crate) t: Type,
pub default_info: DefaultInfo,
loc: AbsLocation,
}
impl ParamSpec {
pub const fn new(
name: Option<&'static str>,
t: Type,
default: DefaultInfo,
loc: AbsLocation,
) -> Self {
Self {
name,
t,
default_info: default,
loc,
}
}
pub const fn named(name: &'static str, t: Type, default: DefaultInfo) -> Self {
Self::new(Some(name), t, default, AbsLocation::unknown())
}
pub const fn named_nd(name: &'static str, t: Type) -> Self {
Self::new(
Some(name),
t,
DefaultInfo::NonDefault,
AbsLocation::unknown(),
)
}
pub const fn t(name: &'static str, default: DefaultInfo) -> Self {
Self::new(Some(name), Type, default, AbsLocation::unknown())
}
pub const fn t_nd(name: &'static str) -> Self {
Self::new(
Some(name),
Type,
DefaultInfo::NonDefault,
AbsLocation::unknown(),
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ContextKind {
Func,
Proc,
Class,
MethodDefs(Option<Type>), // Type: trait implemented
PatchMethodDefs(Type),
Trait,
StructuralTrait,
Patch(Type),
StructuralPatch(Type),
GluePatch(TraitImpl), // TODO: deprecate (integrate into Patch)
Module,
Instant,
Dummy,
}
impl From<DefKind> for ContextKind {
fn from(kind: DefKind) -> Self {
match kind {
DefKind::Class | DefKind::Inherit => Self::Class,
DefKind::Trait | DefKind::Subsume => Self::Trait,
DefKind::StructuralTrait => Self::StructuralTrait,
DefKind::ErgImport | DefKind::PyImport => Self::Module,
DefKind::Other => Self::Instant,
}
}
}
impl ContextKind {
pub const fn is_method_def(&self) -> bool {
matches!(self, Self::MethodDefs(_))
}
pub const fn is_type(&self) -> bool {
matches!(self, Self::Class | Self::Trait | Self::StructuralTrait)
}
pub fn is_class(&self) -> bool {
matches!(self, Self::Class)
}
pub fn is_trait(&self) -> bool {
matches!(self, Self::Trait | Self::StructuralTrait)
}
pub fn is_patch(&self) -> bool {
matches!(self, Self::Patch(_) | Self::GluePatch(_))
}
}
/// Indicates the mode registered in the Context
/// Preregister: subroutine or constant expression, can be forward referenced
/// Normal: Cannot be forward referenced
/// 環境に登録されているモードを表す
/// Preregister: サブルーチンまたは定数式、前方参照できる
/// Normal: 前方参照できない
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegistrationMode {
PreRegister,
Normal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ContextInfo {
mod_id: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MethodInfo {
definition_type: Type,
method_type: VarInfo,
}
impl fmt::Display for MethodInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{{ def: {} info: {} }}",
self.definition_type, self.method_type
)
}
}
impl MethodInfo {
pub const fn new(definition_type: Type, method_type: VarInfo) -> Self {
Self {
definition_type,
method_type,
}
}
}
/// Represents the context of the current scope
///
/// Recursive functions/methods are highlighted with the prefix `rec_`, as performance may be significantly degraded.
#[derive(Debug, Clone)]
pub struct Context {
pub name: Str,
pub kind: ContextKind,
pub(crate) cfg: ErgConfig,
pub(crate) preds: Vec<Predicate>,
/// for looking up the parent scope
pub(crate) outer: Option<Box<Context>>,
// e.g. { "Add": [ConstObjTemplate::App("Self", vec![])])
pub(crate) const_param_defaults: Dict<Str, Vec<ConstTemplate>>,
// Superclasses/supertraits by a patch are not included here
// patchによってsuper class/traitになったものはここに含まれない
pub(crate) super_classes: Vec<Type>, // if self is a patch, means patch classes
pub(crate) super_traits: Vec<Type>, // if self is not a trait, means implemented traits
// method definitions, if the context is a type
// specializations are included and needs to be separated out
pub(crate) methods_list: Vec<(ClassDefType, Context)>,
// K: method name, V: types defines the method
// If it is declared in a trait, it takes precedence over the class.
pub(crate) method_to_traits: Dict<Str, Vec<MethodInfo>>,
pub(crate) method_to_classes: Dict<Str, Vec<MethodInfo>>,
/// K: method name, V: impl patch
/// Provided methods can switch implementations on a scope-by-scope basis
/// K: メソッド名, V: それを実装するパッチたち
/// 提供メソッドはスコープごとに実装を切り替えることができる
pub(crate) method_impl_patches: Dict<VarName, Vec<VarName>>,
/// K: name of a trait, V: (type, monomorphised trait that the type implements)
/// K: トレイトの名前, V: (型, その型が実装する単相化トレイト)
/// e.g. { "Named": [(Type, Named), (Func, Named), ...], "Add": [(Nat, Add(Nat)), (Int, Add(Int)), ...], ... }
pub(crate) trait_impls: Dict<Str, Set<TraitImpl>>,
/// stores declared names (not initialized)
pub(crate) decls: Dict<VarName, VarInfo>,
/// for error reporting
pub(crate) future_defined_locals: Dict<VarName, VarInfo>,
pub(crate) deleted_locals: Dict<VarName, VarInfo>,
// stores defined names
// 型の一致はHashMapでは判定できないため、keyはVarNameとして1つずつ見ていく
/// ```python
/// f [x, y], z = ...
/// ```
/// => params: vec![(None, [T; 2]), (Some("z"), U)]
/// => locals: {"x": T, "y": T}
/// TODO: impl params desugaring and replace to `Dict`
pub(crate) params: Vec<(Option<VarName>, VarInfo)>,
pub(crate) locals: Dict<VarName, VarInfo>,
pub(crate) consts: Dict<VarName, ValueObj>,
// {"Nat": ctx, "Int": ctx, ...}
pub(crate) mono_types: Dict<VarName, (Type, Context)>,
// Implementation Contexts for Polymorphic Types
// Vec<TyParam> are specialization parameters
// e.g. {"Array": [(Array(Nat), ctx), (Array(Int), ctx), (Array(Str), ctx), (Array(Obj), ctx), (Array('T), ctx)], ...}
pub(crate) poly_types: Dict<VarName, (Type, Context)>,
// patches can be accessed like normal records
// but when used as a fallback to a type, values are traversed instead of accessing by keys
pub(crate) patches: Dict<VarName, Context>,
pub(crate) shared: Option<SharedCompilerResource>,
pub(crate) tv_cache: Option<TyVarCache>,
pub(crate) higher_order_caller: Vec<Str>,
pub(crate) level: usize,
}
impl Default for Context {
#[inline]
fn default() -> Self {
Self::default_with_name("<dummy>")
}
}
impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context")
.field("name", &self.name)
.field("preds", &self.preds)
.field("params", &self.params)
.field("decls", &self.decls)
.field("locals", &self.params)
.field("consts", &self.consts)
.field("mono_types", &self.mono_types)
.field("poly_types", &self.poly_types)
.field("patches", &self.patches)
// .field("mod_cache", &self.mod_cache)
.finish()
}
}
impl ContextProvider for Context {
fn dir(&self) -> Vec<(&VarName, &VarInfo)> {
let mut vars: Vec<_> = self
.locals
.iter()
.chain(
self.params
.iter()
.filter_map(|(k, v)| k.as_ref().map(|k| (k, v))),
)
.chain(self.methods_list.iter().flat_map(|(_, ctx)| ctx.dir()))
.collect();
for sup in self.super_classes.iter() {
if let Some((_, sup_ctx)) = self.get_nominal_type_ctx(sup) {
vars.extend(sup_ctx.type_dir());
}
}
if let Some(outer) = self.get_outer() {
vars.extend(outer.dir());
} else if let Some(builtins) = self.get_builtins() {
vars.extend(builtins.locals.iter());
}
vars
}
fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
self.get_mod(receiver_name)
.or_else(|| self.rec_get_type(receiver_name).map(|(_, ctx)| ctx))
.or_else(|| {
let (_, vi) = self.get_var_info(receiver_name)?;
self.get_nominal_type_ctx(&vi.t).map(|(_, ctx)| ctx)
})
}
fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
self.get_var_kv(name).or_else(|| {
self.get_builtins()
.and_then(|builtin| builtin.get_var_kv(name))
})
}
}
impl Context {
pub fn dir(&self) -> Vec<(&VarName, &VarInfo)> {
ContextProvider::dir(self)
}
pub fn get_receiver_ctx(&self, receiver_name: &str) -> Option<&Context> {
ContextProvider::get_receiver_ctx(self, receiver_name)
}
pub fn get_receiver_ctxs(&self, receiver_name: &str) -> Vec<&Context> {
let mut ctxs = vec![];
if let Some(receiver_ctx) = self.get_receiver_ctx(receiver_name) {
ctxs.push(receiver_ctx);
ctxs.extend(
receiver_ctx
.super_classes
.iter()
.flat_map(|t| self.get_nominal_type_ctx(t).map(|(_, ctx)| ctx)),
);
}
ctxs
}
pub fn get_var_info(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
ContextProvider::get_var_info(self, name)
}
}
impl Context {
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn new(
name: Str,
cfg: ErgConfig,
kind: ContextKind,
params: Vec<ParamSpec>,
outer: Option<Context>,
shared: Option<SharedCompilerResource>,
level: usize,
) -> Self {
Self::with_capacity(name, cfg, kind, params, outer, shared, 0, level)
}
pub fn default_with_name(name: &'static str) -> Self {
Self::new(
name.into(),
ErgConfig::default(),
ContextKind::Dummy,
vec![],
None,
None,
Self::TOP_LEVEL,
)
}
#[allow(clippy::too_many_arguments)]
pub fn with_capacity(
name: Str,
cfg: ErgConfig,
kind: ContextKind,
params: Vec<ParamSpec>,
outer: Option<Context>,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
let mut params_ = Vec::new();
for param in params.into_iter() {
let id = DefId(get_hash(&(&name, &param)));
if let Some(name) = param.name {
let kind = VarKind::parameter(id, param.default_info);
let muty = Mutability::from(name);
let vi = VarInfo::new(param.t, muty, Private, kind, None, None, None, param.loc);
params_.push((Some(VarName::new(Token::static_symbol(name))), vi));
} else {
let kind = VarKind::parameter(id, param.default_info);
let muty = Mutability::Immutable;
let vi = VarInfo::new(param.t, muty, Private, kind, None, None, None, param.loc);
params_.push((None, vi));
}
}
Self {
name,
cfg,
kind,
preds: vec![],
outer: outer.map(Box::new),
super_classes: vec![],
super_traits: vec![],
methods_list: vec![],
const_param_defaults: Dict::default(),
method_to_traits: Dict::default(),
method_to_classes: Dict::default(),
method_impl_patches: Dict::default(),
trait_impls: Dict::default(),
params: params_,
decls: Dict::default(),
future_defined_locals: Dict::default(),
deleted_locals: Dict::default(),
locals: Dict::with_capacity(capacity),
consts: Dict::default(),
mono_types: Dict::default(),
poly_types: Dict::default(),
shared,
tv_cache: None,
patches: Dict::default(),
higher_order_caller: vec![],
level,
}
}
#[inline]
pub fn mono(
name: Str,
cfg: ErgConfig,
kind: ContextKind,
outer: Option<Context>,
shared: Option<SharedCompilerResource>,
level: usize,
) -> Self {
Self::new(name, cfg, kind, vec![], outer, shared, level)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn poly(
name: Str,
cfg: ErgConfig,
kind: ContextKind,
params: Vec<ParamSpec>,
outer: Option<Context>,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
Self::with_capacity(name, cfg, kind, params, outer, shared, capacity, level)
}
pub fn poly_trait<S: Into<Str>>(
name: S,
params: Vec<ParamSpec>,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
let name = name.into();
Self::poly(
name,
cfg,
ContextKind::Trait,
params,
None,
shared,
capacity,
level,
)
}
#[inline]
pub fn builtin_poly_trait<S: Into<Str>>(
name: S,
params: Vec<ParamSpec>,
capacity: usize,
) -> Self {
Self::poly_trait(
name,
params,
ErgConfig::default(),
None,
capacity,
Self::TOP_LEVEL,
)
}
pub fn poly_class<S: Into<Str>>(
name: S,
params: Vec<ParamSpec>,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
let name = name.into();
Self::poly(
name,
cfg,
ContextKind::Class,
params,
None,
shared,
capacity,
level,
)
}
#[inline]
pub fn builtin_poly_class<S: Into<Str>>(
name: S,
params: Vec<ParamSpec>,
capacity: usize,
) -> Self {
Self::poly_class(
name,
params,
ErgConfig::default(),
None,
capacity,
Self::TOP_LEVEL,
)
}
#[allow(clippy::too_many_arguments)]
pub fn poly_patch<S: Into<Str>>(
name: S,
base: Type,
params: Vec<ParamSpec>,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
let name = name.into();
Self::poly(
name,
cfg,
ContextKind::Patch(base),
params,
None,
shared,
capacity,
level,
)
}
#[inline]
pub fn mono_trait<S: Into<Str>>(
name: S,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
Self::poly_trait(name, vec![], cfg, shared, capacity, level)
}
#[inline]
pub fn builtin_mono_trait<S: Into<Str>>(name: S, capacity: usize) -> Self {
Self::mono_trait(name, ErgConfig::default(), None, capacity, Self::TOP_LEVEL)
}
#[inline]
pub fn mono_class<S: Into<Str>>(
name: S,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
Self::poly_class(name, vec![], cfg, shared, capacity, level)
}
#[inline]
pub fn builtin_mono_class<S: Into<Str>>(name: S, capacity: usize) -> Self {
Self::mono_class(name, ErgConfig::default(), None, capacity, Self::TOP_LEVEL)
}
#[inline]
pub fn mono_patch<S: Into<Str>>(
name: S,
base: Type,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
Self::poly_patch(name, base, vec![], cfg, shared, capacity, level)
}
#[inline]
pub fn methods(
impl_trait: Option<Type>,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
let name = if let Some(tr) = &impl_trait {
tr.local_name()
} else {
Str::ever("Methods")
};
Self::with_capacity(
name,
cfg,
ContextKind::MethodDefs(impl_trait),
vec![],
None,
shared,
capacity,
level,
)
}
#[inline]
pub fn builtin_methods(impl_trait: Option<Type>, capacity: usize) -> Self {
Self::methods(
impl_trait,
ErgConfig::default(),
None,
capacity,
Self::TOP_LEVEL,
)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn poly_glue_patch<S: Into<Str>>(
name: S,
base: Type,
impls: Type,
params: Vec<ParamSpec>,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
level: usize,
) -> Self {
Self::poly(
name.into(),
cfg,
ContextKind::GluePatch(TraitImpl::new(base, impls)),
params,
None,
shared,
capacity,
level,
)
}
#[inline]
pub fn builtin_poly_glue_patch<S: Into<Str>>(
name: S,
base: Type,
impls: Type,
params: Vec<ParamSpec>,
capacity: usize,
) -> Self {
Self::poly_glue_patch(
name,
base,
impls,
params,
ErgConfig::default(),
None,
capacity,
Self::TOP_LEVEL,
)
}
#[inline]
pub fn module(
name: Str,
cfg: ErgConfig,
shared: Option<SharedCompilerResource>,
capacity: usize,
) -> Self {
Self::with_capacity(
name,
cfg,
ContextKind::Module,
vec![],
None,
shared,
capacity,
Self::TOP_LEVEL,
)
}
#[inline]
pub fn builtin_module<S: Into<Str>>(name: S, cfg: ErgConfig, capacity: usize) -> Self {
Self::module(name.into(), cfg, None, capacity)
}
#[inline]
pub fn instant(
name: Str,
cfg: ErgConfig,
capacity: usize,
shared: Option<SharedCompilerResource>,
outer: Context,
) -> Self {
Self::with_capacity(
name,
cfg,
ContextKind::Instant,
vec![],
Some(outer),
shared,
capacity,
Self::TOP_LEVEL,
)
}
pub(crate) fn module_path(&self) -> Option<&PathBuf> {
if let Input::File(path) = &self.cfg.input {
Some(path)
} else {
None
}
}
pub(crate) fn absolutize(&self, loc: Location) -> AbsLocation {
AbsLocation::new(self.module_path().cloned(), loc)
}
#[inline]
pub fn caused_by(&self) -> String {
String::from(&self.name[..])
}
pub(crate) fn get_outer(&self) -> Option<&Context> {
self.outer.as_ref().map(|x| x.as_ref())
}
pub(crate) fn impl_of(&self) -> Option<Type> {
if let ContextKind::MethodDefs(Some(tr)) = &self.kind {
Some(tr.clone())
} else {
None
}
}
pub(crate) fn path(&self) -> Str {
// NOTE: this need to be changed if we want to support nested classes/traits
if let Some(outer) = self.get_outer() {
outer.path()
} else if self.kind == ContextKind::Module {
self.name.clone()
} else {
BUILTINS.clone()
}
}
/// Returns None if self is `<builtins>`.
/// This avoids infinite loops.
pub(crate) fn get_builtins(&self) -> Option<&Context> {
// builtins中で定義した型等はmod_cacheがNoneになっている
if self.kind != ContextKind::Module || &self.path()[..] != "<builtins>" {
self.shared
.as_ref()
.map(|shared| shared.mod_cache.ref_ctx(Path::new("<builtins>")).unwrap())
.map(|mod_ctx| &mod_ctx.context)
} else {
None
}
}
/// This method is intended to be called __only__ in the top-level module.
/// `.cfg` is not initialized and is used around.
pub fn initialize(&mut self) {
let mut shared = mem::take(&mut self.shared);
if let Some(mod_cache) = shared.as_mut().map(|s| &mut s.mod_cache) {
mod_cache.initialize();
}
if let Some(py_mod_cache) = shared.as_mut().map(|s| &mut s.py_mod_cache) {
py_mod_cache.initialize();
}
*self = Self::new(
self.name.clone(),
self.cfg.clone(),
self.kind.clone(),
vec![],
None,
shared,
self.level,
);
}
pub(crate) fn grow(
&mut self,
name: &str,
kind: ContextKind,
vis: Visibility,
tv_cache: Option<TyVarCache>,
) {
let name = if vis.is_public() {
format!("{parent}.{name}", parent = self.name)
} else {
format!("{parent}::{name}", parent = self.name)
};
log!(info "{}: current namespace: {name}", fn_name!());
self.outer = Some(Box::new(mem::take(self)));
self.cfg = self.get_outer().unwrap().cfg.clone();
self.shared = self.get_outer().unwrap().shared.clone();
self.tv_cache = tv_cache;
self.name = name.into();
self.kind = kind;
}
pub(crate) fn clear_invalid_vars(&mut self) {
self.locals.retain(|_, v| v.t != Failure);
self.decls.retain(|_, v| v.t != Failure);
self.params.retain(|(_, v)| v.t != Failure);
}
pub fn pop(&mut self) -> Context {
if let Some(parent) = self.outer.as_mut() {
let parent = mem::take(parent);
let ctx = mem::take(self);
*self = *parent;
log!(info "{}: current namespace: {}", fn_name!(), self.name);
ctx
} else {
panic!("cannot pop the top-level context (or use `pop_mod`)");
}
}
/// unlike `pop`, `outer` must be `None`.
pub fn pop_mod(&mut self) -> Option<Context> {
if self.outer.is_some() {
log!(err "not in the top-level context");
None
} else {
log!(info "{}: current namespace: <builtins>", fn_name!());
// toplevel
Some(mem::take(self))
}
}
pub(crate) fn check_decls_and_pop(&mut self) -> Result<Context, TyCheckErrors> {
self.check_decls().map_err(|errs| {
self.pop();
errs
})?;
Ok(self.pop())
}
pub(crate) fn check_decls(&mut self) -> Result<(), TyCheckErrors> {
let mut uninited_errs = TyCheckErrors::empty();
for (name, vi) in self.decls.iter() {
uninited_errs.push(TyCheckError::uninitialized_error(
self.cfg.input.clone(),
line!() as usize,
name.loc(),
self.caused_by(),
name.inspect(),
&vi.t,
));
}
if !uninited_errs.is_empty() {
Err(uninited_errs)
} else {
Ok(())
}
}
fn type_dir(&self) -> Vec<(&VarName, &VarInfo)> {
self.locals
.iter()
.chain(self.methods_list.iter().flat_map(|(_, ctx)| ctx.dir()))
.collect()
}
pub(crate) fn mod_cache(&self) -> Option<&SharedModuleCache> {
self.shared.as_ref().map(|shared| &shared.mod_cache)
}
pub(crate) fn py_mod_cache(&self) -> Option<&SharedModuleCache> {
self.shared.as_ref().map(|shared| &shared.py_mod_cache)
}
#[cfg(feature = "els")]
pub fn index(&self) -> Option<&crate::module::SharedModuleIndex> {
self.shared.as_ref().map(|shared| &shared.index)
}
pub fn shared(&self) -> Option<&SharedCompilerResource> {
self.shared.as_ref()
}
}
#[derive(Debug, Clone, Default)]
pub struct ModuleContext {
pub context: Context,
pub scope: Dict<Str, Context>,
}
impl ModuleContext {
pub const fn new(toplevel: Context, scope: Dict<Str, Context>) -> Self {
Self {
context: toplevel,
scope,
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
//! test module for `Context`
use erg_common::error::Location;
use erg_common::Str;
// use erg_common::error::Location;
use erg_common::set;
use crate::ty::constructors::{func1, mono, mono_q, poly, refinement};
use crate::ty::free::Constraint;
use crate::ty::typaram::TyParam;
use crate::ty::{Predicate, Type};
use Type::*;
use crate::context::instantiate::TyVarCache;
use crate::context::Context;
impl Context {
pub fn test_refinement_subtyping(&self) -> Result<(), ()> {
// Nat :> {I: Int | I >= 1} ?
let lhs = Nat;
let var = Str::ever("I");
let rhs = refinement(
var.clone(),
Type::Int,
set! { Predicate::eq(var, TyParam::value(1)) },
);
if self.supertype_of(&lhs, &rhs) {
Ok(())
} else {
Err(())
}
}
pub fn test_resolve_trait_inner1(&self) -> Result<(), ()> {
let name = Str::ever("Add");
let params = vec![TyParam::t(Nat)];
let maybe_trait = poly(name, params);
let mut min = Type::Obj;
for pair in self.get_trait_impls(&maybe_trait) {
if self.supertype_of(&pair.sup_trait, &maybe_trait) {
min = self.min(&min, &pair.sub_type).unwrap_or(&min).clone();
}
}
if min == Nat {
Ok(())
} else {
Err(())
}
}
pub fn test_instantiation_and_generalization(&self) -> Result<(), ()> {
use crate::ty::free::HasLevel;
let t = mono_q("T", Constraint::new_subtype_of(mono("Eq")));
let unbound = func1(t.clone(), t);
let quantified = unbound.clone().quantify();
println!("quantified : {quantified}");
let mut tv_cache = TyVarCache::new(self.level + 1, self);
println!("tv_cache: {tv_cache}");
let inst = self
.instantiate_t_inner(unbound, &mut tv_cache, Location::Unknown)
.map_err(|_| ())?;
println!("inst: {inst}");
inst.lift();
let quantified_again = self.generalize_t(inst);
println!("quantified_again: {quantified_again}");
assert_eq!(quantified, quantified_again);
Ok(())
}
}

View file

@ -0,0 +1,948 @@
//! provides type variable related operations
use std::mem;
use std::option::Option;
use erg_common::error::Location;
use erg_common::fn_name;
use erg_common::Str;
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
use crate::context::instantiate::TyVarCache;
use crate::ty::constructors::*;
use crate::ty::free::{Constraint, FreeKind, HasLevel};
use crate::ty::typaram::TyParam;
use crate::ty::value::ValueObj;
use crate::ty::{Predicate, Type};
use crate::context::{Context, Variance};
use crate::error::{SingleTyCheckResult, TyCheckError, TyCheckErrors, TyCheckResult};
use crate::{feature_error, type_feature_error, unreachable_error};
use Predicate as Pred;
use Type::*;
use ValueObj::{Inf, NegInf};
impl Context {
// occur(X -> ?T, ?T) ==> Error
// occur(?T, ?T -> X) ==> Error
// occur(?T, Option(?T)) ==> Error
// occur(?T, ?T.Output) ==> Error
fn occur(&self, maybe_sub: &Type, maybe_sup: &Type, loc: Location) -> TyCheckResult<()> {
match (maybe_sub, maybe_sup) {
(Type::FreeVar(sub), Type::FreeVar(sup)) => {
if sub.is_unbound() && sup.is_unbound() && sub == sup {
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
maybe_sub,
maybe_sup,
loc,
self.caused_by(),
)))
} else {
Ok(())
}
}
(Type::Subr(subr), Type::FreeVar(fv)) if fv.is_unbound() => {
for default_t in subr.default_params.iter().map(|pt| pt.typ()) {
self.occur(default_t, maybe_sup, loc)?;
}
if let Some(var_params) = subr.var_params.as_ref() {
self.occur(var_params.typ(), maybe_sup, loc)?;
}
for non_default_t in subr.non_default_params.iter().map(|pt| pt.typ()) {
self.occur(non_default_t, maybe_sup, loc)?;
}
self.occur(&subr.return_t, maybe_sup, loc)?;
Ok(())
}
(Type::FreeVar(fv), Type::Subr(subr)) if fv.is_unbound() => {
for default_t in subr.default_params.iter().map(|pt| pt.typ()) {
self.occur(maybe_sub, default_t, loc)?;
}
if let Some(var_params) = subr.var_params.as_ref() {
self.occur(maybe_sub, var_params.typ(), loc)?;
}
for non_default_t in subr.non_default_params.iter().map(|pt| pt.typ()) {
self.occur(maybe_sub, non_default_t, loc)?;
}
self.occur(maybe_sub, &subr.return_t, loc)?;
Ok(())
}
(Type::Poly { params, .. }, Type::FreeVar(fv)) if fv.is_unbound() => {
for param in params.iter().filter_map(|tp| {
if let TyParam::Type(t) = tp {
Some(t)
} else {
None
}
}) {
self.occur(param, maybe_sup, loc)?;
}
Ok(())
}
(Type::FreeVar(fv), Type::Poly { params, .. }) if fv.is_unbound() => {
for param in params.iter().filter_map(|tp| {
if let TyParam::Type(t) = tp {
Some(t)
} else {
None
}
}) {
self.occur(maybe_sub, param, loc)?;
}
Ok(())
}
(Type::Proj { lhs, .. }, rhs) => self.occur(lhs, rhs, loc),
(Type::ProjCall { lhs, args, .. }, rhs) => {
if let TyParam::Type(t) = lhs.as_ref() {
self.occur(t, rhs, loc)?;
}
for arg in args.iter() {
if let TyParam::Type(t) = arg {
self.occur(t, rhs, loc)?;
}
}
Ok(())
}
(lhs, Type::Proj { lhs: rhs, .. }) => self.occur(lhs, rhs, loc),
(lhs, Type::ProjCall { lhs: rhs, args, .. }) => {
if let TyParam::Type(t) = rhs.as_ref() {
self.occur(lhs, t, loc)?;
}
for arg in args.iter() {
if let TyParam::Type(t) = arg {
self.occur(lhs, t, loc)?;
}
}
Ok(())
}
_ => Ok(()),
}
}
/// allow_divergence = trueにすると、Num型変数と±Infの単一化を許す
pub(crate) fn sub_unify_tp(
&self,
maybe_sub: &TyParam,
maybe_sup: &TyParam,
_variance: Option<Variance>,
loc: Location,
allow_divergence: bool,
) -> TyCheckResult<()> {
if maybe_sub.has_no_unbound_var()
&& maybe_sup.has_no_unbound_var()
&& maybe_sub == maybe_sup
{
return Ok(());
}
match (maybe_sub, maybe_sup) {
(TyParam::Type(maybe_sub), TyParam::Type(maybe_sup)) => {
self.sub_unify(maybe_sub, maybe_sup, loc, None)
}
(TyParam::FreeVar(lfv), TyParam::FreeVar(rfv))
if lfv.is_unbound() && rfv.is_unbound() =>
{
if lfv.level().unwrap() > rfv.level().unwrap() {
if !lfv.is_generalized() {
lfv.link(maybe_sup);
}
} else if !rfv.is_generalized() {
rfv.link(maybe_sub);
}
Ok(())
}
(TyParam::FreeVar(lfv), tp) => {
match &*lfv.borrow() {
FreeKind::Linked(l) | FreeKind::UndoableLinked { t: l, .. } => {
return self.sub_unify_tp(l, tp, _variance, loc, allow_divergence);
}
FreeKind::Unbound { .. } | FreeKind::NamedUnbound { .. } => {}
} // &fv is dropped
let fv_t = lfv.constraint().unwrap().get_type().unwrap().clone(); // lfvを参照しないよいにcloneする(あとでborrow_mutするため)
let tp_t = self.get_tp_t(tp)?;
if self.supertype_of(&fv_t, &tp_t) {
// 外部未連携型変数の場合、linkしないで制約を弱めるだけにする(see compiler/inference.md)
if lfv.level() < Some(self.level) {
let new_constraint = Constraint::new_subtype_of(tp_t);
if self.is_sub_constraint_of(&lfv.constraint().unwrap(), &new_constraint)
|| lfv.constraint().unwrap().get_type() == Some(&Type)
{
lfv.update_constraint(new_constraint, false);
}
} else {
lfv.link(tp);
}
Ok(())
} else if allow_divergence
&& (self.eq_tp(tp, &TyParam::value(Inf))
|| self.eq_tp(tp, &TyParam::value(NegInf)))
&& self.subtype_of(&fv_t, &mono("Num"))
{
lfv.link(tp);
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)))
}
}
(tp, TyParam::FreeVar(rfv)) => {
match &*rfv.borrow() {
FreeKind::Linked(l) | FreeKind::UndoableLinked { t: l, .. } => {
return self.sub_unify_tp(l, tp, _variance, loc, allow_divergence);
}
FreeKind::Unbound { .. } | FreeKind::NamedUnbound { .. } => {}
} // &fv is dropped
let fv_t = rfv.constraint().unwrap().get_type().unwrap().clone(); // fvを参照しないよいにcloneする(あとでborrow_mutするため)
let tp_t = self.get_tp_t(tp)?;
if self.supertype_of(&fv_t, &tp_t) {
// 外部未連携型変数の場合、linkしないで制約を弱めるだけにする(see compiler/inference.md)
if rfv.level() < Some(self.level) {
let new_constraint = Constraint::new_subtype_of(tp_t);
if self.is_sub_constraint_of(&rfv.constraint().unwrap(), &new_constraint)
|| rfv.constraint().unwrap().get_type() == Some(&Type)
{
rfv.update_constraint(new_constraint, false);
}
} else {
rfv.link(tp);
}
Ok(())
} else if allow_divergence
&& (self.eq_tp(tp, &TyParam::value(Inf))
|| self.eq_tp(tp, &TyParam::value(NegInf)))
&& self.subtype_of(&fv_t, &mono("Num"))
{
rfv.link(tp);
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)))
}
}
(TyParam::UnaryOp { op: lop, val: lval }, TyParam::UnaryOp { op: rop, val: rval })
if lop == rop =>
{
self.sub_unify_tp(lval, rval, _variance, loc, allow_divergence)
}
(
TyParam::BinOp { op: lop, lhs, rhs },
TyParam::BinOp {
op: rop,
lhs: lhs2,
rhs: rhs2,
},
) if lop == rop => {
self.sub_unify_tp(lhs, lhs2, _variance, loc, allow_divergence)?;
self.sub_unify_tp(rhs, rhs2, _variance, loc, allow_divergence)
}
(l, TyParam::Erased(t)) => {
let sub_t = self.get_tp_t(l)?;
if self.subtype_of(&sub_t, t) {
Ok(())
} else {
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
&sub_t,
t,
loc,
self.caused_by(),
)))
}
}
(l, TyParam::Type(r)) => {
let l = self
.convert_tp_into_ty(l.clone())
.unwrap_or_else(|_| todo!("{l} cannot be a type"));
self.sub_unify(&l, r, loc, None)?;
Ok(())
}
(TyParam::Type(l), r) => {
let r = self
.convert_tp_into_ty(r.clone())
.unwrap_or_else(|_| todo!("{r} cannot be a type"));
self.sub_unify(l, &r, loc, None)?;
Ok(())
}
(TyParam::Array(ls), TyParam::Array(rs)) | (TyParam::Tuple(ls), TyParam::Tuple(rs)) => {
for (l, r) in ls.iter().zip(rs.iter()) {
self.sub_unify_tp(l, r, _variance, loc, allow_divergence)?;
}
Ok(())
}
(TyParam::Dict(ls), TyParam::Dict(rs)) => {
for (lk, lv) in ls.iter() {
if let Some(rv) = rs.get(lk) {
self.sub_unify_tp(lv, rv, _variance, loc, allow_divergence)?;
} else {
// TODO:
return Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)));
}
}
Ok(())
}
(l, r) => panic!("type-parameter unification failed:\nl:{l}\nr: {r}"),
}
}
fn reunify_tp(
&self,
before: &TyParam,
after: &TyParam,
loc: Location,
) -> SingleTyCheckResult<()> {
match (before, after) {
(TyParam::Value(ValueObj::Mut(l)), TyParam::Value(ValueObj::Mut(r))) => {
*l.borrow_mut() = r.borrow().clone();
Ok(())
}
(TyParam::Value(ValueObj::Mut(l)), TyParam::Value(r)) => {
*l.borrow_mut() = r.clone();
Ok(())
}
(TyParam::Type(l), TyParam::Type(r)) => self.reunify(l, r, loc),
(TyParam::UnaryOp { op: lop, val: lval }, TyParam::UnaryOp { op: rop, val: rval })
if lop == rop =>
{
self.reunify_tp(lval, rval, loc)
}
(
TyParam::BinOp { op: lop, lhs, rhs },
TyParam::BinOp {
op: rop,
lhs: lhs2,
rhs: rhs2,
},
) if lop == rop => {
self.reunify_tp(lhs, lhs2, loc)?;
self.reunify_tp(rhs, rhs2, loc)
}
(l, r) if self.eq_tp(l, r) => Ok(()),
(l, r) => panic!("type-parameter re-unification failed:\nl: {l}\nr: {r}"),
}
}
/// predは正規化されているとする
fn sub_unify_pred(
&self,
l_pred: &Predicate,
r_pred: &Predicate,
loc: Location,
) -> TyCheckResult<()> {
match (l_pred, r_pred) {
(Pred::Value(_), Pred::Value(_)) | (Pred::Const(_), Pred::Const(_)) => Ok(()),
(Pred::Equal { rhs, .. }, Pred::Equal { rhs: rhs2, .. })
| (Pred::GreaterEqual { rhs, .. }, Pred::GreaterEqual { rhs: rhs2, .. })
| (Pred::LessEqual { rhs, .. }, Pred::LessEqual { rhs: rhs2, .. })
| (Pred::NotEqual { rhs, .. }, Pred::NotEqual { rhs: rhs2, .. }) => {
self.sub_unify_tp(rhs, rhs2, None, loc, false)
}
(Pred::And(l1, r1), Pred::And(l2, r2)) | (Pred::Or(l1, r1), Pred::Or(l2, r2)) => {
match (
self.sub_unify_pred(l1, l2, loc),
self.sub_unify_pred(r1, r2, loc),
) {
(Ok(()), Ok(())) => Ok(()),
(Ok(()), Err(e)) | (Err(e), Ok(())) | (Err(e), Err(_)) => Err(e),
}
}
(Pred::Not(l), Pred::Not(r)) => self.sub_unify_pred(r, l, loc),
// unify({I >= 0}, {I >= ?M and I <= ?N}): ?M => 0, ?N => Inf
(Pred::GreaterEqual { rhs, .. }, Pred::And(l, r))
| (Predicate::And(l, r), Pred::GreaterEqual { rhs, .. }) => {
match (l.as_ref(), r.as_ref()) {
(
Pred::GreaterEqual { rhs: ge_rhs, .. },
Pred::LessEqual { rhs: le_rhs, .. },
)
| (
Pred::LessEqual { rhs: le_rhs, .. },
Pred::GreaterEqual { rhs: ge_rhs, .. },
) => {
self.sub_unify_tp(rhs, ge_rhs, None, loc, false)?;
self.sub_unify_tp(le_rhs, &TyParam::value(Inf), None, loc, true)
}
_ => Err(TyCheckErrors::from(TyCheckError::pred_unification_error(
self.cfg.input.clone(),
line!() as usize,
l_pred,
r_pred,
self.caused_by(),
))),
}
}
(Pred::LessEqual { rhs, .. }, Pred::And(l, r))
| (Pred::And(l, r), Pred::LessEqual { rhs, .. }) => match (l.as_ref(), r.as_ref()) {
(Pred::GreaterEqual { rhs: ge_rhs, .. }, Pred::LessEqual { rhs: le_rhs, .. })
| (Pred::LessEqual { rhs: le_rhs, .. }, Pred::GreaterEqual { rhs: ge_rhs, .. }) => {
self.sub_unify_tp(rhs, le_rhs, None, loc, false)?;
self.sub_unify_tp(ge_rhs, &TyParam::value(NegInf), None, loc, true)
}
_ => Err(TyCheckErrors::from(TyCheckError::pred_unification_error(
self.cfg.input.clone(),
line!() as usize,
l_pred,
r_pred,
self.caused_by(),
))),
},
(Pred::Equal { rhs, .. }, Pred::And(l, r))
| (Pred::And(l, r), Pred::Equal { rhs, .. }) => match (l.as_ref(), r.as_ref()) {
(Pred::GreaterEqual { rhs: ge_rhs, .. }, Pred::LessEqual { rhs: le_rhs, .. })
| (Pred::LessEqual { rhs: le_rhs, .. }, Pred::GreaterEqual { rhs: ge_rhs, .. }) => {
self.sub_unify_tp(rhs, le_rhs, None, loc, false)?;
self.sub_unify_tp(rhs, ge_rhs, None, loc, false)
}
_ => Err(TyCheckErrors::from(TyCheckError::pred_unification_error(
self.cfg.input.clone(),
line!() as usize,
l_pred,
r_pred,
self.caused_by(),
))),
},
_ => Err(TyCheckErrors::from(TyCheckError::pred_unification_error(
self.cfg.input.clone(),
line!() as usize,
l_pred,
r_pred,
self.caused_by(),
))),
}
}
/// T: Array(Int, !0), U: Array(Int, !1)
/// reunify(T, U):
/// T: Array(Int, !1), U: Array(Int, !1)
pub(crate) fn reunify(
&self,
before_t: &Type,
after_t: &Type,
loc: Location,
) -> SingleTyCheckResult<()> {
match (before_t, after_t) {
(Type::FreeVar(fv), r) if fv.is_linked() => self.reunify(&fv.crack(), r, loc),
(l, Type::FreeVar(fv)) if fv.is_linked() => self.reunify(l, &fv.crack(), loc),
(Type::Ref(l), Type::Ref(r)) => self.reunify(l, r, loc),
(
Type::RefMut {
before: lbefore,
after: lafter,
},
Type::RefMut {
before: rbefore,
after: rafter,
},
) => {
self.reunify(lbefore, rbefore, loc)?;
match (lafter, rafter) {
(Some(lafter), Some(rafter)) => {
self.reunify(lafter, rafter, loc)?;
}
(None, None) => {}
_ => todo!(),
}
Ok(())
}
(Type::Ref(l), r) => self.reunify(l, r, loc),
// REVIEW:
(Type::RefMut { before, .. }, r) => self.reunify(before, r, loc),
(l, Type::Ref(r)) => self.reunify(l, r, loc),
(l, Type::RefMut { before, .. }) => self.reunify(l, before, loc),
(
Type::Poly {
name: ln,
params: lps,
},
Type::Poly {
name: rn,
params: rps,
},
) => {
if ln != rn {
let before_t = poly(ln.clone(), lps.clone());
return Err(TyCheckError::re_unification_error(
self.cfg.input.clone(),
line!() as usize,
&before_t,
after_t,
loc,
self.caused_by(),
));
}
for (l, r) in lps.iter().zip(rps.iter()) {
self.reunify_tp(l, r, loc)?;
}
Ok(())
}
(l, r) if self.same_type_of(l, r) => Ok(()),
(l, r) => Err(TyCheckError::re_unification_error(
self.cfg.input.clone(),
line!() as usize,
l,
r,
loc,
self.caused_by(),
)),
}
}
/// Assuming that `sub` is a subtype of `sup`, fill in the type variable to satisfy the assumption
///
/// When comparing arguments and parameter, the left side (`sub`) is the argument (found) and the right side (`sup`) is the parameter (expected)
///
/// The parameter type must be a supertype of the argument type
/// ```python
/// sub_unify({I: Int | I == 0}, ?T(<: Ord)): (/* OK */)
/// sub_unify(Int, ?T(:> Nat)): (?T :> Int)
/// sub_unify(Nat, ?T(:> Int)): (/* OK */)
/// sub_unify(Nat, Add(?R)): (?R => Nat, Nat.Output => Nat)
/// sub_unify([?T; 0], Mutate): (/* OK */)
/// ```
pub(crate) fn sub_unify(
&self,
maybe_sub: &Type,
maybe_sup: &Type,
loc: Location,
param_name: Option<&Str>,
) -> TyCheckResult<()> {
log!(info "trying sub_unify:\nmaybe_sub: {maybe_sub}\nmaybe_sup: {maybe_sup}");
// In this case, there is no new information to be gained
// この場合、特に新しく得られる情報はない
if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub {
return Ok(());
}
// API definition was failed and inspection is useless after this
if maybe_sub == &Type::Failure || maybe_sup == &Type::Failure {
return Ok(());
}
self.occur(maybe_sub, maybe_sup, loc)?;
let maybe_sub_is_sub = self.subtype_of(maybe_sub, maybe_sup);
if !maybe_sub_is_sub {
log!(err "{maybe_sub} !<: {maybe_sup}");
return Err(TyCheckErrors::from(TyCheckError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
loc,
self.caused_by(),
param_name.unwrap_or(&Str::ever("_")),
None,
maybe_sup,
maybe_sub,
self.get_candidates(maybe_sub),
self.get_simple_type_mismatch_hint(maybe_sup, maybe_sub),
)));
} else if maybe_sub.has_no_unbound_var() && maybe_sup.has_no_unbound_var() {
return Ok(());
}
match (maybe_sub, maybe_sup) {
// lfv's sup can be shrunk (take min), rfv's sub can be expanded (take union)
// lfvのsupは縮小可能(minを取る)、rfvのsubは拡大可能(unionを取る)
// sub_unify(?T[0](:> Never, <: Int), ?U[1](:> Never, <: Nat)): (/* ?U[1] --> ?T[0](:> Never, <: Nat))
// sub_unify(?T[1](:> Never, <: Nat), ?U[0](:> Never, <: Int)): (/* ?T[1] --> ?U[0](:> Never, <: Nat))
// sub_unify(?T[0](:> Never, <: Str), ?U[1](:> Never, <: Int)): (?T[0](:> Never, <: Str and Int) --> Error!)
// sub_unify(?T[0](:> Int, <: Add()), ?U[1](:> Never, <: Mul())): (?T[0](:> Int, <: Add() and Mul()))
// sub_unify(?T[0](:> Str, <: Obj), ?U[1](:> Int, <: Obj)): (/* ?U[1] --> ?T[0](:> Str or Int) */)
(Type::FreeVar(lfv), Type::FreeVar(rfv))
if lfv.constraint_is_sandwiched() && rfv.constraint_is_sandwiched() =>
{
if lfv.is_generalized() || rfv.is_generalized() {
return Ok(());
}
let (lsub, lsup) = lfv.get_subsup().unwrap();
let (rsub, rsup) = rfv.get_subsup().unwrap();
// ?T(<: Add(?T))
// ?U(:> {1, 2}, <: Add(?U)) ==> {1, 2}
rfv.forced_undoable_link(&rsub);
for (lps, rps) in lsub.typarams().iter().zip(rsub.typarams().iter()) {
self.sub_unify_tp(lps, rps, None, loc, false)
.map_err(|errs| {
rfv.undo();
errs
})?;
}
// lsup: Add(?X(:> Int)), rsup: Add(?Y(:> Nat))
// => lsup: Add(?X(:> Int)), rsup: Add((?X(:> Int)))
for (lps, rps) in lsup.typarams().iter().zip(rsup.typarams().iter()) {
self.sub_unify_tp(lps, rps, None, loc, false)
.map_err(|errs| {
rfv.undo();
errs
})?;
}
rfv.undo();
let intersec = self.intersection(&lsup, &rsup);
let new_constraint = if intersec != Type::Never {
Constraint::new_sandwiched(self.union(&lsub, &rsub), intersec)
} else {
return Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
maybe_sub,
maybe_sup,
loc,
self.caused_by(),
)));
};
if lfv.level().unwrap() <= rfv.level().unwrap() {
lfv.update_constraint(new_constraint, false);
rfv.link(maybe_sub);
} else {
rfv.update_constraint(new_constraint, false);
lfv.link(maybe_sup);
}
Ok(())
}
(Type::FreeVar(lfv), _) if lfv.is_linked() => {
self.sub_unify(&lfv.crack(), maybe_sup, loc, param_name)
}
(_, Type::FreeVar(rfv)) if rfv.is_linked() => {
self.sub_unify(maybe_sub, &rfv.crack(), loc, param_name)
}
(_, Type::FreeVar(rfv)) if rfv.is_unbound() => {
// NOTE: cannot `borrow_mut` because of cycle reference
let rfv_ref = unsafe { rfv.as_ptr().as_mut().unwrap() };
match rfv_ref {
FreeKind::NamedUnbound { constraint, .. }
| FreeKind::Unbound { constraint, .. } => match constraint {
// * sub_unify(Nat, ?E(<: Eq(?E)))
// sub !<: l => OK (sub will widen)
// sup !:> l => Error
// * sub_unify(Str, ?T(:> _, <: Int)): (/* Error */)
// * sub_unify(Ratio, ?T(:> _, <: Int)): (/* Error */)
// sub = max(l, sub) if max exists
// * sub_unify(Nat, ?T(:> Int, <: _)): (/* OK */)
// * sub_unify(Int, ?T(:> Nat, <: Obj)): (?T(:> Int, <: Obj))
// * sub_unify(Nat, ?T(:> Never, <: Add(?R))): (?T(:> Nat, <: Add(?R))
// sub = union(l, sub) if max does not exist
// * sub_unify(Str, ?T(:> Int, <: Obj)): (?T(:> Str or Int, <: Obj))
// * sub_unify({0}, ?T(:> {1}, <: Nat)): (?T(:> {0, 1}, <: Nat))
// * sub_unify(Bool, ?T(<: Bool or Y)): (?T == Bool)
Constraint::Sandwiched { sub, sup } => {
let new_sub = self.union(maybe_sub, sub);
if sup.contains_union(&new_sub) {
rfv.link(&new_sub); // Bool <: ?T <: Bool or Y ==> ?T == Bool
} else {
*constraint = Constraint::new_sandwiched(new_sub, mem::take(sup));
}
}
// sub_unify(Nat, ?T(: Type)): (/* ?T(:> Nat) */)
Constraint::TypeOf(ty) => {
if self.supertype_of(&Type, ty) {
*constraint = Constraint::new_supertype_of(maybe_sub.clone());
} else {
todo!()
}
}
Constraint::Uninited => unreachable!(),
},
_ => {}
}
Ok(())
}
(Type::FreeVar(lfv), _) if lfv.is_unbound() => {
let lfv_ref = unsafe { lfv.as_ptr().as_mut().unwrap() };
match lfv_ref {
FreeKind::NamedUnbound { constraint, .. }
| FreeKind::Unbound { constraint, .. } => match constraint {
// sub !<: r => Error
// * sub_unify(?T(:> Int, <: _), Nat): (/* Error */)
// * sub_unify(?T(:> Nat, <: _), Str): (/* Error */)
// sup !:> r => Error
// * sub_unify(?T(:> _, <: Str), Int): (/* Error */)
// * sub_unify(?T(:> _, <: Int), Nat): (/* Error */)
// sub <: r, sup :> r => sup = min(sup, r) if min exists
// * sub_unify(?T(:> Never, <: Nat), Int): (/* OK */)
// * sub_unify(?T(:> Nat, <: Obj), Int): (?T(:> Nat, <: Int))
// sup = union(sup, r) if min does not exist
// * sub_unify(?T(:> Never, <: {1}), {0}): (?T(:> Never, <: {0, 1}))
Constraint::Sandwiched { sub, sup } => {
// REVIEW: correct?
if let Some(new_sup) = self.min(sup, maybe_sup) {
*constraint =
Constraint::new_sandwiched(mem::take(sub), new_sup.clone());
} else {
let new_sup = self.union(sup, maybe_sup);
*constraint = Constraint::new_sandwiched(mem::take(sub), new_sup);
}
}
// sub_unify(?T(: Type), Int): (?T(<: Int))
Constraint::TypeOf(ty) => {
if self.supertype_of(&Type, ty) {
*constraint = Constraint::new_subtype_of(maybe_sup.clone());
} else {
todo!()
}
}
Constraint::Uninited => unreachable!(),
},
_ => {}
}
Ok(())
}
(Type::FreeVar(_fv), _r) => todo!(),
(Type::Record(lrec), Type::Record(rrec)) => {
for (k, l) in lrec.iter() {
if let Some(r) = rrec.get(k) {
self.sub_unify(l, r, loc, param_name)?;
} else {
return Err(TyCheckErrors::from(TyCheckError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
maybe_sub,
maybe_sup,
loc,
self.caused_by(),
)));
}
}
Ok(())
}
(Type::Subr(lsub), Type::Subr(rsub)) => {
for lpt in lsub.default_params.iter() {
if let Some(rpt) = rsub
.default_params
.iter()
.find(|rpt| rpt.name() == lpt.name())
{
// contravariant
self.sub_unify(rpt.typ(), lpt.typ(), loc, param_name)?;
} else {
todo!()
}
}
lsub.non_default_params
.iter()
.zip(rsub.non_default_params.iter())
.try_for_each(|(l, r)| {
// contravariant
self.sub_unify(r.typ(), l.typ(), loc, param_name)
})?;
// covariant
self.sub_unify(&lsub.return_t, &rsub.return_t, loc, param_name)?;
Ok(())
}
(Type::Quantified(lsub), Type::Subr(rsub)) => {
let Type::Subr(lsub) = lsub.as_ref() else { unreachable!() };
for lpt in lsub.default_params.iter() {
if let Some(rpt) = rsub
.default_params
.iter()
.find(|rpt| rpt.name() == lpt.name())
{
if lpt.typ().is_generalized() {
continue;
}
// contravariant
self.sub_unify(rpt.typ(), lpt.typ(), loc, param_name)?;
} else {
todo!()
}
}
lsub.non_default_params
.iter()
.zip(rsub.non_default_params.iter())
.try_for_each(|(l, r)| {
if l.typ().is_generalized() {
Ok(())
}
// contravariant
else {
self.sub_unify(r.typ(), l.typ(), loc, param_name)
}
})?;
// covariant
if !lsub.return_t.is_generalized() {
self.sub_unify(&lsub.return_t, &rsub.return_t, loc, param_name)?;
}
Ok(())
}
(Type::Subr(lsub), Type::Quantified(rsub)) => {
let Type::Subr(rsub) = rsub.as_ref() else { unreachable!() };
for lpt in lsub.default_params.iter() {
if let Some(rpt) = rsub
.default_params
.iter()
.find(|rpt| rpt.name() == lpt.name())
{
// contravariant
if rpt.typ().is_generalized() {
continue;
}
self.sub_unify(rpt.typ(), lpt.typ(), loc, param_name)?;
} else {
todo!()
}
}
lsub.non_default_params
.iter()
.zip(rsub.non_default_params.iter())
.try_for_each(|(l, r)| {
// contravariant
if r.typ().is_generalized() {
Ok(())
} else {
self.sub_unify(r.typ(), l.typ(), loc, param_name)
}
})?;
// covariant
if !rsub.return_t.is_generalized() {
self.sub_unify(&lsub.return_t, &rsub.return_t, loc, param_name)?;
}
Ok(())
}
(
Type::Poly {
name: ln,
params: lps,
},
Type::Poly {
name: rn,
params: rps,
},
) => {
// e.g. Set(?T) <: Eq(Set(?T))
// Array(Str) <: Iterable(Str)
// Zip(T, U) <: Iterable(Tuple([T, U]))
if ln != rn {
self.nominal_sub_unify(maybe_sub, maybe_sup, rps, loc)
} else {
for (l_maybe_sub, r_maybe_sup) in lps.iter().zip(rps.iter()) {
self.sub_unify_tp(l_maybe_sub, r_maybe_sup, None, loc, false)?;
}
Ok(())
}
}
(
_,
Type::Poly {
params: sup_params, ..
},
) => self.nominal_sub_unify(maybe_sub, maybe_sup, sup_params, loc),
// (X or Y) <: Z is valid when X <: Z and Y <: Z
(Type::Or(l, r), _) => {
self.sub_unify(l, maybe_sup, loc, param_name)?;
self.sub_unify(r, maybe_sup, loc, param_name)
}
// X <: (Y and Z) is valid when X <: Y and X <: Z
(_, Type::And(l, r)) => {
self.sub_unify(maybe_sub, l, loc, param_name)?;
self.sub_unify(maybe_sub, r, loc, param_name)
}
// (X and Y) <: Z is valid when X <: Z or Y <: Z
(Type::And(l, r), _) => self
.sub_unify(l, maybe_sup, loc, param_name)
.or_else(|_e| self.sub_unify(r, maybe_sup, loc, param_name)),
// X <: (Y or Z) is valid when X <: Y or X <: Z
(_, Type::Or(l, r)) => self
.sub_unify(maybe_sub, l, loc, param_name)
.or_else(|_e| self.sub_unify(maybe_sub, r, loc, param_name)),
(_, Type::Ref(t)) => self.sub_unify(maybe_sub, t, loc, param_name),
(_, Type::RefMut { before, .. }) => self.sub_unify(maybe_sub, before, loc, param_name),
(Type::Proj { .. }, _) => todo!(),
(_, Type::Proj { .. }) => todo!(),
// TODO: Judgment for any number of preds
(Refinement(sub), Refinement(sup)) => {
// {I: Int or Str | I == 0} <: {I: Int}
if self.subtype_of(&sub.t, &sup.t) {
self.sub_unify(&sub.t, &sup.t, loc, param_name)?;
}
if sup.preds.is_empty() {
self.sub_unify(&sub.t, &sup.t, loc, param_name)?;
return Ok(());
}
if sub.preds.len() == 1 && sup.preds.len() == 1 {
let sub_first = sub.preds.iter().next().unwrap();
let sup_first = sup.preds.iter().next().unwrap();
self.sub_unify_pred(sub_first, sup_first, loc)?;
return Ok(());
}
log!(err "unification error: {sub} <: {sup}");
unreachable_error!(TyCheckErrors, TyCheckError, self)
}
// {I: Int | I >= 1} <: Nat == {I: Int | I >= 0}
(Type::Refinement(_), sup) => {
let sup = sup.clone().into_refinement();
self.sub_unify(maybe_sub, &Type::Refinement(sup), loc, param_name)
}
(sub, Type::Refinement(_)) => {
let sub = sub.clone().into_refinement();
self.sub_unify(&Type::Refinement(sub), maybe_sup, loc, param_name)
}
(Type::Subr(_) | Type::Record(_), Type) => Ok(()),
// REVIEW: correct?
(Type::Poly { name, .. }, Type) if &name[..] == "Array" || &name[..] == "Tuple" => {
Ok(())
}
(Type::Poly { .. }, _) => self.nominal_sub_unify(maybe_sub, maybe_sup, &[], loc),
(Type::Subr(_), Mono(name)) if &name[..] == "GenericCallable" => Ok(()),
_ => type_feature_error!(
self,
loc,
&format!("{maybe_sub} can be a subtype of {maybe_sup}, but failed to semi-unify")
),
}
}
// TODO: Current implementation is inefficient because coercion is performed twice with `subtype_of` in `sub_unify`
fn nominal_sub_unify(
&self,
maybe_sub: &Type,
maybe_sup: &Type,
sup_params: &[TyParam],
loc: Location,
) -> TyCheckResult<()> {
if let Some((sub_def_t, sub_ctx)) = self.get_nominal_type_ctx(maybe_sub) {
let mut tv_cache = TyVarCache::new(self.level, self);
let _sub_def_instance =
self.instantiate_t_inner(sub_def_t.clone(), &mut tv_cache, loc)?;
// e.g.
// maybe_sub: Zip(Int, Str)
// sub_def_t: Zip(T, U) ==> Zip(Int, Str)
// super_traits: [Iterable((T, U)), ...] ==> [Iterable((Int, Str)), ...]
self.substitute_typarams(sub_def_t, maybe_sub)
.map_err(|errs| {
Self::undo_substitute_typarams(sub_def_t);
errs
})?;
for sup_trait in sub_ctx.super_traits.iter() {
let sub_trait_instance =
self.instantiate_t_inner(sup_trait.clone(), &mut tv_cache, loc)?;
if self.supertype_of(maybe_sup, sup_trait) {
for (l_maybe_sub, r_maybe_sup) in
sub_trait_instance.typarams().iter().zip(sup_params.iter())
{
self.sub_unify_tp(l_maybe_sub, r_maybe_sup, None, loc, false)
.map_err(|errs| {
Self::undo_substitute_typarams(sub_def_t);
errs
})?;
}
Self::undo_substitute_typarams(sub_def_t);
return Ok(());
}
}
Self::undo_substitute_typarams(sub_def_t);
}
Err(TyCheckErrors::from(TyCheckError::unification_error(
self.cfg.input.clone(),
line!() as usize,
maybe_sub,
maybe_sup,
loc,
self.caused_by(),
)))
}
}

View file

@ -0,0 +1,341 @@
use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{enum_unwrap, fn_name, log, Str};
use erg_parser::ast;
use erg_parser::ast::AST;
use crate::context::instantiate::TyVarCache;
use crate::lower::ASTLowerer;
use crate::ty::constructors::mono;
use crate::ty::free::HasLevel;
use crate::ty::value::{GenTypeObj, TypeObj};
use crate::ty::{HasType, Type};
use crate::context::RegistrationMode;
use crate::error::{LowerError, LowerErrors, LowerResult};
use crate::hir;
use crate::hir::HIR;
use crate::varinfo::{Mutability, VarInfo, VarKind};
impl ASTLowerer {
fn declare_var(
&mut self,
sig: ast::VarSignature,
mut body: ast::DefBody,
) -> LowerResult<hir::Def> {
log!(info "entered {}({sig})", fn_name!());
if body.block.len() > 1 {
return Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
body.block.loc(),
self.module.context.caused_by(),
)));
}
let opt_spec_t = if let Some(t_spec) = &sig.t_spec {
let mut dummy_tv_cache =
TyVarCache::new(self.module.context.level, &self.module.context);
let t = self.module.context.instantiate_typespec(
t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
t.lift();
Some(self.module.context.generalize_t(t))
} else {
None
};
let chunk = self.declare_chunk(body.block.remove(0))?;
let py_name = if let hir::Expr::TypeAsc(tasc) = &chunk {
enum_unwrap!(tasc.expr.as_ref(), hir::Expr::Accessor)
.local_name()
.map(Str::rc)
} else {
sig.inspect().cloned()
};
let block = hir::Block::new(vec![chunk]);
let found_body_t = block.ref_t();
let ast::VarPattern::Ident(ident) = &sig.pat else { unreachable!() };
let id = body.id;
if let Some(spec_t) = opt_spec_t {
self.module
.context
.sub_unify(found_body_t, &spec_t, sig.loc(), None)?;
}
if let Some(py_name) = &py_name {
self.declare_instance(ident, found_body_t, py_name.clone())?;
} else {
self.module
.context
.assign_var_sig(&sig, found_body_t, id, py_name.clone())?;
}
// FIXME: Identifier::new should be used
let mut ident = hir::Identifier::bare(ident.dot.clone(), ident.name.clone());
ident.vi.t = found_body_t.clone();
ident.vi.py_name = py_name;
let sig = hir::VarSignature::new(ident);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Var(sig), body))
}
/// allowed: alias, import, const functions (e.g. Class)
fn declare_def(&mut self, def: ast::Def) -> LowerResult<hir::Def> {
log!(info "entered {}({})", fn_name!(), def.sig);
let name = if let Some(name) = def.sig.name_as_str() {
name.clone()
} else {
Str::ever("<lambda>")
};
if self
.module
.context
.registered_info(&name, def.sig.is_const())
.is_some()
&& def.sig.vis().is_private()
{
return Err(LowerErrors::from(LowerError::reassign_error(
self.cfg().input.clone(),
line!() as usize,
def.sig.loc(),
self.module.context.caused_by(),
&name,
)));
}
#[allow(clippy::let_and_return)]
let res = match def.sig {
ast::Signature::Subr(sig) => {
return Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
sig.loc(),
self.module.context.caused_by(),
)));
}
ast::Signature::Var(sig) => self.declare_var(sig, def.body),
};
// self.pop_append_errs();
res
}
fn fake_lower_obj(&self, obj: ast::Expr) -> LowerResult<hir::Expr> {
match obj {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => {
let acc = hir::Accessor::Ident(hir::Identifier::bare(ident.dot, ident.name));
Ok(hir::Expr::Accessor(acc))
}
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
let obj = self.fake_lower_obj(*attr.obj)?;
let ident = hir::Identifier::bare(attr.ident.dot, attr.ident.name);
Ok(obj.attr_expr(ident))
}
other => Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
other.loc(),
self.module.context.caused_by(),
))),
}
}
fn declare_ident(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({})", fn_name!(), tasc);
let is_instance_ascription = tasc.is_instance_ascription();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
match *tasc.expr {
ast::Expr::Accessor(ast::Accessor::Ident(mut ident)) => {
if cfg!(feature = "py_compatible") {
ident.trim_end_proc_mark();
}
let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
let t = self.module.context.instantiate_typespec(
&tasc.t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
t.lift();
let t = self.module.context.generalize_t(t);
if is_instance_ascription {
self.declare_instance(&ident, &t, py_name)?;
} else {
self.declare_subtype(&ident, &t)?;
}
let muty = Mutability::from(&ident.inspect()[..]);
let vis = ident.vis();
let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
let vi = VarInfo::new(
t,
muty,
vis,
VarKind::Declared,
None,
None,
Some(py_name),
self.module.context.absolutize(ident.loc()),
);
let ident = hir::Identifier::new(ident.dot, ident.name, None, vi);
Ok(hir::Expr::Accessor(hir::Accessor::Ident(ident)).type_asc(tasc.t_spec))
}
ast::Expr::Accessor(ast::Accessor::Attr(mut attr)) => {
if cfg!(feature = "py_compatible") {
attr.ident.trim_end_proc_mark();
}
let py_name = Str::rc(attr.ident.inspect().trim_end_matches('!'));
let t = self.module.context.instantiate_typespec(
&tasc.t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
let namespace = self.module.context.name.clone();
let ctx = self
.module
.context
.get_mut_singular_ctx(attr.obj.as_ref(), &namespace)?;
ctx.assign_var_sig(
&ast::VarSignature::new(ast::VarPattern::Ident(attr.ident.clone()), None),
&t,
ast::DefId(0),
Some(py_name),
)?;
let obj = self.fake_lower_obj(*attr.obj)?;
let muty = Mutability::from(&attr.ident.inspect()[..]);
let vis = attr.ident.vis();
let py_name = Str::rc(attr.ident.inspect().trim_end_matches('!'));
let vi = VarInfo::new(
t,
muty,
vis,
VarKind::Declared,
None,
None,
Some(py_name),
self.module.context.absolutize(attr.ident.loc()),
);
let ident = hir::Identifier::new(attr.ident.dot, attr.ident.name, None, vi);
let attr = obj.attr_expr(ident);
Ok(attr.type_asc(tasc.t_spec))
}
other => Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
other.loc(),
self.module.context.caused_by(),
))),
}
}
fn declare_instance(
&mut self,
ident: &ast::Identifier,
t: &Type,
py_name: Str,
) -> LowerResult<()> {
// .X = 'x': Type
if ident.is_raw() {
return Ok(());
}
if ident.is_const() {
let vi = VarInfo::new(
t.clone(),
Mutability::Const,
ident.vis(),
VarKind::Declared,
None,
None,
Some(py_name.clone()),
self.module.context.absolutize(ident.loc()),
);
self.module.context.decls.insert(ident.name.clone(), vi);
}
self.module.context.assign_var_sig(
&ast::VarSignature::new(ast::VarPattern::Ident(ident.clone()), None),
t,
ast::DefId(0),
Some(py_name),
)?;
match t {
Type::ClassType => {
let ty_obj = GenTypeObj::class(
mono(format!("{}{ident}", self.module.context.path())),
Some(TypeObj::Builtin(Type::Uninited)),
None,
);
self.module.context.register_gen_type(ident, ty_obj);
}
Type::TraitType => {
let ty_obj = GenTypeObj::trait_(
mono(format!("{}{ident}", self.module.context.path())),
TypeObj::Builtin(Type::Uninited),
None,
);
self.module.context.register_gen_type(ident, ty_obj);
}
_ => {}
}
Ok(())
}
fn declare_subtype(&mut self, ident: &ast::Identifier, trait_: &Type) -> LowerResult<()> {
if ident.is_raw() {
return Ok(());
}
if let Some((_, ctx)) = self.module.context.get_mut_type(ident.inspect()) {
ctx.register_marker_trait(trait_.clone());
Ok(())
} else {
Err(LowerErrors::from(LowerError::no_var_error(
self.cfg().input.clone(),
line!() as usize,
ident.loc(),
self.module.context.caused_by(),
ident.inspect(),
self.module.context.get_similar_name(ident.inspect()),
)))
}
}
fn declare_chunk(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
log!(info "entered {}", fn_name!());
match expr {
ast::Expr::Lit(lit) if lit.is_doc_comment() => {
Ok(hir::Expr::Lit(self.lower_literal(lit)?))
}
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.declare_def(def)?)),
ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.declare_ident(tasc)?)),
ast::Expr::Call(call)
if call
.additional_operation()
.map(|op| op.is_import())
.unwrap_or(false) =>
{
Ok(hir::Expr::Call(self.lower_call(call)?))
}
other => Err(LowerErrors::from(LowerError::declare_error(
self.cfg().input.clone(),
line!() as usize,
other.loc(),
self.module.context.caused_by(),
))),
}
}
pub(crate) fn declare_module(&mut self, ast: AST) -> HIR {
let mut module = hir::Module::with_capacity(ast.module.len());
for chunk in ast.module.into_iter() {
match self.declare_chunk(chunk) {
Ok(chunk) => {
module.push(chunk);
}
Err(errs) => {
self.errs.extend(errs);
}
}
}
HIR::new(ast.name, module)
}
}

View file

@ -0,0 +1,65 @@
use erg_common::log;
use erg_common::traits::Stream;
use crate::hir::{Accessor, Block, Expr, ReDef, HIR};
/// Desugares HIR to make it more like Python semantics.
pub struct HIRDesugarer {}
impl HIRDesugarer {
pub fn desugar(hir: HIR) -> HIR {
log!(info "HIR desugaring process has started.");
let hir = Self::desugar_class_member(hir);
log!(info "HIR desugaring process has completed.");
hir
}
/// ```erg
/// C = Class ...
/// C.
/// _Self = C
/// a = C.x
/// x = 1
/// m(self) = ...
/// ```
/// ↓
/// ```python
/// class C:
/// def m(self): ...
/// C._Self = C
/// C.a = C.x
/// C.x = 1
/// ```
fn desugar_class_member(mut hir: HIR) -> HIR {
for chunk in hir.module.iter_mut() {
let static_members = match chunk {
Expr::ClassDef(class_def) => {
let class = Expr::Accessor(Accessor::Ident(class_def.sig.ident().clone()));
let methods = std::mem::take(class_def.methods.ref_mut_payload());
let (methods, static_members): (Vec<_>, Vec<_>) = methods
.into_iter()
.partition(|attr| matches!(attr, Expr::Def(def) if def.sig.is_subr()));
class_def.methods.extend(methods);
static_members
.into_iter()
.map(|expr| match expr {
Expr::Def(def) => {
let acc = class.clone().attr(def.sig.into_ident());
let redef = ReDef::new(acc, def.body.block);
Expr::ReDef(redef)
}
_ => expr,
})
.collect()
}
_ => vec![],
};
if !static_members.is_empty() {
*chunk = Expr::Compound(Block::new(
[vec![std::mem::take(chunk)], static_members].concat(),
));
}
}
hir
}
}

View file

@ -0,0 +1,467 @@
//! implements SideEffectChecker
//! SideEffectCheckerを実装
//! 関数や不変型に副作用がないかチェックする
use erg_common::config::ErgConfig;
use erg_common::log;
use erg_common::traits::{Locational, Stream};
use erg_common::vis::Visibility;
use erg_common::Str;
use erg_parser::token::TokenKind;
use Visibility::*;
use crate::ty::HasType;
use crate::error::{EffectError, EffectErrors};
use crate::hir::{Array, Def, Dict, Expr, Set, Signature, Tuple, HIR};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum BlockKind {
// forbid side effects
Func,
ConstFunc,
ConstInstant, // e.g. Type definition
// allow side effects
Proc,
Instant,
Module,
}
use BlockKind::*;
/// Checks code for side effects.
/// For example:
/// * check if expressions with side effects are not used in functions
/// * check if methods that change internal state are not defined in immutable classes
#[derive(Debug)]
pub struct SideEffectChecker {
cfg: ErgConfig,
path_stack: Vec<(Str, Visibility)>,
block_stack: Vec<BlockKind>,
errs: EffectErrors,
}
impl SideEffectChecker {
pub fn new(cfg: ErgConfig) -> Self {
Self {
cfg,
path_stack: vec![],
block_stack: vec![],
errs: EffectErrors::empty(),
}
}
fn full_path(&self) -> String {
self.path_stack
.iter()
.fold(String::new(), |acc, (path, vis)| {
if vis.is_public() {
acc + "." + &path[..]
} else {
acc + "::" + &path[..]
}
})
}
/// It is permitted to define a procedure in a function,
/// and of course it is permitted to cause side effects in a procedure
///
/// However, it is not permitted to cause side effects within an instant block in a function
/// (side effects are allowed in instant blocks in procedures and modules)
fn in_context_effects_allowed(&self) -> bool {
// if toplevel
if self.block_stack.len() == 1 {
return true;
}
match (
self.block_stack.get(self.block_stack.len() - 2).unwrap(),
self.block_stack.last().unwrap(),
) {
(_, Func | ConstInstant) => false,
(_, Proc) => true,
(Proc | Module | Instant, Instant) => true,
_ => false,
}
}
pub fn check(mut self, hir: HIR) -> Result<HIR, (HIR, EffectErrors)> {
self.path_stack.push((hir.name.clone(), Private));
self.block_stack.push(Module);
log!(info "the side-effects checking process has started.{RESET}");
// At the top level, there is no problem with side effects, only check for purity violations.
// トップレベルでは副作用があっても問題なく、純粋性違反がないかのみチェックする
for expr in hir.module.iter() {
match expr {
Expr::Def(def) => {
self.check_def(def);
}
Expr::ClassDef(class_def) => {
if let Some(req_sup) = &class_def.require_or_sup {
self.check_expr(req_sup);
}
// TODO: grow
for def in class_def.methods.iter() {
self.check_expr(def);
}
}
Expr::PatchDef(patch_def) => {
self.check_expr(patch_def.base.as_ref());
// TODO: grow
for def in patch_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Call(call) => {
for parg in call.args.pos_args.iter() {
self.check_expr(&parg.expr);
}
for kwarg in call.args.kw_args.iter() {
self.check_expr(&kwarg.expr);
}
}
Expr::BinOp(bin) => {
self.check_expr(&bin.lhs);
self.check_expr(&bin.rhs);
}
Expr::UnaryOp(unary) => {
self.check_expr(&unary.expr);
}
Expr::Accessor(_) | Expr::Lit(_) => {}
Expr::Array(array) => match array {
Array::Normal(arr) => {
for elem in arr.elems.pos_args.iter() {
self.check_expr(&elem.expr);
}
}
Array::WithLength(arr) => {
self.check_expr(&arr.elem);
self.check_expr(&arr.len);
}
Array::Comprehension(arr) => {
self.check_expr(&arr.elem);
self.check_expr(&arr.guard);
}
},
Expr::Tuple(tuple) => match tuple {
Tuple::Normal(tuple) => {
for elem in tuple.elems.pos_args.iter() {
self.check_expr(&elem.expr);
}
}
},
Expr::Record(rec) => {
self.path_stack.push((Str::ever("<record>"), Private));
self.block_stack.push(Instant);
for attr in rec.attrs.iter() {
self.check_def(attr);
}
self.path_stack.pop();
self.block_stack.pop();
}
Expr::Set(set) => match set {
Set::Normal(set) => {
for elem in set.elems.pos_args.iter() {
self.check_expr(&elem.expr);
}
}
Set::WithLength(set) => {
self.check_expr(&set.elem);
self.check_expr(&set.len);
}
},
Expr::Dict(dict) => match dict {
Dict::Normal(dict) => {
for kv in dict.kvs.iter() {
self.check_expr(&kv.key);
self.check_expr(&kv.value);
}
}
other => todo!("{other}"),
},
Expr::TypeAsc(tasc) => {
self.check_expr(&tasc.expr);
}
Expr::Lambda(lambda) => {
let is_proc = lambda.is_procedural();
if is_proc {
self.path_stack.push((Str::ever("<lambda!>"), Private));
self.block_stack.push(Proc);
} else {
self.path_stack.push((Str::ever("<lambda>"), Private));
self.block_stack.push(Func);
}
lambda.body.iter().for_each(|chunk| self.check_expr(chunk));
self.path_stack.pop();
self.block_stack.pop();
}
Expr::ReDef(_)
| Expr::Code(_)
| Expr::Compound(_)
| Expr::Import(_)
| Expr::Dummy(_) => {}
}
}
log!(info "the side-effects checking process has completed, found errors: {}{RESET}", self.errs.len());
if self.errs.is_empty() {
Ok(hir)
} else {
Err((hir, self.errs))
}
}
fn check_def(&mut self, def: &Def) {
let name_and_vis = match &def.sig {
Signature::Var(var) => (var.inspect().clone(), var.vis()),
Signature::Subr(subr) => (subr.ident.inspect().clone(), subr.ident.vis()),
};
self.path_stack.push(name_and_vis);
let is_procedural = def.sig.is_procedural();
let is_subr = def.sig.is_subr();
let is_const = def.sig.is_const();
match (is_procedural, is_subr, is_const) {
(true, true, true) => {
panic!("user-defined constant procedures are not allowed");
}
(true, true, false) => {
self.block_stack.push(Proc);
}
(_, false, false) => {
self.block_stack.push(Instant);
}
(false, true, true) => {
self.block_stack.push(ConstFunc);
}
(false, true, false) => {
self.block_stack.push(Func);
}
(_, false, true) => {
self.block_stack.push(ConstInstant);
}
}
if let Signature::Subr(sig) = &def.sig {
let t = sig.ident.ref_t();
for (nd_param, nd_type) in sig
.params
.non_defaults
.iter()
.zip(t.non_default_params().unwrap())
{
if nd_type.typ().is_procedure() && !nd_param.inspect().unwrap().ends_with('!') {
self.errs.push(EffectError::proc_assign_error(
self.cfg.input.clone(),
line!() as usize,
nd_param.pat.loc(),
self.full_path(),
));
}
}
if let Some((var_arg, va_type)) = sig.params.var_args.as_ref().zip(t.var_args()) {
if va_type.typ().is_procedure() && !var_arg.inspect().unwrap().ends_with('!') {
self.errs.push(EffectError::proc_assign_error(
self.cfg.input.clone(),
line!() as usize,
var_arg.pat.loc(),
self.full_path(),
));
}
}
for (d_param, d_type) in sig.params.defaults.iter().zip(t.default_params().unwrap()) {
if d_type.typ().is_procedure() && !d_param.inspect().unwrap().ends_with('!') {
self.errs.push(EffectError::proc_assign_error(
self.cfg.input.clone(),
line!() as usize,
d_param.sig.pat.loc(),
self.full_path(),
));
}
}
}
let last_idx = def.body.block.len() - 1;
for (i, chunk) in def.body.block.iter().enumerate() {
self.check_expr(chunk);
// e.g. `echo = print!`
if i == last_idx
&& self.block_stack.last().unwrap() == &Instant
&& !def.sig.is_procedural()
&& chunk.t().is_procedure()
{
self.errs.push(EffectError::proc_assign_error(
self.cfg.input.clone(),
line!() as usize,
def.sig.loc(),
self.full_path(),
));
}
}
self.path_stack.pop();
self.block_stack.pop();
}
/// check if `expr` has side-effects / purity violations.
///
/// returns effects, purity violations will be appended to `self.errs`.
///
/// causes side-effects:
/// ```python
/// p!() // 1 side-effect
/// p!(q!()) // 2 side-effects
/// x =
/// y = r!()
/// y + 1 // 1 side-effect
/// ```
/// causes no side-effects:
/// ```python
/// q! = p!
/// y = f(p!)
/// ```
/// purity violation:
/// ```python
/// for iter, i -> print! i
/// ```
fn check_expr(&mut self, expr: &Expr) {
match expr {
Expr::Lit(_) => {}
Expr::Def(def) => {
self.check_def(def);
}
Expr::ClassDef(class_def) => {
if let Some(req_sup) = &class_def.require_or_sup {
self.check_expr(req_sup);
}
for def in class_def.methods.iter() {
self.check_expr(def);
}
}
Expr::PatchDef(patch_def) => {
self.check_expr(patch_def.base.as_ref());
for def in patch_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Array(array) => match array {
Array::Normal(arr) => {
for elem in arr.elems.pos_args.iter() {
self.check_expr(&elem.expr);
}
}
Array::WithLength(arr) => {
self.check_expr(&arr.elem);
self.check_expr(&arr.len);
}
Array::Comprehension(arr) => {
self.check_expr(&arr.elem);
self.check_expr(&arr.guard);
}
},
Expr::Tuple(tuple) => match tuple {
Tuple::Normal(tup) => {
for arg in tup.elems.pos_args.iter() {
self.check_expr(&arg.expr);
}
}
},
Expr::Record(record) => {
self.path_stack.push((Str::ever("<record>"), Private));
self.block_stack.push(Instant);
for attr in record.attrs.iter() {
self.check_def(attr);
}
self.path_stack.pop();
self.block_stack.pop();
}
Expr::Set(set) => match set {
Set::Normal(set) => {
for elem in set.elems.pos_args.iter() {
self.check_expr(&elem.expr);
}
}
Set::WithLength(set) => {
self.check_expr(&set.elem);
self.check_expr(&set.len);
}
},
Expr::Dict(dict) => match dict {
Dict::Normal(dict) => {
for kv in dict.kvs.iter() {
self.check_expr(&kv.key);
self.check_expr(&kv.value);
}
}
other => todo!("{other}"),
},
Expr::Call(call) => {
if (call.obj.t().is_procedure()
|| call
.attr_name
.as_ref()
.map(|name| name.is_procedural())
.unwrap_or(false))
&& !self.in_context_effects_allowed()
{
self.errs.push(EffectError::has_effect(
self.cfg.input.clone(),
line!() as usize,
expr,
self.full_path(),
));
}
call.args
.pos_args
.iter()
.for_each(|parg| self.check_expr(&parg.expr));
call.args
.kw_args
.iter()
.for_each(|kwarg| self.check_expr(&kwarg.expr));
}
Expr::UnaryOp(unary) => {
self.check_expr(&unary.expr);
}
Expr::BinOp(bin) => {
self.check_expr(&bin.lhs);
self.check_expr(&bin.rhs);
if (bin.op.kind == TokenKind::IsOp || bin.op.kind == TokenKind::IsNotOp)
&& !self.in_context_effects_allowed()
{
self.errs.push(EffectError::has_effect(
self.cfg.input.clone(),
line!() as usize,
expr,
self.full_path(),
));
}
}
Expr::Lambda(lambda) => {
let is_proc = lambda.is_procedural();
if is_proc {
self.path_stack.push((Str::ever("<lambda!>"), Private));
self.block_stack.push(Proc);
} else {
self.path_stack.push((Str::ever("<lambda>"), Private));
self.block_stack.push(Func);
}
lambda.body.iter().for_each(|chunk| self.check_expr(chunk));
self.path_stack.pop();
self.block_stack.pop();
}
Expr::TypeAsc(type_asc) => {
self.check_expr(&type_asc.expr);
}
Expr::Accessor(acc) => {
if !self.in_context_effects_allowed() && acc.ref_t().is_mut_type() {
self.errs.push(EffectError::touch_mut_error(
self.cfg.input.clone(),
line!() as usize,
expr,
self.full_path(),
));
}
}
Expr::ReDef(_)
| Expr::Code(_)
| Expr::Compound(_)
| Expr::Import(_)
| Expr::Dummy(_) => {}
}
}
}

View file

@ -0,0 +1,50 @@
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage};
use erg_common::switch_lang;
use crate::error::*;
pub type EvalError = CompileError;
pub type EvalErrors = CompileErrors;
pub type EvalResult<T> = CompileResult<T>;
pub type SingleEvalResult<T> = SingleCompileResult<T>;
impl EvalError {
pub fn not_const_expr(input: Input, errno: usize, loc: Location, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => "定数式ではありません",
"simplified_chinese" => "不是常量表达式",
"traditional_chinese" => "不是常量表達式",
"english" => "not a constant expression",
),
errno,
NotConstExpr,
loc,
),
input,
caused_by,
)
}
pub fn invalid_literal(input: Input, errno: usize, loc: Location, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => "リテラルが不正です",
"simplified_chinese" => "字面量不合法",
"traditional_chinese" => "字面量不合法",
"english" => "invalid literal",
),
errno,
SyntaxError,
loc,
),
input,
caused_by,
)
}
}

View file

@ -0,0 +1,961 @@
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage};
use erg_common::style::{StyledStr, StyledString, StyledStrings};
use erg_common::traits::Locational;
use erg_common::vis::Visibility;
use erg_common::{switch_lang, Str};
use crate::error::*;
use crate::hir::{Expr, Identifier};
use crate::ty::{HasType, Type};
pub type LowerError = CompileError;
pub type LowerWarning = LowerError;
pub type LowerErrors = CompileErrors;
pub type LowerWarnings = LowerErrors;
pub type LowerResult<T> = CompileResult<T>;
pub type SingleLowerResult<T> = SingleCompileResult<T>;
impl LowerError {
pub fn syntax_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
desc: String,
hint: Option<String>,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
desc,
errno,
SyntaxError,
loc,
),
input,
caused_by,
)
}
pub fn unused_expr_warning(input: Input, errno: usize, expr: &Expr, caused_by: String) -> Self {
let desc = switch_lang!(
"japanese" => format!("式の評価結果(: {})が使われていません", expr.ref_t()),
"simplified_chinese" => format!("表达式评估结果(: {})未使用", expr.ref_t()),
"traditional_chinese" => format!("表達式評估結果(: {})未使用", expr.ref_t()),
"english" => format!("the evaluation result of the expression (: {}) is not used", expr.ref_t()),
);
let discard = StyledString::new("discard", Some(HINT), Some(ATTR));
let hint = switch_lang!(
"japanese" => format!("値を使わない場合は、{discard}関数を使用してください"),
"simplified_chinese" => format!("如果您不想使用该值,请使用{discard}函数"),
"traditional_chinese" => format!("如果您不想使用該值,請使用{discard}函數"),
"english" => format!("if you don't use the value, use {discard} function"),
);
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(expr.loc(), vec![], Some(hint))],
desc,
errno,
UnusedWarning,
expr.loc(),
),
input,
caused_by,
)
}
pub fn duplicate_decl_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = readable_name(name);
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{name}は既に宣言されています"),
"simplified_chinese" => format!("{name}已声明"),
"traditional_chinese" => format!("{name}已聲明"),
"english" => format!("{name} is already declared"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn duplicate_definition_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = readable_name(name);
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{name}は既に定義されています"),
"simplified_chinese" => format!("{name}已定义"),
"traditional_chinese" => format!("{name}已定義"),
"english" => format!("{name} is already defined"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn violate_decl_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
spec_t: &Type,
found_t: &Type,
) -> Self {
let name = StyledString::new(readable_name(name), Some(WARN), None);
let expect = StyledString::new(format!("{}", spec_t), Some(HINT), Some(ATTR));
let found = StyledString::new(format!("{}", found_t), Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{name}{expect}型として宣言されましたが、{found}型のオブジェクトが代入されています"),
"simplified_chinese" => format!("{name}被声明为{expect},但分配了一个{found}对象"),
"traditional_chinese" => format!("{name}被聲明為{expect},但分配了一個{found}對象"),
"english" => format!("{name} was declared as {expect}, but an {found} object is assigned"),
),
errno,
TypeError,
loc,
),
input,
caused_by,
)
}
pub fn no_var_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
similar_name: Option<&str>,
) -> Self {
let name = readable_name(name);
let hint = similar_name.map(|n| {
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
switch_lang!(
"japanese" => format!("似た名前の変数があります: {n}"),
"simplified_chinese" => format!("存在相同名称变量: {n}"),
"traditional_chinese" => format!("存在相同名稱變量: {n}"),
"english" => format!("exists a similar name variable: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{found}という変数は定義されていません"),
"simplified_chinese" => format!("{found}未定义"),
"traditional_chinese" => format!("{found}未定義"),
"english" => format!("{found} is not defined"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn access_before_def_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
defined_line: u32,
similar_name: Option<&str>,
) -> Self {
let name = readable_name(name);
let hint = similar_name.map(|n| {
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
switch_lang!(
"japanese" => format!("似た名前の変数があります: {n}"),
"simplified_chinese" => format!("存在相同名称变量: {n}"),
"traditional_chinese" => format!("存在相同名稱變量: {n}"),
"english" => format!("exists a similar name variable: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("定義({defined_line}行目)より前で{found}を参照することは出来ません"),
"simplified_chinese" => format!("{found}定义({defined_line}行)之前引用是不允许的"),
"traditional_chinese" => format!("{found}定義({defined_line}行)之前引用是不允許的"),
"english" => format!("cannot access {found} before its definition (line {defined_line})"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn access_deleted_var_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
del_line: u32,
similar_name: Option<&str>,
) -> Self {
let name = readable_name(name);
let hint = similar_name.map(|n| {
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
switch_lang!(
"japanese" => format!("似た名前の変数があります: {n}"),
"simplified_chinese" => format!("存在相同名称变量: {n}"),
"traditional_chinese" => format!("存在相同名稱變量: {n}"),
"english" => format!("exists a similar name variable: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("削除された変数{found}を参照することは出来ません({del_line}行目で削除)"),
"simplified_chinese" => format!("不能引用已删除的变量{found}({del_line}行)"),
"traditional_chinese" => format!("不能引用已刪除的變量{found}({del_line}行)"),
"english" => format!("cannot access deleted variable {found} (deleted at line {del_line})"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn no_type_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
similar_name: Option<&str>,
) -> Self {
let name = readable_name(name);
let hint = similar_name.map(|n| {
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
switch_lang!(
"japanese" => format!("似た名前の型があります: {n}"),
"simplified_chinese" => format!("存在相同名称类型: {n}"),
"traditional_chinese" => format!("存在相同名稱類型: {n}"),
"english" => format!("exists a similar name type: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{found}という型は定義されていません"),
"simplified_chinese" => format!("{found}未定义"),
"traditional_chinese" => format!("{found}未定義"),
"english" => format!("Type {found} is not defined"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn type_not_found(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
typ: &Type,
) -> Self {
let typ = StyledString::new(typ.to_string(), Some(ERR), Some(ATTR));
let hint = Some(switch_lang!(
"japanese" => format!("恐らくこれはErgコンパイラのバグです、{URL}へ報告してください"),
"simplified_chinese" => format!("这可能是Erg编译器的错误请报告给{URL}"),
"traditional_chinese" => format!("這可能是Erg編譯器的錯誤請報告給{URL}"),
"english" => format!("This may be a bug of Erg compiler, please report to {URL}"),
));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{typ}という型は定義されていません"),
"simplified_chinese" => format!("{typ}未定义"),
"traditional_chinese" => format!("{typ}未定義"),
"english" => format!("Type {typ} is not defined"),
),
errno,
NameError,
loc,
),
input,
caused_by,
)
}
pub fn no_attr_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
obj_t: &Type,
name: &str,
similar_name: Option<&str>,
) -> Self {
let hint = similar_name.map(|n| {
switch_lang!(
"japanese" => format!("似た名前の属性があります: {n}"),
"simplified_chinese" => format!("具有相同名称的属性: {n}"),
"traditional_chinese" => format!("具有相同名稱的屬性: {n}"),
"english" => format!("has a similar name attribute: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{obj_t}型オブジェクトに{found}という属性はありません"),
"simplified_chinese" => format!("{obj_t}对象没有属性{found}"),
"traditional_chinese" => format!("{obj_t}對像沒有屬性{found}"),
"english" => format!("{obj_t} object has no attribute {found}"),
),
errno,
AttributeError,
loc,
),
input,
caused_by,
)
}
#[allow(clippy::too_many_arguments)]
pub fn singular_no_attr_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
obj_name: &str,
obj_t: &Type,
name: &str,
similar_name: Option<&str>,
) -> Self {
let hint = similar_name.map(|n| {
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
switch_lang!(
"japanese" => format!("似た名前の属性があります: {n}"),
"simplified_chinese" => format!("具有相同名称的属性: {n}"),
"traditional_chinese" => format!("具有相同名稱的屬性: {n}"),
"english" => format!("has a similar name attribute: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{obj_name}(: {obj_t})に{found}という属性はありません"),
"simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{found}"),
"traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{found}"),
"english" => format!("{obj_name}(: {obj_t}) has no attribute {found}"),
),
errno,
AttributeError,
loc,
),
input,
caused_by,
)
}
pub fn reassign_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = StyledStr::new(readable_name(name), Some(WARN), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("変数{name}に複数回代入することはできません"),
"simplified_chinese" => format!("不能为变量{name}分配多次"),
"traditional_chinese" => format!("不能為變量{name}分配多次"),
"english" => format!("variable {name} cannot be assigned more than once"),
),
errno,
AssignError,
loc,
),
input,
caused_by,
)
}
pub fn del_error(
input: Input,
errno: usize,
ident: &Identifier,
is_const: bool,
caused_by: String,
) -> Self {
let prefix = if is_const {
switch_lang!(
"japanese" => "定数",
"simplified_chinese" => "定数",
"traditional_chinese" => "定數",
"english" => "constant",
)
} else {
switch_lang!(
"japanese" => "組み込み変数",
"simplified_chinese" => "内置变量",
"traditional_chinese" => "内置變量",
"english" => "built-in variable",
)
};
let name = StyledString::new(readable_name(ident.inspect()), Some(WARN), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(ident.loc())],
switch_lang!(
"japanese" => format!("{prefix}{name}は削除できません"),
"simplified_chinese" => format!("{prefix}{name}不能删除"),
"traditional_chinese" => format!("{prefix}{name}不能刪除"),
"english" => format!("{prefix} {name} cannot be deleted"),
),
errno,
NameError,
ident.loc(),
),
input,
caused_by,
)
}
pub fn visibility_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
vis: Visibility,
) -> Self {
let visibility = if vis.is_private() {
switch_lang!(
"japanese" => "非公開",
"simplified_chinese" => "私有",
"traditional_chinese" => "私有",
"english" => "private",
)
} else {
switch_lang!(
"japanese" => "公開",
"simplified_chinese" => "公有",
"traditional_chinese" => "公有",
"english" => "public",
)
};
let found = StyledString::new(readable_name(name), Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{found}{visibility}変数です"),
"simplified_chinese" => format!("{found}{visibility}变量",),
"traditional_chinese" => format!("{found}{visibility}變量",),
"english" => format!("{found} is {visibility} variable",),
),
errno,
VisibilityError,
loc,
),
input,
caused_by,
)
}
pub fn override_error<S: Into<String>>(
input: Input,
errno: usize,
name: &str,
name_loc: Location,
superclass: &Type,
caused_by: S,
) -> Self {
let name = StyledString::new(name, Some(ERR), Some(ATTR));
let superclass = StyledString::new(format!("{}", superclass), Some(WARN), Some(ATTR));
let hint = Some(
switch_lang!(
"japanese" => {
let mut ovr = StyledStrings::default();
ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR);
ovr.push_str("デコレータを使用してください");
ovr
},
"simplified_chinese" => {
let mut ovr = StyledStrings::default();
ovr.push_str("请使用");
ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR);
ovr.push_str("装饰器");
ovr
},
"traditional_chinese" => {
let mut ovr = StyledStrings::default();
ovr.push_str("請使用");
ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR);
ovr.push_str("裝飾器");
ovr
},
"english" => {
let mut ovr = StyledStrings::default();
ovr.push_str("use ");
ovr.push_str_with_color_and_attribute("@Override", HINT, ATTR);
ovr.push_str(" decorator");
ovr
},
)
.to_string(),
);
let sub_msg = switch_lang!(
"japanese" => "デフォルトでオーバーライドはできません",
"simplified_chinese" => "默认不可重写",
"simplified_chinese" => "默認不可重寫",
"english" => "cannot override by default",
);
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(
name_loc,
vec![sub_msg.to_string()],
hint,
)],
switch_lang!(
"japanese" => format!(
"{name}は{superclass}で既に定義されています",
),
"simplified_chinese" => format!(
"{name}已在{superclass}中定义",
),
"traditional_chinese" => format!(
"{name}已在{superclass}中定義",
),
"english" => format!(
"{name} is already defined in {superclass}",
),
),
errno,
NameError,
name_loc,
),
input,
caused_by.into(),
)
}
pub fn inheritance_error(
input: Input,
errno: usize,
class: String,
loc: Location,
caused_by: String,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{class}は継承できません"),
"simplified_chinese" => format!("{class}不可继承"),
"traditional_chinese" => format!("{class}不可繼承"),
"english" => format!("{class} is not inheritable"),
),
errno,
InheritanceError,
loc,
),
input,
caused_by,
)
}
pub fn file_error(
input: Input,
errno: usize,
desc: String,
loc: Location,
caused_by: String,
hint: Option<String>,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
desc,
errno,
IoError,
loc,
),
input,
caused_by,
)
}
pub fn module_env_error(
input: Input,
errno: usize,
mod_name: &str,
loc: Location,
caused_by: String,
) -> Self {
let desc = switch_lang!(
"japanese" => format!("{mod_name}モジュールはお使いの環境をサポートしていません"),
"simplified_chinese" => format!("{mod_name}模块不支持您的环境"),
"traditional_chinese" => format!("{mod_name}模塊不支持您的環境"),
"english" => format!("module {mod_name} is not supported in your environment"),
);
Self::file_error(input, errno, desc, loc, caused_by, None)
}
pub fn import_error(
input: Input,
errno: usize,
desc: String,
loc: Location,
caused_by: String,
similar_erg_mod: Option<Str>,
similar_py_mod: Option<Str>,
) -> Self {
let mut erg_str = StyledStrings::default();
let mut py_str = StyledStrings::default();
let hint = switch_lang!(
"japanese" => {
match (similar_erg_mod, similar_py_mod) {
(Some(erg), Some(py)) => {
erg_str.push_str("似た名前のergモジュールが存在します: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
py_str.push_str("似た名前のpythonモジュールが存在します: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("pythonのモジュールをインポートするためには");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
hint.push_str("を使用してください");
Some(hint.to_string())
}
(Some(erg), None) => {
erg_str.push_str("似た名前のergモジュールが存在します");
erg_str.push_str_with_color_and_attribute(erg, ACCENT, ATTR);
None
}
(None, Some(py)) => {
py_str.push_str("似た名前のpythonモジュールが存在します");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("pythonのモジュールをインポートするためには");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
hint.push_str("を使用してください");
Some(hint.to_string())
}
(None, None) => None,
}
},
"simplified_chinese" => {
match (similar_erg_mod, similar_py_mod) {
(Some(erg), Some(py)) => {
erg_str.push_str("存在相似名称的erg模块: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
py_str.push_str("存在相似名称的python模块: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("要导入python模块,请使用");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(Some(erg), None) => {
erg_str.push_str("存在相似名称的erg模块: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
None
}
(None, Some(py)) => {
py_str.push_str("存在相似名称的python模块: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("要导入python模块,请使用");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(None, None) => None,
}
},
"traditional_chinese" => {
match (similar_erg_mod, similar_py_mod) {
(Some(erg), Some(py)) => {
erg_str.push_str("存在類似名稱的erg模塊: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
py_str.push_str("存在類似名稱的python模塊: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("要導入python模塊, 請使用");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(Some(erg), None) => {
erg_str.push_str("存在類似名稱的erg模塊: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
None
}
(None, Some(py)) => {
py_str.push_str("存在類似名稱的python模塊: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("要導入python模塊, 請使用");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(None, None) => None,
}
},
"english" => {
match (similar_erg_mod, similar_py_mod) {
(Some(erg), Some(py)) => {
erg_str.push_str("similar name erg module exists: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
py_str.push_str("similar name python module exists: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("to import python modules, use ");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(Some(erg), None) => {
erg_str.push_str("similar name erg module exists: ");
erg_str.push_str_with_color_and_attribute(erg, HINT, ATTR);
None
}
(None, Some(py)) => {
py_str.push_str("similar name python module exits: ");
py_str.push_str_with_color_and_attribute(py, HINT, ATTR);
let mut hint = StyledStrings::default();
hint.push_str("to import python modules, use ");
hint.push_str_with_color_and_attribute("pyimport", ACCENT, ATTR);
Some(hint.to_string())
}
(None, None) => None,
}
},
);
// .to_string().is_empty() is not necessarily empty because there are Color or Attribute that are not displayed
let msg = match (erg_str.is_empty(), py_str.is_empty()) {
(false, false) => vec![erg_str.to_string(), py_str.to_string()],
(false, true) => vec![erg_str.to_string()],
(true, false) => vec![py_str.to_string()],
(true, true) => vec![],
};
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, msg, hint)],
desc,
errno,
ImportError,
loc,
),
input,
caused_by,
)
}
pub fn inner_typedef_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("型はトップレベルで定義されなければなりません"),
"simplified_chinese" => format!("类型必须在顶层定义"),
"traditional_chinese" => format!("類型必須在頂層定義"),
"english" => format!("types must be defined at the top level"),
),
errno,
TypeError,
loc,
),
input,
caused_by,
)
}
pub fn declare_error(input: Input, errno: usize, loc: Location, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("d.erファイル内では宣言、別名定義のみが許可されています"),
"simplified_chinese" => format!("在d.er文件中只允许声明和别名定义"),
"traditional_chinese" => format!("在d.er文件中只允許聲明和別名定義"),
"english" => format!("declarations and alias definitions are only allowed in d.er files"),
),
errno,
SyntaxError,
loc,
),
input,
caused_by,
)
}
#[allow(clippy::too_many_arguments)]
pub fn invalid_type_cast_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
cast_to: &Type,
hint: Option<String>,
) -> Self {
let name = StyledString::new(name, Some(WARN), Some(ATTR));
let found = StyledString::new(format!("{}", cast_to), Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => format!("{name}の型を{found}にキャストすることはできません"),
"simplified_chinese" => format!("{name}的类型无法转换为{found}"),
"traditional_chinese" => format!("{name}的類型無法轉換為{found}"),
"english" => format!("the type of {name} cannot be cast to {found}"),
),
errno,
TypeError,
loc,
),
input,
caused_by,
)
}
}
impl LowerWarning {
pub fn unused_warning(
input: Input,
errno: usize,
loc: Location,
name: &str,
caused_by: String,
) -> Self {
let name = StyledString::new(readable_name(name), Some(WARN), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("{name}は使用されていません"),
"simplified_chinese" => format!("{name}未使用"),
"traditional_chinese" => format!("{name}未使用"),
"english" => format!("{name} is not used"),
),
errno,
UnusedWarning,
loc,
),
input,
caused_by,
)
}
pub fn union_return_type_warning(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
fn_name: &str,
typ: &Type,
) -> Self {
let hint = switch_lang!(
"japanese" => format!("`{fn_name}(...): {typ} = ...`など明示的に戻り値型を指定してください"),
"simplified_chinese" => format!("请明确指定函数{fn_name}的返回类型,例如`{fn_name}(...): {typ} = ...`"),
"traditional_chinese" => format!("請明確指定函數{fn_name}的返回類型,例如`{fn_name}(...): {typ} = ...`"),
"english" => format!("please explicitly specify the return type of function {fn_name}, for example `{fn_name}(...): {typ} = ...`"),
);
LowerError::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], Some(hint))],
switch_lang!(
"japanese" => format!("関数{fn_name}の戻り値型が単一ではありません"),
"simplified_chinese" => format!("函数{fn_name}的返回类型不是单一的"),
"traditional_chinese" => format!("函數{fn_name}的返回類型不是單一的"),
"english" => format!("the return type of function {fn_name} is not single"),
),
errno,
TypeWarning,
loc,
),
input,
caused_by,
)
}
pub fn builtin_exists_warning(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = StyledStr::new(readable_name(name), Some(WARN), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("同名の組み込み関数{name}が既に存在します"),
"simplified_chinese" => format!("已存在同名的内置函数{name}"),
"traditional_chinese" => format!("已存在同名的內置函數{name}"),
"english" => format!("a built-in function named {name} already exists"),
),
errno,
NameWarning,
loc,
),
input,
caused_by,
)
}
}

View file

@ -0,0 +1,813 @@
pub mod eval;
pub mod lower;
pub mod tycheck;
use std::fmt;
use erg_common::config::Input;
use erg_common::error::{
ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage,
};
use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, Theme, THEME};
use erg_common::traits::{Locational, Stream};
use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
pub use crate::error::eval::*;
pub use crate::error::lower::*;
pub use crate::error::tycheck::*;
use crate::hir::Expr;
/// `unreachable!(self: Context)`
#[macro_export]
macro_rules! unreachable_error {
($Strcs: ident, $Strc: ident, $ctx: expr) => {
Err($Strcs::from($Strc::unreachable(
$ctx.cfg.input.clone(),
$crate::erg_common::fn_name!(),
line!(),
)))
};
($Strc: ident, $ctx: expr) => {
Err($Strc::unreachable(
$ctx.cfg.input.clone(),
$crate::erg_common::fn_name!(),
line!(),
))
};
}
/// `feature_error!($Strc: struct, ctx: Context, loc: Location, name: &str)`
#[macro_export]
macro_rules! feature_error {
($Strcs: ident, $Strc: ident, $ctx: expr, $loc: expr, $name: expr) => {
Err($Strcs::from($Strc::feature_error(
$ctx.cfg.input.clone(),
$loc,
$name,
$ctx.caused_by(),
)))
};
($Strc: ident, $ctx: expr, $loc: expr, $name: expr) => {
Err($Strc::feature_error(
$ctx.cfg.input.clone(),
$loc,
$name,
$ctx.caused_by(),
))
};
}
#[macro_export]
macro_rules! type_feature_error {
($ctx: expr, $loc: expr, $name: expr) => {
feature_error!(TyCheckErrors, TyCheckError, $ctx, $loc, $name)
};
}
pub fn ordinal_num(n: usize) -> String {
match n.to_string().chars().last().unwrap() {
'1' => format!("{n}st"),
'2' => format!("{n}nd"),
'3' => format!("{n}rd"),
_ => format!("{n}th"),
}
}
/// dname is for "double under name"
pub fn binop_to_dname(op: &str) -> &str {
match op {
"+" => "__add__",
"-" => "__sub__",
"*" => "__mul__",
"/" => "__div__",
"//" => "__floordiv__",
"**" => "__pow__",
"%" => "__mod__",
"@" => "__matmul__",
".." => "__rng__",
"<.." => "__lorng__",
"..<" => "__rorng__",
"<..<" => "__orng__",
"&&" | "&" | "and" => "__and__",
"||" | "|" | "or" => "__or__",
"^^" | "^" => "__xor__",
"in" => "__in__",
"notin" => "__notin__", // NOTE: this doesn't exist in Python
"contains" => "__contains__",
"subof" => "__subof__",
"supof" => "__supof__",
"is!" => "__is__!",
"isnot!" => "__isnot__!",
"==" => "__eq__",
"!=" => "__ne__",
"<" => "__lt__",
"<=" => "__le__",
">" => "__gt__",
">=" => "__ge__",
"<<" => "__lshift__",
">>" => "__rshift__",
other => todo!("no such binary operator: {other}"),
}
}
pub fn unaryop_to_dname(op: &str) -> &str {
match op {
"+" => "__pos__",
"-" => "__neg__",
"~" => "__invert__",
"!" => "__mutate__",
"..." => "__spread__",
other => todo!("no such unary operator: {other}"),
}
}
pub fn readable_name(name: &str) -> &str {
match name {
"__add__" => "`+`",
"__sub__" => "`-`",
"__mul__" => "`*`",
"__div__" => "`/`",
"__floordiv__" => "`//`",
"__pow__" => "`**`",
"__mod__" => "`%`",
"__matmul__" => "`@`",
"__rng__" => "`..`",
"__lorng__" => "`<..`",
"__rorng__" => "`..<`",
"__orng__" => "`<..<`",
"__and__" => "`and`", // TODO: `&&` if not boolean
"__or__" => "`or`", // TODO: `||` if not boolean
"__in__" => "`in`",
"__notin__" => "`notin`",
"__contains__" => "`contains`",
"__subof__" => "`subof`",
"__supof__" => "`supof`",
"__is__!" => "`is!`",
"__isnot__!" => "`isnot!`",
"__eq__" => "`==`",
"__ne__" => "`!=`",
"__lt__" => "`<`",
"__le__" => "`<=`",
"__gt__" => "`>`",
"__ge__" => "`>=`",
"__pos__" => "`+`",
"__neg__" => "`-`",
"__invert__" => "`~`",
"__mutate__" => "`!`",
"__spread__" => "`...`",
"__lshift__" => "`<<`",
"__rshift__" => "`>>`",
other => other,
}
}
#[derive(Debug, Clone)]
pub struct CompileError {
pub core: Box<ErrorCore>, // ErrorCore is large, so box it
pub input: Input,
pub caused_by: String,
pub theme: Theme,
}
impl_display_and_error!(CompileError);
impl From<ParserRunnerError> for CompileError {
fn from(err: ParserRunnerError) -> Self {
Self {
core: Box::new(err.core),
input: err.input,
caused_by: "".to_owned(),
theme: THEME,
}
}
}
impl ErrorDisplay for CompileError {
fn core(&self) -> &ErrorCore {
&self.core
}
fn input(&self) -> &Input {
&self.input
}
fn caused_by(&self) -> &str {
&self.caused_by
}
fn ref_inner(&self) -> Option<&Self> {
None
}
}
// found, error, rhs
const ERR: Color = THEME.colors.error;
// var name, lhs
const WARN: Color = THEME.colors.warning;
// expect, hint
const HINT: Color = THEME.colors.hint;
// url, accentuation
const ACCENT: Color = THEME.colors.accent;
// url and feature = pretty
const UNDERLINE: Attribute = Attribute::Underline;
#[cfg(not(feature = "pretty"))]
const ATTR: Attribute = Attribute::Bold;
#[cfg(feature = "pretty")]
const ATTR: Attribute = Attribute::Underline;
const URL: StyledStr = StyledStr::new(
"https://github.com/erg-lang/erg",
Some(ACCENT),
Some(UNDERLINE),
);
impl CompileError {
pub fn new(core: ErrorCore, input: Input, caused_by: String) -> Self {
Self {
core: Box::new(core),
input,
caused_by,
theme: THEME,
}
}
pub fn compiler_bug(
errno: usize,
input: Input,
loc: Location,
fn_name: &str,
line: u32,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"),
"simplified_chinese" => format!("这是Erg编译器的错误请报告给{URL}\n\n原因来自: {fn_name}:{line}"),
"traditional_chinese" => format!("這是Erg編譯器的錯誤請報告給{URL}\n\n原因來自: {fn_name}:{line}"),
"english" => format!("this is a bug of the Erg compiler, please report it to {URL}\n\ncaused from: {fn_name}:{line}"),
),
errno,
CompilerSystemError,
loc,
),
input,
"".to_owned(),
)
}
pub fn stack_bug(
input: Input,
loc: Location,
stack_len: u32,
block_id: usize,
fn_name: &str,
) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("\
(: {stack_len}, ID: {block_id})
({URL})
{fn_name}"),
"simplified_chinese" => format!("\
: {stack_len}id: {block_id}
Erg ({URL})
: {fn_name}"),
"traditional_chinese" => format!("\
: {stack_len}id: {block_id}\n
Erg ({URL})
: {fn_name}"),
"english" => format!("\
the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n
this is a bug of the Erg compiler, please report it to {URL}
caused from: {fn_name}"),
),
0,
CompilerSystemError,
loc,
),
input,
"".to_owned(),
)
}
pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"simplified_chinese" => format!("此功能({name})尚未实现"),
"traditional_chinese" => format!("此功能({name})尚未實現"),
"english" => format!("this feature({name}) is not implemented yet"),
),
0,
FeatureError,
loc,
),
input,
caused_by,
)
}
pub fn system_exit() -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(Location::Unknown)],
switch_lang!(
"japanese" => "システムを終了します",
"simplified_chinese" => "系统正在退出",
"traditional_chinese" => "系統正在退出",
"english" => "system is exiting",
),
0,
SystemExit,
Location::Unknown,
),
Input::Dummy,
"".to_owned(),
)
}
}
pub type EffectError = TyCheckError;
pub type EffectErrors = TyCheckErrors;
impl EffectError {
pub fn has_effect(input: Input, errno: usize, expr: &Expr, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(expr.loc())],
switch_lang!(
"japanese" => "この式には副作用があります",
"simplified_chinese" => "此表达式会产生副作用",
"traditional_chinese" => "此表達式會產生副作用",
"english" => "this expression causes a side-effect",
),
errno,
HasEffect,
expr.loc(),
),
input,
caused_by,
)
}
pub fn proc_assign_error(input: Input, errno: usize, loc: Location, caused_by: String) -> Self {
let hint = Some(
switch_lang!(
"japanese" => {
let mut s = StyledStrings::default();
s.push_str("変数の末尾に");
s.push_str_with_color_and_attribute("!", WARN, ATTR);
s.push_str("をつけてください");
s
},
"simplified_chinese" => {
let mut s = StyledStrings::default();
s.push_str("请在变量名后加上");
s.push_str_with_color_and_attribute("!", WARN, ATTR);
s
},
"traditional_chinese" => {
let mut s = StyledStrings::default();
s.push_str("請在變量名後加上");
s.push_str_with_color_and_attribute("!", WARN, ATTR);
s
},
"english" => {
let mut s = StyledStrings::default();
s.push_str("add ");
s.push_str_with_color_and_attribute("!", WARN, ATTR);
s.push_str(" to the end of the variable name");
s
},
)
.to_string(),
);
Self::new(
ErrorCore::new(
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
switch_lang!(
"japanese" => "プロシージャを通常の変数に代入することはできません",
"simplified_chinese" => "不能将过程赋值给普通变量",
"traditional_chinese" => "不能將過程賦值給普通變量",
"english" => "cannot assign a procedure to a normal variable",
),
errno,
HasEffect,
loc,
),
input,
caused_by,
)
}
pub fn touch_mut_error(input: Input, errno: usize, expr: &Expr, caused_by: String) -> Self {
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(expr.loc())],
switch_lang!(
"japanese" => "関数中で可変オブジェクトにアクセスすることは出来ません",
"simplified_chinese" => "函数中不能访问可变对象",
"traditional_chinese" => "函數中不能訪問可變對象",
"english" => "cannot access a mutable object in a function",
),
errno,
HasEffect,
expr.loc(),
),
input,
caused_by,
)
}
}
pub type OwnershipError = CompileError;
pub type OwnershipErrors = CompileErrors;
impl OwnershipError {
pub fn move_error(
input: Input,
errno: usize,
name: &str,
name_loc: Location,
moved_loc: Location,
caused_by: String,
) -> Self {
let found = StyledString::new(name, Some(ERR), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(name_loc)],
switch_lang!(
"japanese" => format!(
"{found}は{}行目ですでに移動されています",
moved_loc.ln_begin().unwrap_or(0)
),
"simplified_chinese" => format!(
"{found}已移至第{}行",
moved_loc.ln_begin().unwrap_or(0)
),
"traditional_chinese" => format!(
"{found}已移至第{}行",
moved_loc.ln_begin().unwrap_or(0)
),
"english" => format!(
"{found} was moved in line {}",
moved_loc.ln_begin().unwrap_or(0)
),
),
errno,
MoveError,
name_loc,
),
input,
caused_by,
)
}
}
#[derive(Debug, Clone)]
pub struct CompileErrors(Vec<CompileError>);
impl std::error::Error for CompileErrors {}
impl_stream_for_wrapper!(CompileErrors, CompileError);
impl From<ParserRunnerErrors> for CompileErrors {
fn from(err: ParserRunnerErrors) -> Self {
Self(err.into_iter().map(CompileError::from).collect())
}
}
impl From<Vec<CompileError>> for CompileErrors {
fn from(errs: Vec<CompileError>) -> Self {
Self(errs)
}
}
impl From<CompileError> for CompileErrors {
fn from(err: CompileError) -> Self {
Self(vec![err])
}
}
impl MultiErrorDisplay<CompileError> for CompileErrors {}
impl fmt::Display for CompileErrors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_all(f)
}
}
impl CompileErrors {
pub fn flush(&mut self) -> Self {
Self(self.0.drain(..).collect())
}
}
pub type SingleCompileResult<T> = Result<T, CompileError>;
pub type CompileResult<T> = Result<T, CompileErrors>;
pub type CompileWarning = CompileError;
pub type CompileWarnings = CompileErrors;
#[cfg(test)]
mod test {
use crate::{
error::*,
hir::Identifier,
ty::{Predicate, Type},
varinfo::{AbsLocation, VarInfo},
};
use erg_common::{config::Input, error::Location};
use erg_parser::ast::VarName;
// These Erg codes are not correct grammar.
// This test make sure sub_msg and hint are displayed correctly.
#[test]
fn default_error_format_confirmation() {
let mut errors = Vec::new();
let input = Input::Pipe("stack bug error".to_owned());
let loc = Location::Line(1);
let err = CompileError::stack_bug(input, loc, 0, 0, "FileName");
errors.push(err);
let input = Input::Pipe("checker bug error".to_owned());
let errno = 0;
let err = TyCheckError::checker_bug(input, errno, Location::Unknown, "name", 1);
errors.push(err);
let loc = Location::LineRange(1, 3);
let input = Input::Pipe("args\nmissing\nerror".to_string());
let caused_by = "<caused_by>";
let err = TyCheckError::args_missing_error(
input,
errno,
loc,
"\"Callee name here\"",
caused_by.into(),
vec!["sample".into(), "args".into(), "here".into()],
);
errors.push(err);
let loc = Location::Range {
ln_begin: 1,
col_begin: 0,
ln_end: 1,
col_end: 17,
};
let expect = Type::Nat;
let found = Type::Int;
let input = Input::Pipe("return type error".to_string());
let name = "name";
let err = TyCheckError::return_type_error(
input,
errno,
loc,
caused_by.to_string(),
name,
&expect,
&found,
);
errors.push(err);
let loc = Location::Range {
ln_begin: 1,
col_begin: 0,
ln_end: 1,
col_end: 4,
};
let expect = Type::Nat;
let found = Type::Int;
let input = Input::Pipe("type mismatch error".to_string());
let err = TyCheckError::type_mismatch_error(
input,
errno,
loc,
caused_by.into(),
name,
Some(1),
&expect,
&found,
None,
Some("hint message here".to_owned()),
);
errors.push(err);
let input = Input::Pipe(
"too_many_args_error(some_long_name_variable_1,
some_long_name_variable_2,
some_long_name_variable_3,
some_long_name_variable_4) ="
.to_string(),
);
let loc = Location::LineRange(1, 4);
let callee_name = "callee name";
let params_len = 3;
let pos_args_len = 4;
let kw_args_len = 4;
let err = TyCheckError::too_many_args_error(
input,
errno,
loc,
callee_name,
caused_by.to_string(),
params_len,
pos_args_len,
kw_args_len,
);
errors.push(err);
let input = Input::Pipe("argument error".to_string());
let loc = Location::range(1, 0, 1, 8);
let err = TyCheckError::argument_error(input, errno, loc, caused_by.to_string(), 1, 2);
errors.push(err);
let input = Input::Pipe("Nat <: Int <: Ratio".to_string());
let loc = Location::range(1, 0, 1, 10);
let sub_t = &Type::Nat;
let sup_t = &Type::Int;
let err =
TyCheckError::subtyping_error(input, errno, sub_t, sup_t, loc, caused_by.to_string());
errors.push(err);
let input = Input::Pipe("pred unification error".to_string());
let lhs = &Predicate::Const("Str".into());
let rhs = &Predicate::Const("Nat".into());
let err =
TyCheckError::pred_unification_error(input, errno, lhs, rhs, caused_by.to_string());
errors.push(err);
let input = Input::Pipe("Trait member type error".to_string());
let errno = 0;
let loc = Location::Range {
ln_begin: 1,
col_begin: 0,
ln_end: 1,
col_end: 5,
};
let t_ty = &Type::Float;
let exp = &Type::Nat;
let fnd = &Type::Obj;
let err = TyCheckError::trait_member_type_error(
input,
errno,
loc,
caused_by.to_string(),
"member name",
t_ty,
exp,
fnd,
Some("hint message here".to_string()),
);
errors.push(err);
let input = Input::Pipe("trait member not defined error".to_string());
let member_name = "member name";
let trait_type = &Type::ClassType;
let class_type = &Type::Ellipsis;
let hint = Some("hint message here".to_string());
let err = TyCheckError::trait_member_not_defined_error(
input,
errno,
caused_by.to_string(),
member_name,
trait_type,
class_type,
hint,
);
errors.push(err);
let input = Input::Pipe("singular no attribute error".to_string());
let loc = Location::Range {
ln_begin: 1,
col_begin: 0,
ln_end: 1,
col_end: 8,
};
let obj_name = "ojb name";
let obj_t = Type::Bool;
let name = "name";
let similar_name = Some("similar name");
let err = LowerError::singular_no_attr_error(
input,
errno,
loc,
caused_by.to_string(),
obj_name,
&obj_t,
name,
similar_name,
);
errors.push(err);
let input = Input::Pipe("ambiguous type error".to_string());
let expr = Identifier::new(
Some(erg_parser::token::Token {
kind: erg_parser::token::TokenKind::EOF,
content: "expr_content".into(),
lineno: 1,
col_begin: 1,
}),
VarName::from_str("variable_name".into()),
None,
VarInfo::new(
Type::Nat,
crate::varinfo::Mutability::Const,
erg_common::vis::Visibility::Private,
crate::varinfo::VarKind::Builtin,
None,
None,
None,
AbsLocation::unknown(),
),
);
let candidates = &[Type::Nat, Type::Inf, Type::Bool];
let err =
EvalError::ambiguous_type_error(input, errno, &expr, candidates, caused_by.to_string());
errors.push(err);
let input = Input::Pipe("invalid type cast error".to_string());
let loc = Location::range(1, 8, 1, 17);
let cast_to = Type::Error;
let hint = Some("hint message here".to_string());
let err = EvalError::invalid_type_cast_error(
input,
errno,
loc,
caused_by.to_string(),
name,
&cast_to,
hint,
);
errors.push(err);
let input = Input::Pipe("override error".to_string());
let name_loc = Location::range(1, 0, 1, 8);
let superclass = &Type::Failure;
let err = TyCheckError::override_error(
input,
errno,
name,
name_loc,
superclass,
caused_by.to_string(),
);
errors.push(err);
let input = Input::Pipe("visibility error".to_string());
let loc = Location::Line(1);
let vis = erg_common::vis::Visibility::Private;
let err =
TyCheckError::visibility_error(input, errno, loc, caused_by.to_string(), name, vis);
errors.push(err);
let input = Input::Pipe("import nunpy as np".to_string());
let errno = 0;
let desc = "nunpy is not defined".to_string();
let loc = Location::range(1, 7, 1, 12);
let similar_erg_mod = Some("numpyer".into());
let similar_py_mod = Some("numpy".into());
let err = TyCheckError::import_error(
input.clone(),
errno,
desc.clone(),
loc,
caused_by.to_string(),
similar_erg_mod.clone(),
similar_py_mod.clone(),
);
errors.push(err);
let err = TyCheckError::import_error(
input.clone(),
errno,
desc.clone(),
loc,
caused_by.to_string(),
None,
similar_py_mod,
);
errors.push(err);
let err = TyCheckError::import_error(
input.clone(),
errno,
desc.clone(),
loc,
caused_by.to_string(),
similar_erg_mod,
None,
);
errors.push(err);
let err =
TyCheckError::import_error(input, errno, desc, loc, caused_by.to_string(), None, None);
errors.push(err);
for err in errors.into_iter() {
print!("{err}");
}
}
}

File diff suppressed because it is too large Load diff

2458
crates/erg_compiler/hir.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
//! defines the compiler for Erg (ergc).
#![allow(clippy::large_enum_variant)]
extern crate erg_common;
pub extern crate erg_parser;
pub mod artifact;
pub mod build_hir;
mod compile;
pub use compile::*;
mod codegen;
pub mod context;
pub mod declare;
pub mod desugar_hir;
pub mod effectcheck;
pub mod error;
pub mod hir;
pub mod link;
pub mod lower;
pub mod module;
pub mod optimize;
pub mod ownercheck;
pub mod reorder;
pub mod transpile;
pub mod ty;
pub mod varinfo;
pub use build_hir::HIRBuilder;
pub use erg_parser::build_ast::ASTBuilder;
pub use transpile::Transpiler;

View file

@ -0,0 +1,14 @@
._StoreAction: ClassType
.ArgumentParser!: ClassType
.new = 'ArgumentParser': (description := Str, prog := Str) -> .ArgumentParser!
.ArgumentParser!.add_argument!: (
name: Str, # TODO: var-args
action := Str or NoneType,
default := Obj or NoneType,
type := Type,
required := Bool,
help := Str or NoneType,
choices := Obj or NoneType,
) => ._StoreAction
.ArgumentParser!.parse_args!: (args := Str or [Str; _] or NoneType,) => Obj # TODO: type with dependent types

View file

@ -0,0 +1,152 @@
'''
Prints the values to a stream, or to `sys.stdout` by default.
Parameters:
* `sep`:
string inserted between values, default a space.
* `end`:
string appended after the last value, default a newline.
* `file`:
a file-like object (stream); defaults to the current `sys.stdout`.
* `flush`:
whether to forcibly flush the stream.
'''
'''japanese
値をストリームに書き出すデフォルトでは`sys.stdout`に表示する
引数:
* `sep`:
値を区切る文字列デフォルトではスペース
* `end`:
最後の値の後に追加される文字列デフォルトでは改行
* `file`:
ストリームとしてのfile-likeなオブジェクトデフォルトでは現在の`sys.stdout`
* `flush`:
ストリームを強制的にフラッシュするかどうか
'''
.print!: (args: Ref(Obj), sep := Str, end := Str, file := Writable!, flush := Bool) => NoneType
'''
Open file and return a stream.
`file` is either a text or byte string giving the name (and the path
if the file isn't in the current working directory) of the file to
be opened or an integer file descriptor of the file to be
wrapped. (If a file descriptor is given, it is closed when the
returned I/O object is closed, unless closefd is set to False.)
`mode` is an optional string that specifies the mode in which the file
is opened. It defaults to 'r' which means open for reading in text
mode. Other common values are 'w' for writing (truncating the file if
it already exists), 'x' for creating and writing to a new file, and
'a' for appending (which on some Unix systems, means that all writes
append to the end of the file regardless of the current seek position).
In text mode, if encoding is not specified the encoding used is platform
dependent: locale.getencoding() is called to get the current locale encoding.
(For reading and writing raw bytes use binary mode and leave encoding
unspecified.) The available modes are:
========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
========= ===============================================================
The default mode is 'rt' (open for reading text). For binary random
access, the mode 'w+b' opens and truncates the file to 0 bytes, while
'r+b' opens the file without truncation. The 'x' mode implies 'w' and
raises an `FileExistsError` if the file already exists.
Python distinguishes between files opened in binary and text modes,
even when the underlying operating system doesn't. Files opened in
binary mode (appending 'b' to the mode argument) return contents as
bytes objects without any decoding. In text mode (the default, or when
't' is appended to the mode argument), the contents of the file are
returned as strings, the bytes having been first decoded using a
platform-dependent encoding or using the specified encoding if given.
buffering is an optional integer used to set the buffering policy.
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
line buffering (only usable in text mode), and an integer > 1 to indicate
the size of a fixed-size chunk buffer. When no buffering argument is
given, the default buffering policy works as follows:
* Binary files are buffered in fixed-size chunks; the size of the buffer
is chosen using a heuristic trying to determine the underlying device's
"block size" and falling back on `io.DEFAULT_BUFFER_SIZE`.
On many systems, the buffer will typically be 4096 or 8192 bytes long.
* "Interactive" text files (files for which isatty() returns True)
use line buffering. Other text files use the policy described above
for binary files.
`encoding` is the name of the encoding used to decode or encode the
file. This should only be used in text mode. The default encoding is
platform dependent, but any encoding supported by Python can be
passed. See the codecs module for the list of supported encodings.
`errors` is an optional string that specifies how encoding errors are to
be handled---this argument should not be used in binary mode. Pass
'strict' to raise a ValueError exception if there is an encoding error
(the default of None has the same effect), or pass 'ignore' to ignore
errors. (Note that ignoring encoding errors can lead to data loss.)
See the documentation for codecs.register or run 'help(codecs.Codec)'
for a list of the permitted encoding error strings.
`newline` controls how universal newlines works (it only applies to text
mode). It can be None, '', '\n', '\r', and '\r\n'. It works as
follows:
* On input, if newline is None, universal newlines mode is
enabled. Lines in the input can end in '\n', '\r', or '\r\n', and
these are translated into '\n' before being returned to the
caller. If it is '', universal newline mode is enabled, but line
endings are returned to the caller untranslated. If it has any of
the other legal values, input lines are only terminated by the given
string, and the line ending is returned to the caller untranslated.
* On output, if newline is None, any '\n' characters written are
translated to the system default line separator, os.linesep. If
newline is '' or '\n', no translation takes place. If newline is any
of the other legal values, any '\n' characters written are translated
to the given string.
If `closefd` is False, the underlying file descriptor will be kept open
when the file is closed. This does not work when a file name is given
and must be True in that case.
A custom opener can be used by passing a callable as *opener*. The
underlying file descriptor for the file object is then obtained by
calling *opener* with (*file*, *flags*). *opener* must return an open
file descriptor (passing os.open as *opener* results in functionality
similar to passing None).
open() returns a file object whose type depends on the mode, and
through which the standard file operations such as reading and writing
are performed. When open() is used to open a file in a text mode ('w',
'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open
a file in a binary mode, the returned class varies: in read binary
mode, it returns a BufferedReader; in write binary and append binary
modes, it returns a BufferedWriter, and in read/write mode, it returns
a BufferedRandom.
It is also possible to use a string or bytearray as a file for both
reading and writing. For strings StringIO can be used like a file
opened in a text mode, and for bytes a BytesIO can be used like a file
opened in a binary mode.
'''
.open!: (
file: PathLike,
mode := Str,
buffering := Int,
encoding := Str or NoneType,
errors := Str or NoneType,
newline := Str or NoneType,
closefd := Bool,
) => File!

View file

@ -0,0 +1,11 @@
.abc = pyimport "abc"
.NamedTuple = 'namedtuple': ClassType
.Deque = 'deque': ClassType
.ChainMap: ClassType
.Counter: ClassType
.OrderedDict: ClassType
.Defaultdict = 'defaultDict': ClassType
.UserDict: ClassType
.UserList: ClassType
.UserString: ClassType

View file

@ -0,0 +1,12 @@
# The ABCs in this file are not defined as a collection of traits.
# Use `std/abc` instead.
.Container: ClassType
.Hashable: ClassType
.Sized: ClassType
.Callable: ClassType
.Iterable: ClassType
.Collection: ClassType
.Iterator: ClassType
.Reversible: ClassType
.Genertor: ClassType
.Sequence: ClassType

View file

@ -0,0 +1,3 @@
.copy: |T: Type|(x: T) -> T
.deepcopy: |T: Type|(x: T) -> T
.Error: ClassType

View file

@ -0,0 +1,58 @@
time = pyimport "time"
.MINYEAR: {1}
.MAXYEAR: {9999}
.TimeDelta = 'timedelta': ClassType
.TimeDelta.__call__: (days := Nat, seconds := Nat, microseconds := Nat, milliseconds := Nat, minutes := Nat, hours := Nat, weeks := Nat) -> .TimeDelta
.TimeDelta.min: .TimeDelta
.TimeDelta.max: .TimeDelta
.TimeDelta.resolution: .TimeDelta
.TimeDelta.total_seconds: (self: .TimeDelta) -> Float
.Date = 'date': ClassType
.Date.__call__: (year: Nat, month: Nat, day: Nat) -> .Date
.Date.fromtimestamp: (timestamp: Float) -> .Date
.Date.fromordinal: (ordinal: Nat) -> .Date
.Date.fromisoformat: (date_string: Str) -> .Date
.Date.fromisocalendar: (year: Nat, week: Nat, day: Nat) -> .Date
.Date.replace: (self: .Date, year := Nat, month := Nat, day := Nat) -> .Date
.Date.timetuple: (self: .Date) -> time.StructTime
.Date.toordinal: (self: .Date) -> Nat
.Date.weekday: (self: .Date) -> 0..6
.Date.isoweekday: (self: .Date) -> 1..7
.Date.isocalendar: (self: .Date) -> {year = Nat; week = Nat; weekday = 1..7}
.Date.isoformat: (self: .Date) -> Str
.Date.strftime: (self: .Date, format: Str) -> Str
.Date.today!: () => .Date
.Date.min: .Date
.Date.max: .Date
.Date.resolution: .TimeDelta
.TZInfo = 'tzinfo': ClassType
.Time = 'time': ClassType
.Time.__call__: (hour: Nat, minute: Nat, second := Nat, microsecond := Nat, tzinfo := .TZInfo or NoneType) -> .Time
.Time.min: .Time
.Time.max: .Time
.Time.resolution: .TimeDelta
.Time.fromisoformat: (time_string: Str) -> .Time
.Time.replace: (self: .Time, hour := Nat, minute := Nat, second := Nat, microsecond := Nat, tzinfo := .TZInfo or NoneType) -> .Time
.Time.isoformat: (self: .Time, timespec := Str) -> Str
.DateTime = 'dateTime': ClassType
.DateTime.__call__: (year: Nat, month: Nat, day: Nat, hour := Nat, minute := Nat, second := Nat, microsecond := Nat, tzinfo := .TZInfo or NoneType) -> .DateTime
.DateTime.today!: () => .DateTime
.DateTime.now!: (tz := .TZInfo or NoneType) => .DateTime
.DateTime.utcnow!: () => .DateTime
.DateTime.fromtimestamp: (timestamp: Float, tz := .TZInfo or NoneType) -> .DateTime
.DateTime.utcfromtimestamp: (timestamp: Float) -> .DateTime
.DateTime.fromordinal: (ordinal: Nat) -> .DateTime
.DateTime.combine: (date: .Date, time: .Time, tzinfo := .TZInfo or NoneType) -> .DateTime
.DateTime.fromisoformat: (date_string: Str) -> .DateTime
.DateTime.fromisocalendar: (year: Nat, week: Nat, day: Nat) -> .DateTime
.DateTime.strptime: (date_string: Str, format: Str) -> .DateTime
.DateTime.min: .DateTime
.DateTime.max: .DateTime
.DateTime.resolution: .TimeDelta
.DateTime.date: (self: .DateTime) -> .Date
.DateTime.time: (self: .DateTime) -> .Time
.DateTime.replace: (self: .DateTime, year := Nat, month := Nat, day := Nat, hour := Nat, minute := Nat, second := Nat, microsecond := Nat, tzinfo := .TZInfo or NoneType) -> .DateTime
.DateTime.utcoffset: (self: .DateTime) -> .TimeDelta or NoneType
.TimeZone = 'timezone': ClassType

View file

@ -0,0 +1,9 @@
.dis!: (x: Code or ClassType or TraitType or Proc or Str or Bytes or NoneType := NoneType) => NoneType
.code_info: (x: Code or ClassType or TraitType or GenericCallable or Str or Bytes) -> Str
.show_code!: (x: Code or ClassType or TraitType or GenericCallable or Str or Bytes) => NoneType
.ByteCode: ClassType
.ByteCode.__call__: (x: GenericCallable or ClassType or TraitType or Str or Code) => .ByteCode
.Instruction: ClassType
.Positions: ClassType

View file

@ -0,0 +1,23 @@
.cmp: (f1: File!, f2: File!, shallow := Bool) -> Bool
.cmpfiles: (dir1: PathLike, dir2: PathLike, common: [Str; _], shallow := Bool) -> Bool
.clear_cache!: () => NoneType
.DirCmp = 'dircmp': ClassType
.DirCmp.__call__: (a: PathLike, b: PathLike, ignore := PathLike or NoneType, hide := PathLike or NoneType) -> .DirCmp
.DirCmp.left: Str
.DirCmp.right: Str
.DirCmp.left_list: [Str; _]
.DirCmp.right_list: [Str; _]
.DirCmp.left_only: [Str; _]
.DirCmp.right_only: [Str; _]
.DirCmp.common: [Str; _]
.DirCmp.common_dirs: [Str; _]
.DirCmp.common_files: [Str; _]
.DirCmp.common_funny: [Str; _]
.DirCmp.same_files: [Str; _]
.DirCmp.diff_files: [Str; _]
.DirCmp.funny_files: [Str; _]
.DirCmp.subdirs: {Str: .DirCmp}
.DirCmp.report!: (self: .DirCmp) => NoneType
.DirCmp.report_full_closure!: (self: .DirCmp) => NoneType
.DirCmp.report_partial_closure!: (self: .DirCmp) => NoneType

View file

@ -0,0 +1,4 @@
.cache: |T <: Proc|(user_function: T) -> T
.lru_cache: |T <: Proc|(user_function: T) -> T
.total_ordering: (cls: ClassType) -> ClassType
.reduce: |T: Type|(function!: T => T, iterable: Iterable(T), initializer := T or NoneType) -> T

View file

@ -0,0 +1,6 @@
.IGlob = '_iglob': ClassType
.IGlob <: Iterable(Str)
.glob!: (pathname: Str, recursive := Bool) => [Str; _]
.iglob!: (pathname: Str, recursive := Bool) => .IGlob
.escape: (pathname: Str) -> Str

View file

@ -0,0 +1,4 @@
.heappush!: |T: Type|(heap: Iterable(T), item: T) => NoneType # TODO: Push!
.heappop!: |T: Type|(heap: Iterable(T)) => T # TODO: Pop!
.heappushpop!: |T: Type|(heap: Iterable(T), item: T) => T # TODO: Push! and Pop!
.heapify!: (heap: Array(Obj, _)) => NoneType

View file

@ -0,0 +1,5 @@
.parser = pyimport "parser"
.entities = pyimport "entities"
.escape: (s: Str, quote := Bool) -> Str
.unescape: (s: Str,) -> Str

View file

@ -0,0 +1,4 @@
.html5: {Str: Str}
.entitydefs: {Str: Str}
.name2codepoint: {Str: Nat}
.codepoint2name: {Nat: Str}

View file

@ -0,0 +1,4 @@
.HTMLParser: ClassType
.HTMLParser.feed!: (self: RefMut(.HTMLParser), data: Str) => NoneType
.HTMLParser.close!: (self: .HTMLParser,) => NoneType
.HTMLParser.reset!: (self: RefMut(.HTMLParser),) => NoneType

View file

@ -0,0 +1 @@
.client = pyimport "client"

View file

@ -0,0 +1,3 @@
.HTTPResponse: ClassType
.HTTPResponse <: FileLike!
.HTTPResponse.read!: (self: RefMut(.HTTPResponse),) => Bytes

View file

@ -0,0 +1,5 @@
.HTTPServer: ClassType
.ThreadingServer: ClassType
.BaseHTTPReqeustHandler: ClassType
.SimpleHTTPRequestHandler: ClassType
.CGIHTTPRequestHandler: ClassType

View file

@ -0,0 +1,6 @@
.util = pyimport "util"
.machinery = pyimport "machinery"
.__import__: |S: Str|(name: {S}, globals := {Str: Obj}) -> Module S
.import_module: |S: Str|(name: {S}, package := Str or NoneType) -> Module S
.reload!: (GenericModule, ) => NoneType

View file

@ -0,0 +1 @@
.ModuleSpec: ClassType

View file

@ -0,0 +1,5 @@
machinery = pyimport "machinery"
.MAGIC_NUMBER: Bytes
.find_spec: (name: Str) -> machinery.ModuleSpec or NoneType

View file

@ -0,0 +1,29 @@
.FrameInfo: ClassType
.Traceback: ClassType
.ismodule: (object: Obj) -> Bool
.isclass: (object: Obj) -> Bool
.ismethod: (object: Obj) -> Bool
.isfunction: (object: Obj) -> Bool
.isgeneratorfunction: (object: Obj) -> Bool
.isgenerator: (object: Obj) -> Bool
.iscoroutinefunction: (object: Obj) -> Bool
.iscoroutine: (object: Obj) -> Bool
.isawaitable: (object: Obj) -> Bool
.isasyncgenfunction: (object: Obj) -> Bool
.isasyncgen: (object: Obj) -> Bool
.istraceback: (object: Obj) -> Bool
.isframe: (object: Obj) -> Bool
.iscode: (object: Obj) -> Bool
.isbuiltin: (object: Obj) -> Bool
.ismethodwrapper: (object: Obj) -> Bool
.isroutine: (object: Obj) -> Bool
.isabstract: (object: Obj) -> Bool
.ismethoddescriptor: (object: Obj) -> Bool
.isdatadescriptor: (object: Obj) -> Bool
.isgetsetdescriptor: (object: Obj) -> Bool
.ismemberdescriptor: (object: Obj) -> Bool
.getdoc: (object: Obj) -> Str
.getcomments: (object: Obj) -> Str
.getfile: (object: Obj) -> Str
.getmodule: (object: Obj) -> Module or NoneType

View file

@ -0,0 +1,18 @@
.DEFAULT_BUFFER_SIZE: {8192}
.StringIO!: ClassType
.StringIO! <: FileLike!
.StringIO!.read!: (self: RefMut(.StringIO!), ) => Str
.StringIO!.write!: (self: RefMut(.StringIO!), s: Str) => NoneType
.StringIO!.getvalue!: (self: Ref(.StringIO!),) => Str
.TextIOWrapper!: ClassType
.BytesIO!: ClassType
.BytesIO! <: FileLike!
.BytesIO!.read!: (self: RefMut(.BytesIO!), ) => Bytes
.BytesIO!.write!: (self: RefMut(.BytesIO!), b: Bytes) => NoneType
.newBytesIO = 'BytesIO': (bytes: Bytes,) -> .BytesIO!
.open!: (file: PathLike, mode := Str, buffering := Nat, encoding := Str or NoneType) -> File!
.open_code!: (path: PathLike) -> File!

View file

@ -0,0 +1,7 @@
.dump: (obj: Obj, fp: FileLike!) -> Str
.dumps: (obj: Obj,) -> Str
.load: (fp: File!,) -> Obj
.loads: (s: Str,) -> Obj
.JsonDecoder: ClassType
.JsonEncoder: ClassType

View file

@ -0,0 +1,40 @@
.Logger: ClassType
.Handler: ClassType
.Formatter: ClassType
.Filter: ClassType
.LogRecord: ClassType
.LogRecord.name: Str
.LogRecord.level: Nat
.LogRecord.pathname: Str
.LogRecord.lineno: Nat
.LogRecord.msg: Str
.LogRecord.args: GenericTuple
.LogRecord.exc_info: GenericTuple
.LogRecord.func: Str or NoneType
.LogRecord.sinfo: Str or NoneType
.LogRecord.getMessage: (self: .LogRecord) -> Str
.LoggerAdaptor: ClassType
.getLogger: (name: Str or NoneType := NoneType) -> .Logger
.getLoggerClass: () -> ClassType
.getLogRecordFactory: () -> ClassType
.debug!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.info!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.warning!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.error!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.critical!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.exception!: (msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.log!: (level: Nat, msg: Str, args := GenericTuple, kwargs := GenericDict) => NoneType
.disable!: (level := Nat) => NoneType
.addLevelName!: (level: Nat, levelName: Str) => NoneType
.getLevelNamesMapping!: () => GenericDict
.getLevelName!: (level: Nat) -> Str
.makeLogRecord!: (args: GenericTuple) -> .LogRecord
# TODO: stream
.basicConfig!: (level := Nat, format := Str, datefmt := Str, filename := Str, filemode := Str, stream := Obj) => NoneType
.shutdown!: () => NoneType

View file

@ -0,0 +1,37 @@
.pi: Float
.tau: Float
.e: Float
.sin: Float -> Float
.cos: Float -> Float
.tan: Float -> Float
.asin: Float -> Float
.acos: Float -> Float
.atan: Float -> Float
.sinh: Float -> Float
.cosh: Float -> Float
.tanh: Float -> Float
.asinh: Float -> Float
.acosh: Float -> Float
.atanh: Float -> Float
.sqrt: Float -> Float
.exp: Float -> Float
.log: Float -> Float
.log2: Float -> Float
.log10: Float -> Float
.floor: Float -> Int
.ceil: Float -> Int
.comb: (n: Int, k: Int) -> Int
.copysign: (x: Float, y: Float) -> Float
.trunc: Float -> Int
.fabs: (x: Float) -> Float
.factorial: (n: Nat) -> Nat
.fmod: (x: Float, y: Float) -> Float
.frexp: (x: Float) -> (Float, Int)
.fsum: (iterable: Iterable Float) -> Float
.isclose: (a: Float, b: Float, rel_tol := Float, abs_tol := Float) -> Bool
.isfinite: (x: Float) -> Bool
.isinf: (x: Float) -> Bool
.isnan: (x: Float) -> Bool
.isqrt: (n: Nat) -> Nat
.perm: (n: Nat, k := Nat) -> Nat
.prod: |T <: Mul(T)|(iterable: Iterable(T), start := T) -> T.Output

View file

@ -0,0 +1,22 @@
.lt: |T <: Ord|(T, T) -> Bool
.le: |T <: Ord|(T, T) -> Bool
.eq: |T <: Eq|(T, T) -> Bool
.ne: |T <: Eq|(T, T) -> Bool
.ge: |T <: Ord|(T, T) -> Bool
.gt: |T <: Ord|(T, T) -> Bool
.__lt__: |T <: Ord|(T, T) -> Bool
.__le__: |T <: Ord|(T, T) -> Bool
.__eq__: |T <: Eq|(T, T) -> Bool
.__ne__: |T <: Eq|(T, T) -> Bool
.__ge__: |T <: Ord|(T, T) -> Bool
.__gt__: |T <: Ord|(T, T) -> Bool
.not_: |T <: Bool|(T) -> Bool
.__not__: |T <: Bool|(T) -> Bool
.truth: Obj -> Bool
.is_: (Obj, Obj) -> Bool
.is_not: (Obj, Obj) -> Bool
.abs: Num -> Nat
.__abs__: Num -> Nat
.__add__: |T: Type, A <: Add(T)| (A, T) -> A.Output
.and_: (Int, Int) -> Bool
.__and__: (Int, Int) -> Bool

View file

@ -0,0 +1,21 @@
.path = pyimport "path"
# .PathLike: ClassType
.name: Str
.chdir!: (path: PathLike, ) => NoneType
.getcwd!: () => Str
.getenv!: (key: Str, default: Str or NoneType := NoneType) => Str
.listdir!: (path := PathLike,) => [Str; _]
.mkdir!: (path: PathLike, mode := Nat) => NoneType
.putenv!: (key: Str, value: Str) => NoneType
.remove!: (path: PathLike,) => NoneType
.removedirs!: (path: PathLike,) => NoneType
.rename!: (src: PathLike, dst: PathLike) => NoneType
.rmdir!: (path: PathLike,) => NoneType
# posix = pyimport "posix"
# .uname!: () => posix.UnameResult
.getrandom!: (size: Nat) => Bytes
.urandom!: (size: Nat) => Bytes

View file

@ -0,0 +1,9 @@
.dirname!: (path: PathLike,) => Str
.exists!: (path: PathLike,) => Bool
.lexists!: (path: PathLike,) => Bool
.expanduser!: (path: PathLike,) => Str
.expandvars!: (path: PathLike,) => Str
.isfile!: (path: PathLike,) => Bool
.isdir!: (path: PathLike,) => Bool
.islink!: (path: PathLike,) => Bool
.ismount!: (path: PathLike,) => Bool

View file

@ -0,0 +1,7 @@
.PurePath: ClassType
.PurePath.parts: [Str; _]
.PurePosixPath: ClassType
.PureWindowsPath: ClassType
.Path: ClassType
.PosixPath: ClassType
.WindowsPath: ClassType

View file

@ -0,0 +1,13 @@
.architecture!: () => Str
.machine!: () => Str
.node!: () => Str
.platform!: () => Str
.processor!: () => Str
.python_build!: () => Str
.python_compiler!: () => Str
.python_branch!: () => Str
.python_implementation!: () => Str
.python_revision!: () => Str
.python_version!: () => Str
.python_version_tuple!: () => (Str, Str, Str)
.uname!: () => {.system = Str; .node = Str; .release = Str; .version = Str; .machine = Str}

View file

@ -0,0 +1 @@
.UnameResult: ClassType

View file

View file

@ -0,0 +1,14 @@
.seed!: (a := Num, version := Int) => NoneType
.randbytes!: (n: Nat) => Bytes
.randrange!: (start: Int, stop := Int, step := Int) => Int
.randint!: (a: Int, b: Int) => Int
.getrandbits!: (k: Nat) => Nat
.choice!: |T: Type, S <: Seq(T)|(seq: S) => T
# TODO: dependent length array type
.choices!: |T: Type, S <: Seq(T)|(population: S, weights := [Nat; _] or NoneType, k := Nat) => [T; _]
# TODO: Seq!
.shuffle!: |T: Type, S <: Seq(T)|(seq: S) => NoneType
.sample!: |T: Type, S <: Seq(T)|(population: S, k := Nat) => [T; _]
.random!: () => 0.0..1.0 # TODO: <1.0
.dep_uniform! = 'uniform': |A: Int, B: Int|(a: {A}, b: {B}) => A..B
.uniform!: (a: Int, b: Int) => Int

View file

@ -0,0 +1,48 @@
.RegexFlag: ClassType
.A: .RegexFlag
.ASCII: .RegexFlag
.DEBUG: .RegexFlag
.I: .RegexFlag
.IGNORECASE: .RegexFlag
.L: .RegexFlag
.LOCALE: .RegexFlag
.M: .RegexFlag
.MULTILINE: .RegexFlag
.NOFLAG: .RegexFlag
.S: .RegexFlag
.DOTALL: .RegexFlag
.X: .RegexFlag
.VERBOSE: .RegexFlag
.Match: ClassType
.Match.expand: (self: .Match, template: Str) -> Str
# TODO: tuple
.Match.group: (self: .Match, x := Int or Str) -> Str
.Match.__getitem__: (self: .Match, x := Int or Str) -> Str
.Pattern: ClassType
.Pattern.search: (self: .Pattern, string: Str) -> .Match or NoneType
.Pattern.match: (self: .Pattern, string: Str) -> .Match or NoneType
.Pattern.fullmatch: (self: .Pattern, string: Str) -> .Match or NoneType
.Pattern.split: (self: .Pattern, string: Str, maxspilit := Nat) -> [Str; _]
.Pattern.findall: (self: .Pattern, string: Str) -> [Str; _]
# TODO: iterator
.Pattern.finditer: (self: .Pattern, string: Str) -> [.Match; _]
.Pattern.sub: (self: .Pattern, repl: Str, string: Str, count := Nat) -> Str
.Pattern.subn: (self: .Pattern, repl: Str, string: Str, count := Nat) -> (Str, Nat)
.Pattern.flags: Nat
.Pattern.groups: Nat
.Pattern.pattern: Str
.compile: (pattern: Str, flags := Nat or .RegexFlag) -> .Pattern
.search: (pattern: Str, string: Str, flags := Nat or .RegexFlag) -> .Match or NoneType
.match: (pattern: Str, string: Str, flags := Nat or .RegexFlag) -> .Match or NoneType
.fullmatch: (pattern: Str, string: Str, flags := Nat or .RegexFlag) -> .Match or NoneType
.split: (pattern: Str, string: Str, maxspilit := Nat, flags := Nat or .RegexFlag) -> [Str; _]
.findall: (pattern: Str, string: Str, flags := Nat or .RegexFlag) -> [Str; _]
# TODO: iterator
.finditer: (pattern: Str, string: Str, flags := Nat or .RegexFlag) -> [.Match; _]
.sub: (pattern: Str, repl: Str, string: Str, count := Nat, flags := Nat or .RegexFlag) -> Str
.subn: (pattern: Str, repl: Str, string: Str, count := Nat, flags := Nat or .RegexFlag) -> (Str, Nat)
.escape: (pattern: Str) -> Str
.purge!: () => ()

View file

@ -0,0 +1,6 @@
.copyfile!: (src: PathLike, dst: PathLike,) => NoneType
.copy!: (src: PathLike, dst: PathLike,) => NoneType
.copytree!: (src: PathLike, dst: PathLike,) => NoneType
.rmtree!: (path: PathLike,) => NoneType
.move!: (src: PathLike, dst: PathLike) => NoneType
.which!: (cmd: Str,) => Str or NoneType

View file

@ -0,0 +1,2 @@
.Socket!: ClassType
.Socket!.new: (family := Int, type := Int, proto := Int, fileno: Int or NoneType := NoneType) -> .Socket!

View file

@ -0,0 +1,17 @@
.TCPServer: ClassType
.UDPServer: ClassType
.UnixStreamServer: ClassType
.UnixDatagramServer: ClassType
.ForkingMixIn: ClassType
.ThreadingMixIn: ClassType
.ForkingTCPServer: ClassType
.ForkingUDPServer: ClassType
.ThreadingTCPServer: ClassType
.ThreadingUDPServer: ClassType
.BaseServer: ClassType
.BaseRequestHandler: ClassType
.StreamRequestHandler: ClassType
.DatagramRequestHandler: ClassType

View file

@ -0,0 +1,12 @@
.SSLContext: ClassType
.create_default_context!: () => .SSLContext
.SSLError: ClassType
.SSLSocket: ClassType
.SSLSocket <: FileLike!
.rand_bytes! = 'RAND_bytes': (num: Nat,) => Bytes
.rand_pseudo_bytes! = 'RAND_pseudo_bytes': (num: Nat,) => Bytes
.rand_status! = 'RAND_status': () => Bool
.rand_add! = 'RAND_add': (bytes: Bytes, entropy: Float,) => NoneType

View file

@ -0,0 +1,10 @@
.ascii_letters: Str
.ascii_lowercase: Str
.ascii_uppercase: Str
.digits: Str
.hexdigits: Str
.octdigits: Str
.punctuation: Str
.printable: Str
.whitespace: Str
.Formatter: ClassType

View file

@ -0,0 +1,14 @@
.CompletedProcess: ClassType
.CompletedProcess.args: Str or [Str; _]
.CompletedProcess.returncode: Int
.CompletedProcess.stdout: Bytes or NoneType
.CompletedProcess.stderr: Bytes or NoneType
.run!: (
args: Str or [Str; _],
stdin: File! or NoneType := NoneType,
stdout: File! or NoneType := NoneType,
stderr: File! or NoneType := NoneType,
capture_output := Bool,
shell := Bool,
) => .CompletedProcess

View file

@ -0,0 +1,20 @@
io = pyimport "io"
.argv: [Str; _]
.byteorder: Str
.builtin_module_names: [Str; _]
.copyright: Str
.executable: Str
.path: Array!(Str, _)
.platform: Str
.prefix: Str
.ps1: Str!
.ps2: Str!
.stderr: io.TextIOWrapper!
.stdin: io.TextIOWrapper!
.stdout: io.TextIOWrapper!
.version: Str
.exit: Int -> Never
.getdefaultencoding!: () => Str
.setrecursionlimit!: Int => NoneType

View file

@ -0,0 +1,11 @@
.TarFile!: ClassType
.TarFile! <: FileLike!
.TarFile!.open!: (path: PathLike or NoneType := NoneType, mode := Str) => .TarFile!
.TarFile!.add!: (self: RefMut(.TarFile!), name: PathLike, arcname: PathLike or NoneType := NoneType, recursive := Bool) => NoneType
.TarFile!.close!: (self: .TarFile!,) => NoneType
.TarFile!.extractall!: (self: RefMut(.TarFile!), path := PathLike, members: [Str; _] or NoneType := NoneType, numeric_owner := Bool) => NoneType
.TarFile!.getnames: (self: Ref(.TarFile!),) -> [Str; _]
.TarFile!.list: (self: Ref(.TarFile!), verbose := Bool) -> [Str; _]
.open!: (path: PathLike or NoneType := NoneType, mode := Str, fileobj: FileLike! or NoneType := NoneType) => .TarFile!
.is_tarfile: (name: Str or File!,) -> Bool

View file

@ -0,0 +1,23 @@
.Thread!: ClassType
.Thread!.name: Str
.Thread!.daemon: Bool
.Thread!.ident: Nat or NoneType # TODO: Pos or NoneType
.Thread!.native_id: Nat or NoneType
.Thread!.start!: (self: .Thread!) => NoneType
.Thread!.run!: (self: .Thread!) => NoneType
.Thread!.join!: (self: .Thread!, timeout := Nat or NoneType) => NoneType
.Thread!.is_alive: (self: .Thread!) -> Bool
.Local! = 'local': ClassType
.Lock!: ClassType
.RLock!: ClassType
.Condition!: ClassType
.Semaphore!: ClassType
.BoundedSemaphore!: ClassType
.Event!: ClassType
.Timer!: ClassType
.Barrier!: ClassType
.BrokenBarrierError: ClassType
.active_count!: () => Nat

View file

@ -0,0 +1,15 @@
.sleep!: Float => NoneType
.time!: () => Float
.StructTime = 'struct_time': ClassType
.StructTime.tm_year: Nat
.StructTime.tm_mon: Nat
.StructTime.tm_mday: Nat
.StructTime.tm_hour: Nat
.StructTime.tm_min: Nat
.StructTime.tm_sec: Nat
.StructTime.tm_wday: Nat
.StructTime.tm_yday: Nat
.StructTime.tm_isdst: Nat
.StructTime.tm_zone: Str
.StructTime.tm_gmtoff: Nat

View file

@ -0,0 +1 @@
.timeit: (stmt: Str or Proc, setup := Str or Proc, number := Nat) -> Float

View file

@ -0,0 +1,5 @@
inspect = pyimport "inspect"
.print_tb!: (tb: inspect.Traceback, limit := Nat or NoneType, file := File! or NoneType) => NoneType
.TracebackExeption: ClassType

View file

@ -0,0 +1,24 @@
.NoneType: ClassType
.FunctionType: ClassType
.LambdaType: ClassType
.GeneratorType: ClassType
.CoroutineType: ClassType
.AsyncGeneratorType: ClassType
.CodeType: ClassType
.CellType: ClassType
.MethodType: ClassType
.BuiltinFunctionType: ClassType
.BuiltinMethodType: ClassType
.WrapperDescriptorType: ClassType
.MethodWrapperType: ClassType
.NotImplementedType: ClassType
.MethodDescriptorType: ClassType
.ClassMethodDescriptorType: ClassType
.ModuleType: ClassType
.EllipsisType: ClassType
.GenericAlias: (Type, GenericTuple) -> ClassType # TODO: Tuple Type
.UnionType: (Type, Type) -> Type
.TracebackType: ClassType
.FrameType: ClassType
.MappingProxyType: ClassType

View file

@ -0,0 +1,44 @@
.Any: Type
.LiteralString: Type
.Never: Type
.NoReturn: Type
.Self: Type
.TypeAlias: Type
.Tuple: (...Type) -> Type
.Union: (...Type) -> Type
.Optional: (Type) -> Type
.Callable: (...Type) -> Type
.Concatenate: (...Type) -> Type
.Type: (Type) -> Type
.Literal: (...Obj) -> Type
.ClassVar: (Type) -> Type
.Final: (Type) -> Type
.Required: (Type) -> Type
.NotRequired: (Type) -> Type
.Annotated: (Type, ...Obj) -> Type
.TypeGuard: (Type) -> Type
.Generic: (Type) -> Type
.TypeVar: (Str) -> Type
.TypeVarTuple: (Str) -> Type
.Unpack: (Type) -> Type
.ParamSpec: (Str) -> Type
.AnyStr: Type
.Protocol: (Type := NoneType) -> Type
.NamedTuple: Type
.NewType: (Str, Type) -> Type
.TypedDict: (Str, Type) -> Type
.Dict: (Type, Type) -> Type
.List: (Type) -> Type
.Set: (Type) -> Type
.FrozenSet: (Type) -> Type
.OrderedDict: (Type, Type) -> Type
.ChainMap: (Type, Type) -> Type
.Counter: (Type, Int) -> Type
.Deque: (Type) -> Type
.IO: Type
.TextIO: Type
.BinaryIO: Type
.Pattern: Type
.Match: Type
.Text: Type
.Sequence: (Type) -> Type

View file

@ -0,0 +1,2 @@
.parse = pyimport "parse"
.request = pyimport "request"

View file

@ -0,0 +1,5 @@
http = pyimport "http"
.Request: ClassType
.Request.data: Bytes
.urlopen!: (url: Str or .Request, data: Bytes or NoneType := NoneType, timeout: Nat or NoneType := NoneType) -> http.client.HTTPResponse

View file

@ -0,0 +1,11 @@
.ZipFile!: ClassType
.ZipFile! <: FileLike!
.open! = 'ZipFile': (path: PathLike or FileLike!, mode := Str) => .ZipFile!
.ZipFile!.open!: (name: PathLike, mode := Str) => .ZipFile!
.ZipFile!.add!: (self: RefMut(.ZipFile!), name: PathLike, arcname: PathLike or NoneType := NoneType, recursive := Bool) => NoneType
.ZipFile!.close!: (self: .ZipFile!,) => NoneType
.ZipFile!.extractall!: (self: RefMut(.ZipFile!), path := PathLike, members: [Str; _] or NoneType := NoneType, numeric_owner := Bool) => NoneType
.ZipFile!.namelist: (self: Ref(.ZipFile!),) -> [Str; _]
.is_zipfile: (name: Str or File!,) -> Bool

View file

@ -0,0 +1,8 @@
class Array(list):
def dedup(self):
return Array(list(set(self)))
def dedup_by(self, f):
return Array(list(set(map(f, self))))
def push(self, value):
self.append(value)
return self

View file

@ -0,0 +1,17 @@
from _erg_nat import Nat
from _erg_result import Error
class Bool(Nat):
def try_new(b: bool): # -> Result[Nat]
if b == True or b == False:
return Bool(b)
else:
return Error("Bool can't be other than True or False")
def __str__(self) -> str:
if self:
return "True"
else:
return "False"
def __repr__(self) -> str:
return self.__str__()

View file

@ -0,0 +1,21 @@
def if__(cond, then, else_=lambda: None):
if cond:
return then()
else:
return else_()
def for__(iterable, body):
for i in iterable:
body(i)
def while__(cond_block, body):
while cond_block():
body()
def with__(obj, body):
obj.__enter__()
body(e)
obj.__exit__()
def discard__(obj):
pass

View file

@ -0,0 +1,14 @@
from _erg_int import Int
from _erg_nat import Nat
def int__(i):
try:
return Int(i)
except:
return None
def nat__(i):
try:
return Nat(i)
except:
return None

View file

@ -0,0 +1,24 @@
from _erg_result import is_ok
from _erg_range import Range
def in_operator(x, y):
if type(y) == type:
if isinstance(x, y):
return True
elif is_ok(y.try_new(x)):
return True
# TODO: trait check
return False
elif (issubclass(type(y), list) or issubclass(type(y), set)) \
and (type(y[0]) == type or issubclass(type(y[0]), Range)):
# FIXME:
type_check = in_operator(x[0], y[0])
len_check = len(x) == len(y)
return type_check and len_check
elif issubclass(type(y), dict) and issubclass(type(next(iter(y.keys()))), type):
# TODO:
type_check = True # in_operator(x[next(iter(x.keys()))], next(iter(y.keys())))
len_check = len(x) >= len(y)
return type_check and len_check
else:
return x in y

View file

@ -0,0 +1,85 @@
from _erg_result import Error
class Int(int):
def try_new(i): # -> Result[Nat]
if isinstance(i, int):
Int(i)
else:
Error("not an integer")
def succ(self):
return Int(self + 1)
def pred(self):
return Int(self - 1)
def mutate(self):
return IntMut(self)
class IntMut(): # inherits Int
value: Int
def __init__(self, i):
self.value = Int(i)
def __repr__(self):
return self.value.__repr__()
def __eq__(self, other):
if isinstance(other, Int):
return self.value == other
else:
return self.value == other.value
def __ne__(self, other):
if isinstance(other, Int):
return self.value != other
else:
return self.value != other.value
def __le__(self, other):
if isinstance(other, Int):
return self.value <= other
else:
return self.value <= other.value
def __ge__(self, other):
if isinstance(other, Int):
return self.value >= other
else:
return self.value >= other.value
def __lt__(self, other):
if isinstance(other, Int):
return self.value < other
else:
return self.value < other.value
def __gt__(self, other):
if isinstance(other, Int):
return self.value > other
else:
return self.value > other.value
def __add__(self, other):
if isinstance(other, Int):
return IntMut(self.value + other)
else:
return IntMut(self.value + other.value)
def __sub__(self, other):
if isinstance(other, Int):
return IntMut(self.value - other)
else:
return IntMut(self.value - other.value)
def __mul__(self, other):
if isinstance(other, Int):
return IntMut(self.value * other)
else:
return IntMut(self.value * other.value)
def __floordiv__(self, other):
if isinstance(other, Int):
return IntMut(self.value // other)
else:
return IntMut(self.value // other.value)
def __pow__(self, other):
if isinstance(other, Int):
return IntMut(self.value ** other)
else:
return IntMut(self.value ** other.value)
def inc(self, i=1):
self.value = Int(self.value + i)
def dec(self, i=1):
self.value = Int(self.value - i)
def succ(self):
return self.value.succ()
def pred(self):
return self.value.pred()

View file

@ -0,0 +1,5 @@
def mutate_operator(x):
if hasattr(x, 'mutate'):
return x.mutate()
else:
return x

View file

@ -0,0 +1,94 @@
from _erg_result import Error
from _erg_int import Int
from _erg_int import IntMut
class Nat(Int):
def try_new(i): # -> Result[Nat]
if i >= 0:
return Nat(i)
else:
return Error("Nat can't be negative")
def times(self, f):
for _ in range(self):
f()
def saturating_sub(self, other):
if self > other:
return self - other
else:
return 0
def mutate(self):
return NatMut(self)
class NatMut(IntMut): # and Nat
value: Nat
def __init__(self, n: Nat):
self.value = n
def __repr__(self):
return self.value.__repr__()
def __eq__(self, other):
if isinstance(other, int):
return self.value == other
else:
return self.value == other.value
def __ne__(self, other):
if isinstance(other, int):
return self.value != other
else:
return self.value != other.value
def __le__(self, other):
if isinstance(other, int):
return self.value <= other
else:
return self.value <= other.value
def __ge__(self, other):
if isinstance(other, int):
return self.value >= other
else:
return self.value >= other.value
def __lt__(self, other):
if isinstance(other, int):
return self.value < other
else:
return self.value < other.value
def __gt__(self, other):
if isinstance(other, int):
return self.value > other
else:
return self.value > other.value
def __add__(self, other):
if isinstance(other, Nat):
return NatMut(self.value + other)
else:
return NatMut(self.value + other.value)
def __radd__(self, other):
if isinstance(other, Nat):
return Nat(other + self.value)
else:
return Nat(other.value + self.value)
def __mul__(self, other):
if isinstance(other, Nat):
return NatMut(self.value * other)
else:
return NatMut(self.value * other.value)
def __rmul__(self, other):
if isinstance(other, Nat):
return Nat(other * self.value)
else:
return Nat(other.value * self.value)
def __pow__(self, other):
if isinstance(other, Nat):
return NatMut(self.value ** other)
else:
return NatMut(self.value ** other.value)
def try_new(i): # -> Result[Nat]
if i >= 0:
return NatMut(i)
else:
return Error("Nat can't be negative")
def times(self, f):
for _ in range(self.value):
f()

View file

@ -0,0 +1,96 @@
from _erg_nat import Nat
from _erg_str import Str
# from collections.abc import Iterable, Sequence, Iterator, Container
class Range:
def __init__(self, start, end):
self.start = start
self.end = end
def __contains__(self, item):
pass
def __getitem__(self, item):
res = self.start + item
if res in self:
return res
else:
raise IndexError("Index out of range")
# TODO: for Str, etc.
def __len__(self):
if self.start in self:
if self.end in self:
# len(1..4) == 4
return self.end - self.start + 1
else:
# len(1..<4) == 3
return self.end - self.start
else:
if self.end in self:
# len(1<..4) == 3
return self.end - self.start
else:
# len(1<..<4) == 2
return self.end - self.start - 2
def __iter__(self):
return RangeIterator(rng=self)
# Sequence.register(Range)
# Container.register(Range)
# Iterable.register(Range)
# represents `start<..end`
class LeftOpenRange(Range):
def __contains__(self, item):
return self.start < item <= self.end
# represents `start..<end`
class RightOpenRange(Range):
def __contains__(self, item):
return self.start <= item < self.end
# represents `start<..<end`
class OpenRange(Range):
def __contains__(self, item):
return self.start < item < self.end
# represents `start..end`
class ClosedRange(Range):
def __contains__(self, item):
return self.start <= item <= self.end
class RangeIterator:
def __init__(self, rng):
self.rng = rng
self.needle = self.rng.start
if issubclass(Nat, type(self.rng.start)):
if not(self.needle in self.rng):
self.needle += 1
elif issubclass(Str, type(self.rng.start)):
if not(self.needle in self.rng):
self.needle = chr(ord(self.needle) + 1)
else:
if not(self.needle in self.rng):
self.needle = self.needle.succ()
def __iter__(self):
return self
def __next__(self):
if issubclass(Nat, type(self.rng.start)):
if self.needle in self.rng:
result = self.needle
self.needle += 1
return result
elif issubclass(Str, type(self.rng.start)):
if self.needle in self.rng:
result = self.needle
self.needle = chr(ord(self.needle) + 1)
return result
else:
if self.needle in self.rng:
result = self.needle
self.needle = self.needle.succ()
return result
raise StopIteration
# Iterator.register(RangeIterator)

View file

@ -0,0 +1,18 @@
# from typing import TypeVar, Union, _SpecialForm, _type_check
class Error:
def __init__(self, message):
self.message = message
# T = TypeVar("T")
# @_SpecialForm
def Result(self, parameters):
"""Result type.
Result[T] is equivalent to Union[T, Error].
"""
# arg = _type_check(parameters, f"{self} requires a single type.")
return [arg, Error]
def is_ok(obj) -> bool:
return not isinstance(obj, Error)

Some files were not shown because too many files have changed in this diff Show more