feat: add Dict.concat/diff, Dict!.merge!/remove!

This commit is contained in:
Shunsuke Shibayama 2023-10-04 22:39:30 +09:00
parent 73958a3e56
commit 13a346e488
11 changed files with 223 additions and 6 deletions

View file

@ -1304,6 +1304,32 @@ impl Context {
(e @ TyParam::Erased(_), _) | (_, e @ TyParam::Erased(_)) => Ok(e), (e @ TyParam::Erased(_), _) | (_, e @ TyParam::Erased(_)) => Ok(e),
(lhs @ TyParam::FreeVar(_), rhs) => Ok(TyParam::bin(op, lhs, rhs)), (lhs @ TyParam::FreeVar(_), rhs) => Ok(TyParam::bin(op, lhs, rhs)),
(lhs, rhs @ TyParam::FreeVar(_)) => Ok(TyParam::bin(op, lhs, rhs)), (lhs, rhs @ TyParam::FreeVar(_)) => Ok(TyParam::bin(op, lhs, rhs)),
(TyParam::Value(lhs), rhs) => {
let lhs = match Self::convert_value_into_tp(lhs) {
Ok(tp) => tp,
Err(lhs) => {
return feature_error!(
self,
Location::Unknown,
&format!("{lhs} {op} {rhs}")
);
}
};
self.eval_bin_tp(op, lhs, rhs)
}
(lhs, TyParam::Value(rhs)) => {
let rhs = match Self::convert_value_into_tp(rhs) {
Ok(tp) => tp,
Err(rhs) => {
return feature_error!(
self,
Location::Unknown,
&format!("{lhs} {op} {rhs}")
);
}
};
self.eval_bin_tp(op, lhs, rhs)
}
(l, r) => feature_error!(self, Location::Unknown, &format!("{l} {op} {r}")) (l, r) => feature_error!(self, Location::Unknown, &format!("{l} {op} {r}"))
.map_err(Into::into), .map_err(Into::into),
} }
@ -1439,6 +1465,10 @@ impl Context {
} }
Ok(TyParam::Array(new_tps)) Ok(TyParam::Array(new_tps))
} }
TyParam::UnsizedArray(elem) => {
let elem = self.eval_tp(*elem)?;
Ok(TyParam::UnsizedArray(Box::new(elem)))
}
TyParam::Tuple(tps) => { TyParam::Tuple(tps) => {
let mut new_tps = Vec::with_capacity(tps.len()); let mut new_tps = Vec::with_capacity(tps.len());
for tp in tps { for tp in tps {
@ -1484,7 +1514,7 @@ impl Context {
} }
TyParam::ProjCall { obj, attr, args } => self.eval_proj_call(*obj, attr, args, &()), TyParam::ProjCall { obj, attr, args } => self.eval_proj_call(*obj, attr, args, &()),
TyParam::Value(_) => Ok(p.clone()), TyParam::Value(_) => Ok(p.clone()),
_other => feature_error!(self, Location::Unknown, "???"), other => feature_error!(self, Location::Unknown, &format!("evaluating {other}")),
} }
} }

View file

@ -1798,6 +1798,40 @@ impl Context {
dict_.register_py_builtin(FUNC_GET, get_t, Some(FUNC_GET), 9); dict_.register_py_builtin(FUNC_GET, get_t, Some(FUNC_GET), 9);
let copy_t = fn0_met(dict_t.clone(), dict_t.clone()).quantify(); let copy_t = fn0_met(dict_t.clone(), dict_t.clone()).quantify();
dict_.register_py_builtin(COPY, copy_t, Some(COPY), 7); dict_.register_py_builtin(COPY, copy_t, Some(COPY), 7);
let D2 = mono_q_tp("D2", instanceof(mono(GENERIC_DICT)));
let other_dict_t = poly(DICT, vec![D2.clone()]);
let dict_concat_t = fn1_met(
dict_t.clone(),
other_dict_t.clone(),
poly(
DICT,
vec![D.clone().proj_call(FUNC_CONCAT.into(), vec![D2.clone()])],
),
)
.quantify();
let concat = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNC_CONCAT,
dict_concat,
dict_concat_t,
None,
)));
dict_.register_builtin_const(FUNC_CONCAT, Visibility::BUILTIN_PUBLIC, concat);
let dict_diff_t = fn1_met(
dict_t.clone(),
other_dict_t.clone(),
poly(
DICT,
vec![D.clone().proj_call(FUNC_DIFF.into(), vec![D2.clone()])],
),
)
.quantify();
let diff = ValueObj::Subr(ConstSubr::Builtin(BuiltinConstSubr::new(
FUNC_DIFF,
dict_diff,
dict_diff_t,
None,
)));
dict_.register_builtin_const(FUNC_DIFF, Visibility::BUILTIN_PUBLIC, diff);
/* Bytes */ /* Bytes */
let mut bytes = Self::builtin_mono_class(BYTES, 2); let mut bytes = Self::builtin_mono_class(BYTES, 2);
bytes.register_superclass(Obj, &obj); bytes.register_superclass(Obj, &obj);
@ -2692,16 +2726,66 @@ impl Context {
dict_mut_t.clone(), dict_mut_t.clone(),
Some(poly( Some(poly(
MUT_DICT, MUT_DICT,
vec![D + dict! { K.clone() => V.clone() }.into()], vec![D.clone() + dict! { K.clone() => V.clone() }.into()],
)), )),
), ),
vec![kw(KW_KEY, K), kw(KW_VALUE, V)], vec![kw(KW_KEY, K.clone()), kw(KW_VALUE, V.clone())],
None, None,
vec![], vec![],
NoneType, NoneType,
) )
.quantify(); .quantify();
dict_mut.register_py_builtin(PROC_INSERT, insert_t, Some(FUNDAMENTAL_SETITEM), 12); dict_mut.register_py_builtin(PROC_INSERT, insert_t, Some(FUNDAMENTAL_SETITEM), 12);
let remove_t = pr_met(
ref_mut(
dict_mut_t.clone(),
Some(poly(
MUT_DICT,
vec![D
.clone()
.proj_call(FUNC_DIFF.into(), vec![dict! { K.clone() => Never }.into()])],
)),
),
vec![kw(KW_KEY, K.clone())],
None,
vec![],
proj_call(D.clone(), FUNDAMENTAL_GETITEM, vec![ty_tp(K.clone())]) | NoneType,
)
.quantify();
dict_mut.register_py_builtin(PROC_REMOVE, remove_t, Some(FUNC_REMOVE), 19);
let update_t = pr_met(
ref_mut(
dict_mut_t.clone(),
Some(poly(
MUT_DICT,
vec![D.clone() + dict! { K.clone() => V.clone() }.into()],
)),
),
vec![kw(
KW_ITERABLE,
poly(ITERABLE, vec![ty_tp(tuple_t(vec![K.clone(), V.clone()]))]),
)],
None,
vec![],
NoneType,
)
.quantify();
dict_mut.register_py_builtin(PROC_UPDATE, update_t, Some(FUNC_UPDATE), 26);
let merge_t = pr_met(
ref_mut(
dict_mut_t.clone(),
Some(poly(
MUT_DICT,
vec![D.proj_call(FUNC_CONCAT.into(), vec![D2.clone()])],
)),
),
vec![kw(KW_OTHER, poly(DICT, vec![D2.clone()]))],
None,
vec![],
NoneType,
)
.quantify();
dict_mut.register_py_builtin(PROC_MERGE, merge_t, Some(FUNC_MERGE), 32);
/* Set! */ /* Set! */
let set_mut_t = poly(MUT_SET, vec![ty_tp(T.clone()), N]); let set_mut_t = poly(MUT_SET, vec![ty_tp(T.clone()), N]);
let mut set_mut_ = let mut set_mut_ =

View file

