mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-01 13:11:11 +00:00
Implement Patch
This commit is contained in:
parent
7078f95cec
commit
bade70ef91
22 changed files with 741 additions and 116 deletions
|
@ -40,7 +40,7 @@ use crate::context::eval::type_from_token_kind;
|
|||
use crate::error::CompileError;
|
||||
use crate::hir::{
|
||||
Accessor, Args, Array, AttrDef, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier,
|
||||
Lambda, Literal, Params, PosArg, Record, Signature, SubrSignature, Tuple, UnaryOp,
|
||||
Lambda, Literal, Params, PatchDef, PosArg, Record, Signature, SubrSignature, Tuple, UnaryOp,
|
||||
VarSignature, HIR,
|
||||
};
|
||||
use crate::ty::value::ValueObj;
|
||||
|
@ -48,11 +48,16 @@ use crate::ty::{HasType, Type, TypeCode, TypePair};
|
|||
use erg_common::fresh::fresh_varname;
|
||||
use AccessKind::*;
|
||||
|
||||
fn fake_method_to_func(class: &str, name: &str) -> Option<&'static str> {
|
||||
match (class, name) {
|
||||
(_, "abs") => Some("abs"),
|
||||
(_, "iter") => Some("iter"),
|
||||
(_, "map") => Some("map"),
|
||||
/// patch method -> function
|
||||
/// patch attr -> variable
|
||||
fn debind(name: Option<&str>) -> Option<Str> {
|
||||
match name {
|
||||
Some(name) if name.starts_with("Function::") => {
|
||||
Some(Str::from(name.replace("Function::", "")))
|
||||
}
|
||||
Some(patch_method) if patch_method.contains("::") || patch_method.contains('.') => {
|
||||
Some(Str::rc(patch_method))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -873,11 +878,17 @@ impl PyCodeGenerator {
|
|||
if is_record {
|
||||
a.ident.dot = Some(DOT);
|
||||
}
|
||||
if let Some(varname) = debind(a.ident.vi.py_name.as_ref().map(|s| &s[..])) {
|
||||
a.ident.dot = None;
|
||||
a.ident.name = VarName::from_str(varname);
|
||||
self.emit_load_name_instr(a.ident);
|
||||
} else {
|
||||
self.emit_expr(*a.obj);
|
||||
self.emit_load_attr_instr(a.ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_def(&mut self, def: Def) {
|
||||
log!(info "entered {} ({})", fn_name!(), def.sig);
|
||||
|
@ -1135,6 +1146,27 @@ impl PyCodeGenerator {
|
|||
self.stack_dec();
|
||||
}
|
||||
|
||||
fn emit_patch_def(&mut self, patch_def: PatchDef) {
|
||||
log!(info "entered {} ({})", fn_name!(), patch_def.sig);
|
||||
for def in patch_def.methods {
|
||||
// Invert.
|
||||
// invert self = ...
|
||||
// ↓
|
||||
// def Invert::invert(self): ...
|
||||
let Expr::Def(mut def) = def else { todo!() };
|
||||
let namespace = self.cur_block_codeobj().name.trim_start_matches("::");
|
||||
let name = format!(
|
||||
"{}{}{}",
|
||||
namespace,
|
||||
patch_def.sig.ident().to_string_without_type(),
|
||||
def.sig.ident().to_string_without_type()
|
||||
);
|
||||
def.sig.ident_mut().name = VarName::from_str(Str::from(name));
|
||||
def.sig.ident_mut().dot = None;
|
||||
self.emit_def(def);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: use `TypeVar`, `Generic` in `typing` module
|
||||
// fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {}
|
||||
|
||||
|
@ -1986,14 +2018,13 @@ impl PyCodeGenerator {
|
|||
|
||||
fn emit_call_method(&mut self, obj: Expr, method_name: Identifier, args: Args) {
|
||||
log!(info "entered {}", fn_name!());
|
||||
let class = obj.ref_t().qual_name(); // これは必ずmethodのあるクラスになっている
|
||||
if &method_name.inspect()[..] == "update!" {
|
||||
if self.py_version.minor >= Some(11) {
|
||||
return self.emit_call_update_311(obj, args);
|
||||
} else {
|
||||
return self.emit_call_update_310(obj, args);
|
||||
}
|
||||
} else if let Some(func_name) = fake_method_to_func(&class, method_name.inspect()) {
|
||||
} else if let Some(func_name) = debind(method_name.vi.py_name.as_ref().map(|s| &s[..])) {
|
||||
return self.emit_call_fake_method(obj, func_name, method_name, args);
|
||||
}
|
||||
let is_py_api = obj.is_py_api();
|
||||
|
@ -2114,13 +2145,13 @@ impl PyCodeGenerator {
|
|||
fn emit_call_fake_method(
|
||||
&mut self,
|
||||
obj: Expr,
|
||||
func_name: &'static str,
|
||||
func_name: Str,
|
||||
mut method_name: Identifier,
|
||||
mut args: Args,
|
||||
) {
|
||||
log!(info "entered {}", fn_name!());
|
||||
method_name.dot = None;
|
||||
method_name.vi.py_name = Some(Str::ever(func_name));
|
||||
method_name.vi.py_name = Some(func_name);
|
||||
self.emit_push_null();
|
||||
self.emit_load_name_instr(method_name);
|
||||
args.insert_pos(0, PosArg::new(obj));
|
||||
|
@ -2314,6 +2345,7 @@ impl PyCodeGenerator {
|
|||
Expr::Accessor(acc) => self.emit_acc(acc),
|
||||
Expr::Def(def) => self.emit_def(def),
|
||||
Expr::ClassDef(class) => self.emit_class_def(class),
|
||||
Expr::PatchDef(patch) => self.emit_patch_def(patch),
|
||||
Expr::AttrDef(attr) => self.emit_attr_def(attr),
|
||||
Expr::Lambda(lambda) => self.emit_lambda(lambda),
|
||||
Expr::UnaryOp(unary) => self.emit_unaryop(unary),
|
||||
|
|
|
@ -245,8 +245,20 @@ impl Context {
|
|||
self.nominal_supertype_of(rhs, lhs)
|
||||
}
|
||||
|
||||
fn _find_compatible_patch(&self, sup: &Type, sub: &Type) -> Option<&Context> {
|
||||
for patch in self._all_patches().into_iter() {
|
||||
pub(crate) fn find_patches_of<'a>(
|
||||
&'a self,
|
||||
typ: &'a Type,
|
||||
) -> impl Iterator<Item = &'a Context> {
|
||||
self.all_patches().into_iter().filter(|ctx| {
|
||||
if let ContextKind::Patch(base) = &ctx.kind {
|
||||
return self.supertype_of(base, typ);
|
||||
}
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
fn _find_compatible_glue_patch(&self, sup: &Type, sub: &Type) -> Option<&Context> {
|
||||
for patch in self.all_patches().into_iter() {
|
||||
if let ContextKind::GluePatch(tr_inst) = &patch.kind {
|
||||
if self.subtype_of(sub, &tr_inst.sub_type)
|
||||
&& self.subtype_of(&tr_inst.sup_trait, sup)
|
||||
|
|
|
@ -246,7 +246,12 @@ impl Context {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
call.loc(),
|
||||
self.caused_by(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,13 +316,16 @@ impl Context {
|
|||
let elem = self.eval_const_expr(&elem.expr)?;
|
||||
elems.push(elem);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(ValueObj::Array(RcArray::from(elems)))
|
||||
}
|
||||
_ => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
arr.loc(),
|
||||
self.caused_by(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_const_set(&self, set: &AstSet) -> EvalResult<ValueObj> {
|
||||
let mut elems = vec![];
|
||||
|
@ -327,13 +335,16 @@ impl Context {
|
|||
let elem = self.eval_const_expr(&elem.expr)?;
|
||||
elems.push(elem);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(ValueObj::Set(Set::from(elems)))
|
||||
}
|
||||
_ => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
set.loc(),
|
||||
self.caused_by(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_const_dict(&self, dict: &AstDict) -> EvalResult<ValueObj> {
|
||||
let mut elems = dict! {};
|
||||
|
@ -344,13 +355,16 @@ impl Context {
|
|||
let value = self.eval_const_expr(&elem.value)?;
|
||||
elems.insert(key, value);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Ok(ValueObj::Dict(elems))
|
||||
}
|
||||
_ => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
dict.loc(),
|
||||
self.caused_by(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_const_tuple(&self, tuple: &Tuple) -> EvalResult<ValueObj> {
|
||||
let mut elems = vec![];
|
||||
|
|
|
@ -143,6 +143,35 @@ pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueOb
|
|||
Ok(ValueObj::gen_t(GenTypeObj::trait_(t, require, 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!("{REQ_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 {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::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(|| {
|
||||
|
|
|
@ -491,7 +491,7 @@ impl Context {
|
|||
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_decl("iter", t, Public);
|
||||
iterable.register_builtin_py_decl("iter", t, Public, Some("__iter__"));
|
||||
iterable.register_builtin_decl("Iter", Type, Public);
|
||||
let R = mono_q("R", instanceof(Type));
|
||||
let params = vec![PS::t("R", WithDefault)];
|
||||
|
@ -772,7 +772,7 @@ impl Context {
|
|||
int.register_marker_trait(mono("Num"));
|
||||
// class("Rational"),
|
||||
// class("Integral"),
|
||||
int.register_builtin_impl("abs", fn0_met(Int, Nat), Immutable, Public);
|
||||
int.register_builtin_py_impl("abs", fn0_met(Int, Nat), Immutable, Public, Some("__abs__"));
|
||||
let mut int_ord = Self::builtin_methods(Some(mono("Ord")), 2);
|
||||
int_ord.register_builtin_impl(
|
||||
"__partial_cmp__",
|
||||
|
@ -927,6 +927,20 @@ impl Context {
|
|||
Immutable,
|
||||
Public,
|
||||
);
|
||||
str_.register_builtin_impl(
|
||||
"lower",
|
||||
fn_met(Str, vec![], None, vec![], Str),
|
||||
Immutable,
|
||||
Public,
|
||||
);
|
||||
str_.register_builtin_impl(
|
||||
"upper",
|
||||
fn_met(Str, vec![], None, vec![], Str),
|
||||
Immutable,
|
||||
Public,
|
||||
);
|
||||
let str_getitem_t = fn1_kw_met(Str, kw("idx", Nat), Str);
|
||||
str_.register_builtin_impl("__getitem__", str_getitem_t, Immutable, Public);
|
||||
let mut str_eq = Self::builtin_methods(Some(mono("Eq")), 2);
|
||||
str_eq.register_builtin_impl("__eq__", fn1_met(Str, Str, Bool), Const, Public);
|
||||
str_.register_trait(Str, str_eq);
|
||||
|
@ -949,11 +963,12 @@ impl Context {
|
|||
str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public);
|
||||
str_.register_trait(Str, str_show);
|
||||
let mut str_iterable = Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(Str)])), 2);
|
||||
str_iterable.register_builtin_impl(
|
||||
str_iterable.register_builtin_py_impl(
|
||||
"iter",
|
||||
fn0_met(Str, mono("StrIterator")),
|
||||
Immutable,
|
||||
Public,
|
||||
Some("__iter__"),
|
||||
);
|
||||
str_.register_trait(Str, str_iterable);
|
||||
/* NoneType */
|
||||
|
@ -1149,12 +1164,12 @@ impl Context {
|
|||
array_.register_trait(arr_t.clone(), array_show);
|
||||
let mut array_iterable =
|
||||
Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2);
|
||||
array_iterable.register_builtin_impl(
|
||||
"iter",
|
||||
fn0_met(Str, mono("ArrayIterator")),
|
||||
Immutable,
|
||||
Public,
|
||||
);
|
||||
let t = fn0_met(
|
||||
array_t(T.clone(), TyParam::erased(Nat)),
|
||||
poly("ArrayIterator", vec![ty_tp(T.clone())]),
|
||||
)
|
||||
.quantify();
|
||||
array_iterable.register_builtin_py_impl("iter", t, Immutable, Public, Some("__iter__"));
|
||||
array_.register_trait(arr_t.clone(), array_iterable);
|
||||
/* Set */
|
||||
let mut set_ =
|
||||
|
@ -1278,8 +1293,10 @@ impl Context {
|
|||
str_iterator.register_superclass(Obj, &obj);
|
||||
let mut array_iterator = Self::builtin_poly_class("ArrayIterator", vec![PS::t_nd("T")], 1);
|
||||
array_iterator.register_superclass(Obj, &obj);
|
||||
array_iterator.register_marker_trait(poly("Output", vec![ty_tp(T.clone())]));
|
||||
let mut range_iterator = Self::builtin_poly_class("RangeIterator", vec![PS::t_nd("T")], 1);
|
||||
range_iterator.register_superclass(Obj, &obj);
|
||||
range_iterator.register_marker_trait(poly("Output", vec![ty_tp(T.clone())]));
|
||||
let mut obj_mut = Self::builtin_mono_class("Obj!", 2);
|
||||
obj_mut.register_superclass(Obj, &obj);
|
||||
let mut obj_mut_mutable = Self::builtin_methods(Some(mono("Mutable")), 2);
|
||||
|
@ -1526,11 +1543,12 @@ impl Context {
|
|||
range.register_trait(range_t.clone(), range_eq);
|
||||
let mut range_iterable =
|
||||
Self::builtin_methods(Some(poly("Iterable", vec![ty_tp(T.clone())])), 2);
|
||||
range_iterable.register_builtin_impl(
|
||||
range_iterable.register_builtin_py_impl(
|
||||
"iter",
|
||||
fn0_met(Str, mono("RangeIterator")),
|
||||
Immutable,
|
||||
Public,
|
||||
Some("__iter__"),
|
||||
);
|
||||
range.register_trait(range_t.clone(), range_iterable);
|
||||
let range_getitem_t = fn1_kw_met(range_t.clone(), anon(T.clone()), T.clone()).quantify();
|
||||
|
@ -1897,6 +1915,14 @@ impl Context {
|
|||
// TODO: register Del function object
|
||||
let t_del = nd_func(vec![kw("obj", Obj)], None, NoneType);
|
||||
self.register_builtin_impl("Del", t_del, Immutable, Private);
|
||||
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", Private, ValueObj::Subr(patch));
|
||||
}
|
||||
|
||||
fn init_builtin_procs(&mut self) {
|
||||
|
|
|
@ -35,7 +35,7 @@ use crate::AccessKind;
|
|||
use RegistrationMode::*;
|
||||
use Visibility::*;
|
||||
|
||||
use super::MethodInfo;
|
||||
use super::{ContextKind, MethodInfo};
|
||||
|
||||
impl Context {
|
||||
pub(crate) fn validate_var_sig_t(
|
||||
|
@ -151,6 +151,7 @@ impl Context {
|
|||
) -> SingleTyCheckResult<&Context> {
|
||||
self.get_mod(ident.inspect())
|
||||
.or_else(|| self.rec_get_type(ident.inspect()).map(|(_, ctx)| ctx))
|
||||
.or_else(|| self.rec_get_patch(ident.inspect()))
|
||||
.ok_or_else(|| {
|
||||
TyCheckError::no_var_error(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -468,6 +469,26 @@ impl Context {
|
|||
return Err(e);
|
||||
}
|
||||
}
|
||||
for patch in self.find_patches_of(obj.ref_t()) {
|
||||
if let Some(vi) = patch
|
||||
.locals
|
||||
.get(ident.inspect())
|
||||
.or_else(|| patch.decls.get(ident.inspect()))
|
||||
{
|
||||
self.validate_visibility(ident, vi, input, namespace)?;
|
||||
return Ok(vi.clone());
|
||||
}
|
||||
for (_, methods_ctx) in patch.methods_list.iter() {
|
||||
if let Some(vi) = methods_ctx
|
||||
.locals
|
||||
.get(ident.inspect())
|
||||
.or_else(|| methods_ctx.decls.get(ident.inspect()))
|
||||
{
|
||||
self.validate_visibility(ident, vi, input, namespace)?;
|
||||
return Ok(vi.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: dependent type widening
|
||||
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||
parent.rec_get_attr_info(obj, ident, input, namespace)
|
||||
|
@ -607,7 +628,10 @@ impl Context {
|
|||
line!() as usize,
|
||||
))
|
||||
}
|
||||
other => todo!("{other}"),
|
||||
_other => Err(TyCheckError::dummy(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(TyCheckError::dummy(
|
||||
|
@ -701,7 +725,26 @@ impl Context {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
// TODO: patch
|
||||
for patch in self.find_patches_of(obj.ref_t()) {
|
||||
if let Some(vi) = patch
|
||||
.locals
|
||||
.get(attr_name.inspect())
|
||||
.or_else(|| patch.decls.get(attr_name.inspect()))
|
||||
{
|
||||
self.validate_visibility(attr_name, vi, input, namespace)?;
|
||||
return Ok(vi.clone());
|
||||
}
|
||||
for (_, methods_ctx) in patch.methods_list.iter() {
|
||||
if let Some(vi) = methods_ctx
|
||||
.locals
|
||||
.get(attr_name.inspect())
|
||||
.or_else(|| methods_ctx.decls.get(attr_name.inspect()))
|
||||
{
|
||||
self.validate_visibility(attr_name, vi, input, namespace)?;
|
||||
return Ok(vi.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(TyCheckError::no_attr_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -741,6 +784,7 @@ impl Context {
|
|||
&& &self.name[..] != namespace
|
||||
&& !namespace.contains(&self.name[..])
|
||||
{
|
||||
log!(err "{namespace}/{}", self.name);
|
||||
Err(TyCheckError::visibility_error(
|
||||
input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -1879,19 +1923,9 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn _rec_get_patch(&self, name: &VarName) -> Option<&Context> {
|
||||
if let Some(patch) = self.patches.get(name) {
|
||||
Some(patch)
|
||||
} else if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||
outer._rec_get_patch(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn _all_patches(&self) -> Vec<&Context> {
|
||||
pub(crate) fn all_patches(&self) -> Vec<&Context> {
|
||||
if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||
[outer._all_patches(), self.patches.values().collect()].concat()
|
||||
[outer.all_patches(), self.patches.values().collect()].concat()
|
||||
} else {
|
||||
self.patches.values().collect()
|
||||
}
|
||||
|
@ -1984,15 +2018,9 @@ impl Context {
|
|||
// TODO: poly type
|
||||
pub(crate) fn rec_get_self_t(&self) -> Option<Type> {
|
||||
if self.kind.is_method_def() || self.kind.is_type() {
|
||||
// let name = self.name.split(&[':', '.']).last().unwrap();
|
||||
/*if let Some((t, _)) = self.rec_get_type(name) {
|
||||
log!("{t}");
|
||||
Some(t.clone())
|
||||
} else {
|
||||
log!("none");
|
||||
None
|
||||
}*/
|
||||
Some(mono(self.name.clone()))
|
||||
} else if let ContextKind::PatchMethodDefs(t) = &self.kind {
|
||||
Some(t.clone())
|
||||
} else if let Some(outer) = self.get_outer() {
|
||||
outer.rec_get_self_t()
|
||||
} else {
|
||||
|
@ -2063,6 +2091,16 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rec_get_patch(&self, name: &str) -> Option<&Context> {
|
||||
if let Some(ctx) = self.patches.get(name) {
|
||||
Some(ctx)
|
||||
} else if let Some(outer) = self.get_outer().or_else(|| self.get_builtins()) {
|
||||
outer.rec_get_patch(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_method_type_by_name(&self, name: &Identifier) -> SingleTyCheckResult<&MethodInfo> {
|
||||
// TODO: min_by
|
||||
if let Some(candidates) = self.method_to_traits.get(name.inspect()) {
|
||||
|
|
|
@ -232,6 +232,7 @@ pub enum ContextKind {
|
|||
Proc,
|
||||
Class,
|
||||
MethodDefs(Option<Type>), // Type: trait implemented
|
||||
PatchMethodDefs(Type),
|
||||
Trait,
|
||||
StructuralTrait,
|
||||
Patch(Type),
|
||||
|
@ -270,6 +271,10 @@ impl ContextKind {
|
|||
pub fn is_trait(&self) -> bool {
|
||||
matches!(self, Self::Trait | Self::StructuralTrait)
|
||||
}
|
||||
|
||||
pub fn is_patch(&self) -> bool {
|
||||
matches!(self, Self::Patch(_) | Self::GluePatch(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// 記号表に登録されているモードを表す
|
||||
|
@ -682,6 +687,31 @@ impl Context {
|
|||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn poly_patch<S: Into<Str>>(
|
||||
name: S,
|
||||
base: Type,
|
||||
params: Vec<ParamSpec>,
|
||||
cfg: ErgConfig,
|
||||
mod_cache: Option<SharedModuleCache>,
|
||||
py_mod_cache: Option<SharedModuleCache>,
|
||||
capacity: usize,
|
||||
level: usize,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
Self::poly(
|
||||
name,
|
||||
cfg,
|
||||
ContextKind::Patch(base),
|
||||
params,
|
||||
None,
|
||||
mod_cache,
|
||||
py_mod_cache,
|
||||
capacity,
|
||||
level,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mono_trait<S: Into<Str>>(
|
||||
name: S,
|
||||
|
@ -730,6 +760,28 @@ impl Context {
|
|||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mono_patch<S: Into<Str>>(
|
||||
name: S,
|
||||
base: Type,
|
||||
cfg: ErgConfig,
|
||||
mod_cache: Option<SharedModuleCache>,
|
||||
py_mod_cache: Option<SharedModuleCache>,
|
||||
capacity: usize,
|
||||
level: usize,
|
||||
) -> Self {
|
||||
Self::poly_patch(
|
||||
name,
|
||||
base,
|
||||
vec![],
|
||||
cfg,
|
||||
mod_cache,
|
||||
py_mod_cache,
|
||||
capacity,
|
||||
level,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn methods(
|
||||
impl_trait: Option<Type>,
|
||||
|
|
|
@ -81,6 +81,11 @@ impl Context {
|
|||
let vis = ident.vis();
|
||||
let kind = id.map_or(VarKind::Declared, VarKind::Defined);
|
||||
let sig_t = self.instantiate_var_sig_t(sig.t_spec.as_ref(), opt_t, PreRegister)?;
|
||||
let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind {
|
||||
Some(Str::from(format!("::{}{}", self.name, ident)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(_decl) = self.decls.remove(&ident.name) {
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -90,10 +95,8 @@ impl Context {
|
|||
ident.name.inspect(),
|
||||
)))
|
||||
} else {
|
||||
self.future_defined_locals.insert(
|
||||
ident.name.clone(),
|
||||
VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), None),
|
||||
);
|
||||
let vi = VarInfo::new(sig_t, muty, vis, kind, None, self.impl_of(), py_name);
|
||||
self.future_defined_locals.insert(ident.name.clone(), vi);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +126,11 @@ impl Context {
|
|||
Ok(t) => (TyCheckErrors::empty(), t),
|
||||
Err((errs, t)) => (errs, t),
|
||||
};
|
||||
let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind {
|
||||
Some(Str::from(format!("::{}{}", self.name, sig.ident)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let vi = VarInfo::new(
|
||||
t,
|
||||
muty,
|
||||
|
@ -130,7 +138,7 @@ impl Context {
|
|||
kind,
|
||||
Some(comptime_decos),
|
||||
self.impl_of(),
|
||||
None,
|
||||
py_name,
|
||||
);
|
||||
if let Some(_decl) = self.decls.remove(name) {
|
||||
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
|
||||
|
@ -182,8 +190,15 @@ impl Context {
|
|||
}
|
||||
self.validate_var_sig_t(ident, sig.t_spec.as_ref(), body_t, Normal)?;
|
||||
let muty = Mutability::from(&ident.inspect()[..]);
|
||||
self.decls.remove(ident.inspect());
|
||||
self.future_defined_locals.remove(ident.inspect());
|
||||
let py_name = if let Some(vi) = self
|
||||
.decls
|
||||
.remove(ident.inspect())
|
||||
.or_else(|| self.future_defined_locals.remove(ident.inspect()))
|
||||
{
|
||||
vi.py_name
|
||||
} else {
|
||||
py_name
|
||||
};
|
||||
let vis = ident.vis();
|
||||
let vi = VarInfo::new(
|
||||
body_t.clone(),
|
||||
|
@ -456,7 +471,7 @@ impl Context {
|
|||
};
|
||||
sub_t.lift();
|
||||
let found_t = self.generalize_t(sub_t);
|
||||
if let Some(vi) = self.decls.remove(name) {
|
||||
let py_name = if let Some(vi) = self.decls.remove(name) {
|
||||
if !self.supertype_of(&vi.t, &found_t) {
|
||||
return Err(TyCheckErrors::from(TyCheckError::violate_decl_error(
|
||||
self.cfg.input.clone(),
|
||||
|
@ -468,7 +483,10 @@ impl Context {
|
|||
&found_t,
|
||||
)));
|
||||
}
|
||||
}
|
||||
vi.py_name
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let comptime_decos = decorators
|
||||
.iter()
|
||||
.filter_map(|deco| match &deco.0 {
|
||||
|
@ -485,7 +503,7 @@ impl Context {
|
|||
VarKind::Defined(id),
|
||||
Some(comptime_decos),
|
||||
self.impl_of(),
|
||||
None,
|
||||
py_name,
|
||||
);
|
||||
let t = vi.t.clone();
|
||||
log!(info "Registered {}::{name}: {t}", self.name);
|
||||
|
@ -548,6 +566,11 @@ impl Context {
|
|||
total_errs.extend(errs.into_iter());
|
||||
}
|
||||
}
|
||||
ast::Expr::PatchDef(patch_def) => {
|
||||
if let Err(errs) = self.preregister_def(&patch_def.def) {
|
||||
total_errs.extend(errs.into_iter());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -950,6 +973,23 @@ impl Context {
|
|||
todo!("polymorphic type definition is not supported yet");
|
||||
}
|
||||
}
|
||||
GenTypeObj::Patch(_) => {
|
||||
if gen.typ().is_monomorphic() {
|
||||
let base = enum_unwrap!(gen.require_or_sup().unwrap(), TypeObj::Builtin);
|
||||
let ctx = Self::mono_patch(
|
||||
gen.typ().qual_name(),
|
||||
base.clone(),
|
||||
self.cfg.clone(),
|
||||
self.mod_cache.clone(),
|
||||
self.py_mod_cache.clone(),
|
||||
2,
|
||||
self.level,
|
||||
);
|
||||
self.register_gen_mono_patch(ident, gen, ctx, Const);
|
||||
} else {
|
||||
todo!("polymorphic patch definition is not supported yet");
|
||||
}
|
||||
}
|
||||
other => todo!("{other:?}"),
|
||||
}
|
||||
}
|
||||
|
@ -1046,6 +1086,72 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn register_gen_mono_patch(
|
||||
&mut self,
|
||||
ident: &Identifier,
|
||||
gen: GenTypeObj,
|
||||
ctx: Self,
|
||||
muty: Mutability,
|
||||
) {
|
||||
// FIXME: not panic but error
|
||||
// FIXME: recursive search
|
||||
if self.patches.contains_key(ident.inspect()) {
|
||||
panic!("{ident} has already been registered");
|
||||
} else if self.rec_get_const_obj(ident.inspect()).is_some() && ident.vis().is_private() {
|
||||
panic!("{ident} has already been registered as const");
|
||||
} else {
|
||||
let t = gen.typ().clone();
|
||||
let meta_t = gen.meta_type();
|
||||
let name = &ident.name;
|
||||
let id = DefId(get_hash(&(&self.name, &name)));
|
||||
self.decls.insert(
|
||||
name.clone(),
|
||||
VarInfo::new(
|
||||
meta_t,
|
||||
muty,
|
||||
ident.vis(),
|
||||
VarKind::Defined(id),
|
||||
None,
|
||||
self.impl_of(),
|
||||
None,
|
||||
),
|
||||
);
|
||||
self.consts
|
||||
.insert(name.clone(), ValueObj::Type(TypeObj::Generated(gen)));
|
||||
for impl_trait in ctx.super_traits.iter() {
|
||||
if let Some(impls) = self.trait_impls.get_mut(&impl_trait.qual_name()) {
|
||||
impls.insert(TypeRelationInstance::new(t.clone(), impl_trait.clone()));
|
||||
} else {
|
||||
self.trait_impls.insert(
|
||||
impl_trait.qual_name(),
|
||||
set![TypeRelationInstance::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.patches.insert(name.clone(), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn import_mod(
|
||||
&mut self,
|
||||
kind: OperationKind,
|
||||
|
|
|
@ -358,6 +358,7 @@ impl Context {
|
|||
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 {
|
||||
|
@ -772,6 +773,12 @@ impl Context {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
hir::Expr::PatchDef(patch_def) => {
|
||||
for def in patch_def.methods.iter_mut() {
|
||||
self.resolve_expr_t(def)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
hir::Expr::AttrDef(attr_def) => {
|
||||
// REVIEW: attr_def.attr is not dereferenced
|
||||
for chunk in attr_def.block.iter_mut() {
|
||||
|
|
|
@ -95,11 +95,19 @@ impl SideEffectChecker {
|
|||
self.check_def(def);
|
||||
}
|
||||
Expr::ClassDef(class_def) => {
|
||||
self.check_expr(class_def.require_or_sup.as_ref());
|
||||
// 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);
|
||||
|
@ -271,10 +279,17 @@ impl SideEffectChecker {
|
|||
self.check_def(def);
|
||||
}
|
||||
Expr::ClassDef(class_def) => {
|
||||
self.check_expr(class_def.require_or_sup.as_ref());
|
||||
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() {
|
||||
|
|
|
@ -442,8 +442,12 @@ impl Identifier {
|
|||
}
|
||||
|
||||
/// show dot + name (no qual_name & type)
|
||||
pub fn show(&self) -> String {
|
||||
format!("{}{}", fmt_option!(self.dot), self.name)
|
||||
pub fn to_string_without_type(&self) -> String {
|
||||
if self.dot.is_some() {
|
||||
format!(".{}", self.name)
|
||||
} else {
|
||||
format!("::{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_procedural(&self) -> bool {
|
||||
|
@ -1410,7 +1414,7 @@ impl NestedDisplay for SubrSignature {
|
|||
write!(
|
||||
f,
|
||||
"{}{} (: {})",
|
||||
self.ident.show(),
|
||||
self.ident.to_string_without_type(),
|
||||
self.params,
|
||||
self.ident.t()
|
||||
)
|
||||
|
@ -1781,6 +1785,54 @@ impl ClassDef {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PatchDef {
|
||||
pub sig: Signature,
|
||||
pub base: Box<Expr>,
|
||||
pub methods: Block,
|
||||
}
|
||||
|
||||
impl NestedDisplay for PatchDef {
|
||||
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
|
||||
write!(f, "{} = Patch ", self.sig)?;
|
||||
self.base.fmt_nest(f, level)?;
|
||||
writeln!(f, ":")?;
|
||||
self.methods.fmt_nest(f, level + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl_display_from_nested!(PatchDef);
|
||||
impl_locational!(PatchDef, sig);
|
||||
|
||||
impl HasType for PatchDef {
|
||||
#[inline]
|
||||
fn ref_t(&self) -> &Type {
|
||||
Type::NONE
|
||||
}
|
||||
#[inline]
|
||||
fn ref_mut_t(&mut self) -> &mut Type {
|
||||
todo!()
|
||||
}
|
||||
#[inline]
|
||||
fn signature_t(&self) -> Option<&Type> {
|
||||
None
|
||||
}
|
||||
#[inline]
|
||||
fn signature_mut_t(&mut self) -> Option<&mut Type> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PatchDef {
|
||||
pub fn new(sig: Signature, base: Expr, methods: Block) -> Self {
|
||||
Self {
|
||||
sig,
|
||||
base: Box::new(base),
|
||||
methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AttrDef {
|
||||
pub attr: Accessor,
|
||||
|
@ -1881,6 +1933,7 @@ pub enum Expr {
|
|||
Lambda(Lambda),
|
||||
Def(Def),
|
||||
ClassDef(ClassDef),
|
||||
PatchDef(PatchDef),
|
||||
AttrDef(AttrDef),
|
||||
TypeAsc(TypeAscription),
|
||||
Code(Block), // code object
|
||||
|
@ -1888,10 +1941,10 @@ pub enum Expr {
|
|||
Import(Accessor),
|
||||
}
|
||||
|
||||
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
impl_display_from_nested!(Expr);
|
||||
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
impl_t_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Record, BinOp, UnaryOp, Call, Lambda, Def, ClassDef, PatchDef, AttrDef, Code, Compound, TypeAsc, Set, Import);
|
||||
|
||||
impl Default for Expr {
|
||||
fn default() -> Self {
|
||||
|
|
|
@ -139,6 +139,11 @@ impl<'a> Linker<'a> {
|
|||
self.resolve_pymod_path(def);
|
||||
}
|
||||
}
|
||||
Expr::PatchDef(patch_def) => {
|
||||
for def in patch_def.methods.iter_mut() {
|
||||
self.resolve_pymod_path(def);
|
||||
}
|
||||
}
|
||||
Expr::AttrDef(attr_def) => {
|
||||
// REVIEW:
|
||||
for chunk in attr_def.block.iter_mut() {
|
||||
|
@ -255,6 +260,11 @@ impl<'a> Linker<'a> {
|
|||
self.replace_import(def);
|
||||
}
|
||||
}
|
||||
Expr::PatchDef(patch_def) => {
|
||||
for def in patch_def.methods.iter_mut() {
|
||||
self.replace_import(def);
|
||||
}
|
||||
}
|
||||
Expr::AttrDef(attr_def) => {
|
||||
// REVIEW:
|
||||
for chunk in attr_def.block.iter_mut() {
|
||||
|
|
|
@ -220,8 +220,15 @@ impl ASTLowerer {
|
|||
}
|
||||
}
|
||||
}
|
||||
hir::Expr::ClassDef(cl_def) => {
|
||||
for chunk in cl_def.methods.iter() {
|
||||
hir::Expr::ClassDef(class_def) => {
|
||||
for chunk in class_def.methods.iter() {
|
||||
if let Err(ws) = self.use_check(chunk, mode) {
|
||||
warns.extend(ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Expr::PatchDef(patch_def) => {
|
||||
for chunk in patch_def.methods.iter() {
|
||||
if let Err(ws) = self.use_check(chunk, mode) {
|
||||
warns.extend(ws);
|
||||
}
|
||||
|
@ -1287,7 +1294,7 @@ impl ASTLowerer {
|
|||
} else {
|
||||
todo!()
|
||||
};
|
||||
let require_or_sup = self.get_require_or_sup(hir_def.body.block.remove(0));
|
||||
let require_or_sup = self.get_require_or_sup_or_base(hir_def.body.block.remove(0));
|
||||
Ok(hir::ClassDef::new(
|
||||
type_obj.clone(),
|
||||
hir_def.sig,
|
||||
|
@ -1298,6 +1305,71 @@ impl ASTLowerer {
|
|||
))
|
||||
}
|
||||
|
||||
fn lower_patch_def(&mut self, class_def: ast::PatchDef) -> LowerResult<hir::PatchDef> {
|
||||
log!(info "entered {}({class_def})", fn_name!());
|
||||
let base_t = {
|
||||
let base_t_expr =
|
||||
enum_unwrap!(class_def.def.body.block.get(0).unwrap(), ast::Expr::Call)
|
||||
.args
|
||||
.get_left_or_key("Base")
|
||||
.unwrap();
|
||||
let spec = Parser::expr_to_type_spec(base_t_expr.clone()).unwrap();
|
||||
let mut dummy_tv_cache = TyVarCache::new(self.ctx.level, &self.ctx);
|
||||
self.ctx.instantiate_typespec(
|
||||
&spec,
|
||||
None,
|
||||
&mut dummy_tv_cache,
|
||||
RegistrationMode::Normal,
|
||||
false,
|
||||
)?
|
||||
};
|
||||
let mut hir_def = self.lower_def(class_def.def)?;
|
||||
let base = self.get_require_or_sup_or_base(hir_def.body.block.remove(0));
|
||||
let mut hir_methods = hir::Block::empty();
|
||||
for mut methods in class_def.methods_list.into_iter() {
|
||||
let kind = ContextKind::PatchMethodDefs(base_t.clone());
|
||||
self.ctx
|
||||
.grow(hir_def.sig.ident().inspect(), kind, hir_def.sig.vis(), None);
|
||||
for attr in methods.attrs.iter_mut() {
|
||||
match attr {
|
||||
ast::ClassAttr::Def(def) => {
|
||||
if methods.vis.is(TokenKind::Dot) {
|
||||
def.sig.ident_mut().unwrap().dot = Some(Token::new(
|
||||
TokenKind::Dot,
|
||||
".",
|
||||
def.sig.ln_begin().unwrap(),
|
||||
def.sig.col_begin().unwrap(),
|
||||
));
|
||||
}
|
||||
self.ctx.preregister_def(def)?;
|
||||
}
|
||||
ast::ClassAttr::Decl(_decl) => {}
|
||||
}
|
||||
}
|
||||
for attr in methods.attrs.into_iter() {
|
||||
match attr {
|
||||
ast::ClassAttr::Def(def) => match self.lower_def(def) {
|
||||
Ok(def) => {
|
||||
hir_methods.push(hir::Expr::Def(def));
|
||||
}
|
||||
Err(errs) => {
|
||||
self.errs.extend(errs);
|
||||
}
|
||||
},
|
||||
ast::ClassAttr::Decl(decl) => {
|
||||
let decl = self.lower_type_asc(decl)?;
|
||||
hir_methods.push(hir::Expr::TypeAsc(decl));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(mut errs) = self.ctx.check_decls() {
|
||||
self.errs.append(&mut errs);
|
||||
}
|
||||
self.push_patch();
|
||||
}
|
||||
Ok(hir::PatchDef::new(hir_def.sig, base, hir_methods))
|
||||
}
|
||||
|
||||
fn register_trait_impl(
|
||||
&mut self,
|
||||
class: &Type,
|
||||
|
@ -1561,16 +1633,56 @@ impl ASTLowerer {
|
|||
.push((ClassDefType::Simple(class), methods));
|
||||
}
|
||||
|
||||
fn push_patch(&mut self) {
|
||||
let methods = self.ctx.pop();
|
||||
let ContextKind::PatchMethodDefs(base) = &methods.kind else { unreachable!() };
|
||||
let patch_name = *methods.name.split_with(&["::", "."]).last().unwrap();
|
||||
let patch_root = self
|
||||
.ctx
|
||||
.patches
|
||||
.get_mut(patch_name)
|
||||
.unwrap_or_else(|| todo!("{} not found", methods.name));
|
||||
for (newly_defined_name, vi) in methods.locals.clone().into_iter() {
|
||||
for (_, already_defined_methods) in patch_root.methods_list.iter_mut() {
|
||||
// TODO: 特殊化なら同じ名前でもOK
|
||||
// TODO: 定義のメソッドもエラー表示
|
||||
if let Some((_already_defined_name, already_defined_vi)) =
|
||||
already_defined_methods.get_local_kv(newly_defined_name.inspect())
|
||||
{
|
||||
if already_defined_vi.kind != VarKind::Auto
|
||||
&& already_defined_vi.impl_of == vi.impl_of
|
||||
{
|
||||
self.errs.push(LowerError::duplicate_definition_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
newly_defined_name.loc(),
|
||||
methods.caused_by(),
|
||||
newly_defined_name.inspect(),
|
||||
));
|
||||
} else {
|
||||
already_defined_methods
|
||||
.locals
|
||||
.remove(&newly_defined_name.inspect()[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
patch_root
|
||||
.methods_list
|
||||
.push((ClassDefType::Simple(base.clone()), methods));
|
||||
}
|
||||
|
||||
#[allow(clippy::only_used_in_recursion)]
|
||||
fn get_require_or_sup(&self, expr: hir::Expr) -> hir::Expr {
|
||||
fn get_require_or_sup_or_base(&self, expr: hir::Expr) -> hir::Expr {
|
||||
match expr {
|
||||
acc @ hir::Expr::Accessor(_) => acc,
|
||||
hir::Expr::Call(mut call) => match call.obj.show_acc().as_ref().map(|s| &s[..]) {
|
||||
Some("Class") => call.args.remove_left_or_key("Requirement").unwrap(),
|
||||
Some("Inherit") => call.args.remove_left_or_key("Super").unwrap(),
|
||||
Some("Inheritable") => {
|
||||
self.get_require_or_sup(call.args.remove_left_or_key("Class").unwrap())
|
||||
self.get_require_or_sup_or_base(call.args.remove_left_or_key("Class").unwrap())
|
||||
}
|
||||
Some("Patch") => call.args.remove_left_or_key("Base").unwrap(),
|
||||
_ => todo!(),
|
||||
},
|
||||
other => todo!("{other}"),
|
||||
|
@ -1637,6 +1749,7 @@ impl ASTLowerer {
|
|||
ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)),
|
||||
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
|
||||
ast::Expr::ClassDef(defs) => Ok(hir::Expr::ClassDef(self.lower_class_def(defs)?)),
|
||||
ast::Expr::PatchDef(defs) => Ok(hir::Expr::PatchDef(self.lower_patch_def(defs)?)),
|
||||
ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_type_asc(tasc)?)),
|
||||
other => todo!("{other}"),
|
||||
}
|
||||
|
|
|
@ -126,6 +126,18 @@ impl OwnershipChecker {
|
|||
self.check_block(&def.body.block);
|
||||
self.path_stack.pop();
|
||||
}
|
||||
Expr::ClassDef(class_def) => {
|
||||
self.check_expr(&class_def.require_or_sup, Ownership::Owned, false);
|
||||
for def in class_def.methods.iter() {
|
||||
self.check_expr(def, Ownership::Owned, true);
|
||||
}
|
||||
}
|
||||
Expr::PatchDef(patch_def) => {
|
||||
self.check_expr(&patch_def.base, Ownership::Owned, false);
|
||||
for def in patch_def.methods.iter() {
|
||||
self.check_expr(def, Ownership::Owned, true);
|
||||
}
|
||||
}
|
||||
// Access in chunks does not drop variables (e.g., access in REPL)
|
||||
Expr::Accessor(acc) => self.check_acc(acc, ownership, chunk),
|
||||
// TODO: referenced
|
||||
|
|
|
@ -4,7 +4,7 @@ use erg_common::log;
|
|||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_common::Str;
|
||||
|
||||
use erg_parser::ast::{ClassDef, Expr, Methods, Module, PreDeclTypeSpec, TypeSpec, AST};
|
||||
use erg_parser::ast::{ClassDef, Expr, Methods, Module, PatchDef, PreDeclTypeSpec, TypeSpec, AST};
|
||||
|
||||
use crate::error::{TyCheckError, TyCheckErrors};
|
||||
|
||||
|
@ -46,6 +46,14 @@ impl Reorderer {
|
|||
let type_def = ClassDef::new(def, vec![]);
|
||||
new.push(Expr::ClassDef(type_def));
|
||||
}
|
||||
Some("Patch") => {
|
||||
self.def_root_pos_map.insert(
|
||||
def.sig.ident().unwrap().inspect().clone(),
|
||||
new.len(),
|
||||
);
|
||||
let type_def = PatchDef::new(def, vec![]);
|
||||
new.push(Expr::PatchDef(type_def));
|
||||
}
|
||||
_ => {
|
||||
new.push(Expr::Def(def));
|
||||
}
|
||||
|
@ -97,12 +105,17 @@ impl Reorderer {
|
|||
|
||||
fn link_methods(&mut self, name: Str, new: &mut Vec<Expr>, methods: Methods) {
|
||||
if let Some(pos) = self.def_root_pos_map.get(&name) {
|
||||
let mut class_def = match new.remove(*pos) {
|
||||
Expr::ClassDef(class_def) => class_def,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match new.remove(*pos) {
|
||||
Expr::ClassDef(mut class_def) => {
|
||||
class_def.methods_list.push(methods);
|
||||
new.insert(*pos, Expr::ClassDef(class_def));
|
||||
}
|
||||
Expr::PatchDef(mut patch_def) => {
|
||||
patch_def.methods_list.push(methods);
|
||||
new.insert(*pos, Expr::PatchDef(patch_def));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let similar_name = self
|
||||
.def_root_pos_map
|
||||
|
|
|
@ -130,6 +130,23 @@ impl UnionTypeObj {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PatchObj {
|
||||
pub t: Type,
|
||||
pub base: Box<TypeObj>,
|
||||
pub impls: Option<Box<TypeObj>>,
|
||||
}
|
||||
|
||||
impl PatchObj {
|
||||
pub fn new(t: Type, base: TypeObj, impls: Option<TypeObj>) -> Self {
|
||||
Self {
|
||||
t,
|
||||
base: Box::new(base),
|
||||
impls: impls.map(Box::new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum GenTypeObj {
|
||||
Class(ClassTypeObj),
|
||||
|
@ -138,6 +155,7 @@ pub enum GenTypeObj {
|
|||
Subtrait(SubsumedTypeObj),
|
||||
StructuralTrait(TraitTypeObj),
|
||||
Union(UnionTypeObj),
|
||||
Patch(PatchObj),
|
||||
}
|
||||
|
||||
impl fmt::Display for GenTypeObj {
|
||||
|
@ -164,6 +182,10 @@ impl GenTypeObj {
|
|||
GenTypeObj::Trait(TraitTypeObj::new(t, require, impls))
|
||||
}
|
||||
|
||||
pub fn patch(t: Type, base: TypeObj, impls: Option<TypeObj>) -> Self {
|
||||
GenTypeObj::Patch(PatchObj::new(t, base, impls))
|
||||
}
|
||||
|
||||
pub fn subsumed(
|
||||
t: Type,
|
||||
sup: TypeObj,
|
||||
|
@ -184,6 +206,7 @@ impl GenTypeObj {
|
|||
Self::Trait(trait_) => Some(trait_.require.as_ref()),
|
||||
Self::Subtrait(subtrait) => Some(subtrait.sup.as_ref()),
|
||||
Self::StructuralTrait(trait_) => Some(trait_.require.as_ref()),
|
||||
Self::Patch(patch) => Some(patch.base.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -193,6 +216,7 @@ impl GenTypeObj {
|
|||
Self::Class(class) => class.impls.as_ref().map(|x| x.as_ref()),
|
||||
Self::Subclass(subclass) => subclass.impls.as_ref().map(|x| x.as_ref()),
|
||||
Self::Subtrait(subtrait) => subtrait.impls.as_ref().map(|x| x.as_ref()),
|
||||
Self::Patch(patch) => patch.impls.as_ref().map(|x| x.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -202,6 +226,7 @@ impl GenTypeObj {
|
|||
Self::Class(class) => Some(&mut class.impls),
|
||||
Self::Subclass(subclass) => Some(&mut subclass.impls),
|
||||
Self::Subtrait(subtrait) => Some(&mut subtrait.impls),
|
||||
Self::Patch(patch) => Some(&mut patch.impls),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +243,7 @@ impl GenTypeObj {
|
|||
match self {
|
||||
Self::Class(_) | Self::Subclass(_) => Type::ClassType,
|
||||
Self::Trait(_) | Self::Subtrait(_) | Self::StructuralTrait(_) => Type::TraitType,
|
||||
Self::Patch(_) => Type::Patch,
|
||||
_ => Type::Type,
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +256,7 @@ impl GenTypeObj {
|
|||
Self::Subtrait(subtrait) => &subtrait.t,
|
||||
Self::StructuralTrait(trait_) => &trait_.t,
|
||||
Self::Union(union_) => &union_.t,
|
||||
Self::Patch(patch) => &patch.t,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +268,7 @@ impl GenTypeObj {
|
|||
Self::Subtrait(subtrait) => &mut subtrait.t,
|
||||
Self::StructuralTrait(trait_) => &mut trait_.t,
|
||||
Self::Union(union_) => &mut union_.t,
|
||||
Self::Patch(patch) => &mut patch.t,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +280,7 @@ impl GenTypeObj {
|
|||
Self::Subtrait(subtrait) => subtrait.t,
|
||||
Self::StructuralTrait(trait_) => trait_.t,
|
||||
Self::Union(union_) => union_.t,
|
||||
Self::Patch(patch) => patch.t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3412,6 +3412,36 @@ impl ClassDef {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PatchDef {
|
||||
pub def: Def,
|
||||
pub methods_list: Vec<Methods>,
|
||||
}
|
||||
|
||||
impl NestedDisplay for PatchDef {
|
||||
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
|
||||
write!(f, "(patch)")?;
|
||||
self.def.fmt_nest(f, level)?;
|
||||
for methods in self.methods_list.iter() {
|
||||
write!(f, "(methods)")?;
|
||||
methods.fmt_nest(f, level + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl_display_from_nested!(PatchDef);
|
||||
impl_locational!(PatchDef, def);
|
||||
|
||||
impl PatchDef {
|
||||
pub const fn new(def: Def, methods: Vec<Methods>) -> Self {
|
||||
Self {
|
||||
def,
|
||||
methods_list: methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expression(式)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Expr {
|
||||
|
@ -3431,11 +3461,12 @@ pub enum Expr {
|
|||
Def(Def),
|
||||
Methods(Methods),
|
||||
ClassDef(ClassDef),
|
||||
PatchDef(PatchDef),
|
||||
}
|
||||
|
||||
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef);
|
||||
impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef, PatchDef);
|
||||
impl_display_from_nested!(Expr);
|
||||
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef);
|
||||
impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAsc, Def, Methods, ClassDef, PatchDef);
|
||||
|
||||
impl Expr {
|
||||
pub fn is_match_call(&self) -> bool {
|
||||
|
@ -3451,7 +3482,10 @@ impl Expr {
|
|||
}
|
||||
|
||||
pub const fn is_definition(&self) -> bool {
|
||||
matches!(self, Expr::Def(_) | Expr::ClassDef(_) | Expr::Methods(_))
|
||||
matches!(
|
||||
self,
|
||||
Expr::Def(_) | Expr::ClassDef(_) | Expr::PatchDef(_) | Expr::Methods(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn need_to_be_closed(&self) -> bool {
|
||||
|
|
|
@ -16,9 +16,10 @@ use crate::ast::{
|
|||
ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Expr,
|
||||
Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, MixedRecord, Module,
|
||||
NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple,
|
||||
ParamPattern, ParamRecordAttr, Params, PosArg, Record, RecordAttrOrIdent, RecordAttrs,
|
||||
Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TypeAppArgs, TypeBoundSpecs,
|
||||
TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarSignature,
|
||||
ParamPattern, ParamRecordAttr, Params, PatchDef, PosArg, Record, RecordAttrOrIdent,
|
||||
RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TypeAppArgs,
|
||||
TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr,
|
||||
VarSignature,
|
||||
};
|
||||
use crate::token::{Token, TokenKind, COLON, DOT};
|
||||
|
||||
|
@ -225,6 +226,15 @@ impl Desugarer {
|
|||
.collect();
|
||||
Expr::ClassDef(ClassDef::new(def, methods))
|
||||
}
|
||||
Expr::PatchDef(class_def) => {
|
||||
let def = enum_unwrap!(desugar(Expr::Def(class_def.def)), Expr::Def);
|
||||
let methods = class_def
|
||||
.methods_list
|
||||
.into_iter()
|
||||
.map(|method| enum_unwrap!(desugar(Expr::Methods(method)), Expr::Methods))
|
||||
.collect();
|
||||
Expr::PatchDef(PatchDef::new(def, methods))
|
||||
}
|
||||
Expr::Lambda(lambda) => {
|
||||
let mut chunks = vec![];
|
||||
for chunk in lambda.body.into_iter() {
|
||||
|
|
|
@ -34,7 +34,7 @@ StrangeInt.
|
|||
# Overriding methods must be given Override decorators.
|
||||
# In addition, you need to override all Int methods that depend on Int.`_+_`.
|
||||
@Override
|
||||
`_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... ````` , so these methods must also be overridden
|
||||
`_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... , so these methods must also be overridden
|
||||
```
|
||||
|
||||
## Selecting Patches
|
||||
|
|
|
@ -14,23 +14,14 @@ namedtupleについては、[こちら](https://docs.python.jp/3/library/collect
|
|||
似たような機能にdataclassがありますが、dataclassは`__eq__`や`__hash__`が自動実装されるなどの影響で少しパフォーマンスが落ちます。
|
||||
|
||||
```python
|
||||
Employee = Class {.name = Str; .id = Int}
|
||||
|
||||
employee = Employee.new({.name = "John Smith"; .id = 100})
|
||||
|
||||
employee = {.name = "John Smith"; .id = 100}
|
||||
assert employee.name == "John Smith"
|
||||
```
|
||||
|
||||
```python
|
||||
from typing import NamedTuple
|
||||
|
||||
class Employee(NamedTuple):
|
||||
__records__ = ['name', 'id']
|
||||
name: str
|
||||
id: int
|
||||
|
||||
employee = Employee('John Smith', 100)
|
||||
|
||||
employee = NamedTuple(['name', 'id'])('John Smith', 100)
|
||||
assert employee.name == 'John Smith'
|
||||
```
|
||||
|
||||
|
@ -90,3 +81,19 @@ y::x = 2
|
|||
assert module::x == 2
|
||||
y = None
|
||||
```
|
||||
|
||||
## Patch
|
||||
|
||||
```python
|
||||
func b: Bool =
|
||||
Invert = Patch Bool
|
||||
Invert.
|
||||
invert self = not self
|
||||
b.invert()
|
||||
```
|
||||
|
||||
```python
|
||||
def func(b):
|
||||
def Invert::invert(self): return not self
|
||||
return Invert::invert(b)
|
||||
```
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
Binary = Patch {0, 1}
|
||||
Binary.
|
||||
invert self =
|
||||
if self == 0, 1 else 0
|
||||
assert 1.invert() == 0
|
||||
assert 0.invert() == 1
|
||||
Invert = Patch Bool
|
||||
Invert::
|
||||
_zero = False
|
||||
_invert self = not self
|
||||
Invert.
|
||||
zero = Invert::_zero
|
||||
invert self = self::_invert()
|
||||
|
||||
Nat = Patch {I | I >= 0}
|
||||
Nat.times! self, block! =
|
||||
for! 0..<self, _ => block!()
|
||||
10.times! do!:
|
||||
print! "!"
|
||||
assert False.invert()
|
||||
|
||||
.ToLower = Patch Str
|
||||
.ToLower::
|
||||
_lowers = "abcdefghijklmnopqrstuvwxyz"
|
||||
_to_lowercase self = self.lower()
|
||||
.ToLower.
|
||||
lowers = .ToLower::_lowers
|
||||
to_lowercase self = self::_to_lowercase()
|
||||
|
||||
print! "".lowers
|
||||
print! "AAA".to_lowercase()
|
||||
|
|
|
@ -84,6 +84,11 @@ fn exec_infer_trait() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/infer_trait.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_patch() -> Result<(), ()> {
|
||||
expect_success("examples/patch.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_pattern() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/pattern.er")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue