mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 04:09:05 +00:00
feat: add Dict.concat/diff
, Dict!.merge!/remove!
This commit is contained in:
parent
73958a3e56
commit
13a346e488
11 changed files with 223 additions and 6 deletions
|
@ -1304,6 +1304,32 @@ impl Context {
|
|||
(e @ TyParam::Erased(_), _) | (_, e @ TyParam::Erased(_)) => Ok(e),
|
||||
(lhs @ TyParam::FreeVar(_), rhs) => 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}"))
|
||||
.map_err(Into::into),
|
||||
}
|
||||
|
@ -1439,6 +1465,10 @@ impl Context {
|
|||
}
|
||||
Ok(TyParam::Array(new_tps))
|
||||
}
|
||||
TyParam::UnsizedArray(elem) => {
|
||||
let elem = self.eval_tp(*elem)?;
|
||||
Ok(TyParam::UnsizedArray(Box::new(elem)))
|
||||
}
|
||||
TyParam::Tuple(tps) => {
|
||||
let mut new_tps = Vec::with_capacity(tps.len());
|
||||
for tp in tps {
|
||||
|
@ -1484,7 +1514,7 @@ impl Context {
|
|||
}
|
||||
TyParam::ProjCall { obj, attr, args } => self.eval_proj_call(*obj, attr, args, &()),
|
||||
TyParam::Value(_) => Ok(p.clone()),
|
||||
_other => feature_error!(self, Location::Unknown, "???"),
|
||||
other => feature_error!(self, Location::Unknown, &format!("evaluating {other}")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1798,6 +1798,40 @@ impl Context {
|
|||
dict_.register_py_builtin(FUNC_GET, get_t, Some(FUNC_GET), 9);
|
||||
let copy_t = fn0_met(dict_t.clone(), dict_t.clone()).quantify();
|
||||
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 */
|
||||
let mut bytes = Self::builtin_mono_class(BYTES, 2);
|
||||
bytes.register_superclass(Obj, &obj);
|
||||
|
@ -2692,16 +2726,66 @@ impl Context {
|
|||
dict_mut_t.clone(),
|
||||
Some(poly(
|
||||
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,
|
||||
vec![],
|
||||
NoneType,
|
||||
)
|
||||
.quantify();
|
||||
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! */
|
||||
let set_mut_t = poly(MUT_SET, vec![ty_tp(T.clone()), N]);
|
||||
let mut set_mut_ =
|
||||
|
|
|
@ -363,6 +363,32 @@ pub(crate) fn dict_items(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
|
|||
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`
|
||||
pub(crate) fn array_union(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<TyParam> {
|
||||
let slf = args
|
||||
|
|
|
@ -54,6 +54,7 @@ const SELF: &str = "Self";
|
|||
const IMMUTIZABLE: &str = "Immutizable";
|
||||
const IMMUT_TYPE: &str = "ImmutType";
|
||||
const PROC_UPDATE: &str = "update!";
|
||||
const FUNC_UPDATE: &str = "update";
|
||||
const MUTIZABLE: &str = "Mutizable";
|
||||
const MUTABLE_MUT_TYPE: &str = "MutType!";
|
||||
const PATH_LIKE: &str = "PathLike";
|
||||
|
@ -232,8 +233,11 @@ const PROC_UPDATE_NTH: &str = "update_nth!";
|
|||
const FUNC_PARTITION: &str = "partition";
|
||||
const FUNC_DEDUP: &str = "dedup";
|
||||
const FUNC_CONCAT: &str = "concat";
|
||||
const FUNC_DIFF: &str = "diff";
|
||||
const FUNC_PUSH: &str = "push";
|
||||
const PROC_PUSH: &str = "push!";
|
||||
const FUNC_MERGE: &str = "merge";
|
||||
const PROC_MERGE: &str = "merge!";
|
||||
const ARRAY_ITERATOR: &str = "ArrayIterator";
|
||||
const GENERIC_SET: &str = "GenericSet";
|
||||
const SET: &str = "Set";
|
||||
|
@ -557,6 +561,7 @@ const KW_SUB: &str = "sub";
|
|||
const KW_OFFSET: &str = "offset";
|
||||
const KW_WHENCE: &str = "whence";
|
||||
const KW_CHARS: &str = "chars";
|
||||
const KW_OTHER: &str = "other";
|
||||
|
||||
pub fn builtins_path() -> PathBuf {
|
||||
erg_pystd_path().join("builtins.d.er")
|
||||
|
|
|
@ -4,4 +4,29 @@ dict = pyimport "Dict"
|
|||
.Dict!: ClassType
|
||||
.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
|
||||
|
|
|
@ -10,3 +10,13 @@
|
|||
items: |K, V|(self: .Dict(K, V)) -> .DictItems(K, V)
|
||||
keys: |K, V|(self: .Dict(K, V)) -> .DictKeys(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)
|
||||
|
|
|
@ -1,2 +1,18 @@
|
|||
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
|
||||
|
|
|
@ -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 {
|
||||
match self {
|
||||
TyParam::Value(ValueObj::Type(obj)) => {
|
||||
|
|
|
@ -494,7 +494,7 @@ impl TypeObj {
|
|||
}
|
||||
|
||||
/// 値オブジェクト
|
||||
/// コンパイル時評価ができ、シリアライズも可能
|
||||
/// コンパイル時評価ができ、シリアライズも可能(Typeなどはシリアライズ不可)
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub enum ValueObj {
|
||||
Int(i32),
|
||||
|
|
|
@ -2,3 +2,7 @@ for! {"a": 1, "b": 2}.keys(), s =>
|
|||
print! "key: " + s
|
||||
for! {"a": 1, "b": 2}.values(), i =>
|
||||
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 }
|
||||
|
|
|
@ -4,3 +4,11 @@ dic = !d
|
|||
dic.insert! "b", 2
|
||||
assert dic.get("a") == 1
|
||||
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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue