Implement Patch

This commit is contained in:
Shunsuke Shibayama 2022-12-02 20:03:02 +09:00
parent 7078f95cec
commit bade70ef91
22 changed files with 741 additions and 116 deletions

View file

@ -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),

View file

@ -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)

View file

@ -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![];

View file

@ -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(|| {

View file

@ -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) {

View file

@ -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()) {

View file

@ -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>,

View file

@ -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,

View file

@ -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() {

View file

@ -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() {

View file

@ -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 {

View file

@ -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() {

View file

@ -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}"),
}

View file

@ -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

View file

@ -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

View file

@ -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,
}
}
}

View file

@ -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 {

View file

@ -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() {

View file

@ -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

View file

@ -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)
```

View file

@ -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()

View file

@ -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")