@ -363,6 +363,32 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
Ok(ValueObj::builtin_type(union).into()) Ok(ValueObj::builtin_type(union).into())
} }
/// If the key is duplicated, the value of the right dict is used.
/// `{Str: Int, Int: Float}.concat({Int: Str, Float: Bool}) == {Str: Int, Int: Str, Float: Bool}`
pub(crate) fn dict_concat(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let other = args
.remove_left_or_key("Other")
.ok_or_else(|| not_passed("Other"))?;
let other = enum_unwrap!(other, ValueObj::Dict);
Ok(ValueObj::Dict(slf.concat(other)).into())
}
pub(crate) fn dict_diff(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args
.remove_left_or_key("Self")
.ok_or_else(|| not_passed("Self"))?;
let slf = enum_unwrap!(slf, ValueObj::Dict);
let other = args
.remove_left_or_key("Other")
.ok_or_else(|| not_passed("Other"))?;
let other = enum_unwrap!(other, ValueObj::Dict);
Ok(ValueObj::Dict(slf.diff(&other)).into())
}
/// `[Int, Str].union() == Int or Str` /// `[Int, Str].union() == Int or Str`
pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> { pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
let slf = args let slf = args

View file

@ -54,6 +54,7 @@ const SELF: &str = "Self";
const IMMUTIZABLE: &str = "Immutizable"; const IMMUTIZABLE: &str = "Immutizable";
const IMMUT_TYPE: &str = "ImmutType"; const IMMUT_TYPE: &str = "ImmutType";
const PROC_UPDATE: &str = "update!"; const PROC_UPDATE: &str = "update!";
const FUNC_UPDATE: &str = "update";
const MUTIZABLE: &str = "Mutizable"; const MUTIZABLE: &str = "Mutizable";
const MUTABLE_MUT_TYPE: &str = "MutType!"; const MUTABLE_MUT_TYPE: &str = "MutType!";
const PATH_LIKE: &str = "PathLike"; const PATH_LIKE: &str = "PathLike";
@ -232,8 +233,11 @@ const PROC_UPDATE_NTH: &str = "update_nth!";
const FUNC_PARTITION: &str = "partition"; const FUNC_PARTITION: &str = "partition";
const FUNC_DEDUP: &str = "dedup"; const FUNC_DEDUP: &str = "dedup";
const FUNC_CONCAT: &str = "concat"; const FUNC_CONCAT: &str = "concat";
const FUNC_DIFF: &str = "diff";
const FUNC_PUSH: &str = "push"; const FUNC_PUSH: &str = "push";
const PROC_PUSH: &str = "push!"; const PROC_PUSH: &str = "push!";
const FUNC_MERGE: &str = "merge";
const PROC_MERGE: &str = "merge!";
const ARRAY_ITERATOR: &str = "ArrayIterator"; const ARRAY_ITERATOR: &str = "ArrayIterator";
const GENERIC_SET: &str = "GenericSet"; const GENERIC_SET: &str = "GenericSet";
const SET: &str = "Set"; const SET: &str = "Set";
@ -557,6 +561,7 @@ const KW_SUB: &str = "sub";
const KW_OFFSET: &str = "offset"; const KW_OFFSET: &str = "offset";
const KW_WHENCE: &str = "whence"; const KW_WHENCE: &str = "whence";
const KW_CHARS: &str = "chars"; const KW_CHARS: &str = "chars";
const KW_OTHER: &str = "other";
pub fn builtins_path() -> PathBuf { pub fn builtins_path() -> PathBuf {
erg_pystd_path().join("builtins.d.er") erg_pystd_path().join("builtins.d.er")

View file

@ -4,4 +4,29 @@ dict = pyimport "Dict"
.Dict!: ClassType .Dict!: ClassType
.Dict! <: dict.Dict .Dict! <: dict.Dict
.Dict!. .Dict!.
insert!: |K, V|(self: .Dict!, key: K, value: V) => NoneType '''erg
dic = !{"a": 1}
dic.insert!("b", 2)
assert dic == {"a": 1, "b": 2}
'''
insert!: |K, V|(self: .Dict!(K, V), key: K, value: V) => NoneType
'''erg
dic = !{"a": 1}
x = dic.remove!("a")
assert dic == {}
assert x == 1
'''
remove!: |K, V|(self: .Dict!(K, V), key: K) => V or NoneType
'''erg
dic = !{"a": 1}
dic.update!({"b": 2})
dic.update!([("c", 3)])
assert dic == {"a": 1, "b": 2, "c": 3}
'''
update!: |K, V|(self: .Dict!(K, V), other: Iterable([K, V])) => NoneType
'''erg
dic = !{"a": 1}
dic.merge!({"b": 2})
assert dic == {"a": 1, "b": 2}
'''
merge!: |K, V|(self: .Dict!(K, V), other: .Dict!(K, V)) => NoneType

View file

@ -10,3 +10,13 @@
items: |K, V|(self: .Dict(K, V)) -> .DictItems(K, V) items: |K, V|(self: .Dict(K, V)) -> .DictItems(K, V)
keys: |K, V|(self: .Dict(K, V)) -> .DictKeys(K, V) keys: |K, V|(self: .Dict(K, V)) -> .DictKeys(K, V)
values: |K, V|(self: .Dict(K, V)) -> .DictValues(K, V) values: |K, V|(self: .Dict(K, V)) -> .DictValues(K, V)
'''erg
dic = {"a": 1, "b": 2}
assert dic.concat({"c": 3}) == {"a": 1, "b": 2, "c": 3}
'''
concat: (self: .Dict(K, V), other: .Dict(K, V)) -> .Dict(K, V)
'''erg
dic = {"a": 1, "b": 2}
assert dic.diff({"a": 2, "d": 4}) == {"b": 2}
'''
diff: (self: .Dict(K, V), other: .Dict(K, V)) -> .Dict(K, V)

View file

@ -1,2 +1,18 @@
class Dict(dict): class Dict(dict):
pass def concat(self, other):
return Dict({**self, **other})
def diff(self, other):
return Dict({k: v for k, v in self.items() if k not in other})
# other: Iterable
def extend(self, other):
self.update(other)
# other: Dict
def merge(self, other):
self.update(other)
def insert(self, key, value):
self[key] = value
def remove(self, key):
res = self.get(key)
if res != None:
del self[key]
return res

View file

@ -1363,6 +1363,15 @@ impl TyParam {
} }
} }
pub fn is_type(&self) -> bool {
match self {
Self::Type(_) => true,
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_type(),
Self::Value(ValueObj::Type(_)) => true,
_ => false,
}
}
pub fn replace(self, target: &Type, to: &Type) -> TyParam { pub fn replace(self, target: &Type, to: &Type) -> TyParam {
match self { match self {
TyParam::Value(ValueObj::Type(obj)) => { TyParam::Value(ValueObj::Type(obj)) => {

View file

@ -494,7 +494,7 @@ impl TypeObj {
} }
/// 値オブジェクト /// 値オブジェクト
/// コンパイル時評価ができ、シリアライズも可能 /// コンパイル時評価ができ、シリアライズも可能(Typeなどはシリアライズ不可)
#[derive(Clone, PartialEq, Default)] #[derive(Clone, PartialEq, Default)]
pub enum ValueObj { pub enum ValueObj {
Int(i32), Int(i32),

View file

@ -2,3 +2,7 @@ for! {"a": 1, "b": 2}.keys(), s =>
print! "key: " + s print! "key: " + s
for! {"a": 1, "b": 2}.values(), i => for! {"a": 1, "b": 2}.values(), i =>
print! i + 0 print! i + 0
dic = { "a": 1, "b": 2 }
assert dic.concat({ "c": 3 }) == { "a": 1, "b": 2, "c": 3 }
assert dic.diff({ "a": 1 }) == { "b": 2 }

View file

@ -4,3 +4,11 @@ dic = !d
dic.insert! "b", 2 dic.insert! "b", 2
assert dic.get("a") == 1 assert dic.get("a") == 1
assert dic.get("b") == 2 assert dic.get("b") == 2
dic.merge!({ "a": 1 })
x = dic.remove!("a")
assert x == 1
dics as Dict!({Str: [Int; _]}) = !{:}
dics.insert! "a", []
dics.insert! "b", [1, 2, 3]