Skip to content

Commit

Permalink
Merge pull request #191 from GreasySlug/feature/implement-set
Browse files Browse the repository at this point in the history
Implement normal set literal
  • Loading branch information
mtshiba authored Oct 7, 2022
2 parents b2f7617 + 56c9765 commit db344a1
Show file tree
Hide file tree
Showing 30 changed files with 1,220 additions and 212 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* [x] Variable length arguments
* [x] Array literal
* [x] Record literal
* [ ] Set literal
* [x] Set literal
* [ ] Dict literal
* [x] Tuple literal
* [x] Variable visibility
Expand Down
18 changes: 18 additions & 0 deletions compiler/erg_compiler/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ fn is_fake_method(class: &str, name: &str) -> bool {
fn convert_to_python_attr(class: &str, uniq_obj_name: Option<&str>, name: Str) -> Str {
match (class, uniq_obj_name, &name[..]) {
("Array!", _, "push!") => Str::ever("append"),
("Set!", _, "add!") => Str::ever("add"),
("Complex" | "Float" | "Ratio" | "Int" | "Nat" | "Bool", _, "Real") => Str::ever("real"),
("Complex" | "Float" | "Ratio" | "Int" | "Nat" | "Bool", _, "Imag") => Str::ever("imag"),
("File!", _, "read!") => Str::ever("read"),
Expand Down Expand Up @@ -273,6 +274,7 @@ fn convert_to_python_name(name: Str) -> Str {
"Str" | "Str!" => Str::ever("str"),
"Bool" | "Bool!" => Str::ever("bool"),
"Array" | "Array!" => Str::ever("list"),
"Set" | "Set!" => Str::ever("set"),
_ => name,
}
}
Expand Down Expand Up @@ -1704,6 +1706,22 @@ impl CodeGenerator {
}
}
},
Expr::Set(set) => match set {
crate::hir::Set::Normal(mut set) => {
let len = set.elems.len();
while let Some(arg) = set.elems.try_remove_pos(0) {
self.emit_expr(arg.expr);
}
self.write_instr(BUILD_SET);
self.write_arg(len as u8);
if len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(len - 1);
}
}
crate::hir::Set::WithLength(_) => todo!(),
},
Expr::Record(rec) => self.emit_record(rec),
Expr::Code(code) => {
let code = self.emit_block(code, None, vec![]);
Expand Down
73 changes: 53 additions & 20 deletions compiler/erg_compiler/context/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl Context {
}
}
(TyParam::MonoQVar(name), _other) | (_other, TyParam::MonoQVar(name)) => {
log!(err "comparing '{name} and {_other}");
panic!("Not instantiated type parameter: {name}")
}
(TyParam::UnaryOp { op: lop, val: lval }, TyParam::UnaryOp { op: rop, val: rval }) => {
Expand Down Expand Up @@ -275,7 +276,7 @@ impl Context {
if !self.is_class(lhs) || !self.is_class(rhs) {
return (Maybe, false);
}
if let Some((_, ty_ctx)) = self.get_nominal_type_ctx(rhs) {
if let Some(ty_ctx) = self.get_nominal_type_ctx(rhs) {
for rhs_sup in ty_ctx.super_classes.iter() {
let rhs_sup = if rhs_sup.has_qvar() {
let rhs = match rhs {
Expand Down Expand Up @@ -313,7 +314,7 @@ impl Context {
if !self.is_trait(lhs) {
return (Maybe, false);
}
if let Some((_, rhs_ctx)) = self.get_nominal_type_ctx(rhs) {
if let Some(rhs_ctx) = self.get_nominal_type_ctx(rhs) {
for rhs_sup in rhs_ctx.super_traits.iter() {
let rhs_sup = if rhs_sup.has_qvar() {
let rhs = match rhs {
Expand Down Expand Up @@ -346,12 +347,12 @@ impl Context {
}

/// ```python
/// assert sup_conforms(?E(<: Eq(?E)), base: Nat, sup_trait: Eq(Nat))
/// assert sup_conforms(?E(<: Eq(?R)), base: T, sup_trait: Eq(U))
/// assert sup_conforms(?E(<: Eq(?E)), arg: Nat, sup_trait: Eq(Nat))
/// assert sup_conforms(?E(<: Eq(?R)), arg: T, sup_trait: Eq(U))
/// ```
fn sup_conforms(&self, free: &FreeTyVar, base: &Type, sup_trait: &Type) -> bool {
fn sup_conforms(&self, free: &FreeTyVar, arg: &Type, sup_trait: &Type) -> bool {
let (_sub, sup) = free.get_bound_types().unwrap();
free.forced_undoable_link(base);
free.forced_undoable_link(arg);
let judge = self.supertype_of(&sup, sup_trait);
free.undo();
judge
Expand Down Expand Up @@ -541,9 +542,17 @@ impl Context {
self.structural_supertype_of(re, &nat)
}
// Int :> {I: Int | ...} == true
// Real :> {I: Int | ...} == false
// Int :> {I: Str| ...} == false
(l, Refinement(r)) => self.supertype_of(l, &r.t),
// Eq({1, 2}) :> {1, 2} (= {I: Int | I == 1 or I == 2})
// => Eq(Int) :> Eq({1, 2}) :> {1, 2}
// => true
(l, Refinement(r)) => {
if self.supertype_of(l, &r.t) {
return true;
}
let l = l.derefine();
self.supertype_of(&l, &r.t)
}
// ({I: Int | True} :> Int) == true, ({N: Nat | ...} :> Int) == false, ({I: Int | I >= 0} :> Int) == false
(Refinement(l), r) => {
if l.preds.is_empty() {
Expand Down Expand Up @@ -625,6 +634,7 @@ impl Context {
}
self.poly_supertype_of(lhs, lparams, rparams)
}
// `Eq(Set(T, N)) :> Set(T, N)` will be false, such cases are judged by nominal_supertype_of
(
Poly {
path: lp,
Expand Down Expand Up @@ -673,22 +683,44 @@ impl Context {
}

pub(crate) fn cyclic_supertype_of(&self, lhs: &FreeTyVar, rhs: &Type) -> bool {
let ty_ctx = self.get_nominal_type_ctx(rhs).unwrap();
let subst_ctx = SubstContext::new(rhs, ty_ctx);
// if `rhs` is {S: Str | ... }, `defined_rhs` will be Str
let defined_rhs = if let Some((defined_rhs, _)) = self.get_nominal_type_ctx(rhs) {
defined_rhs
/*let defined_rhs = if let Some((defined_rhs, _ty_ctx)) = self.get_nominal_type_ctx(rhs) {
if defined_rhs.has_qvar() {
subst_ctx
.substitute(defined_rhs.clone(), self, Location::Unknown)
.unwrap()
} else {
defined_rhs.clone()
}
} else {
return false;
};
if let Some(super_traits) = self.get_nominal_super_trait_ctxs(rhs) {
for (sup_trait, _) in super_traits {
if self.sup_conforms(lhs, defined_rhs, sup_trait) {
};*/
if let Some(super_traits) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_traits) {
for sup_trait in super_traits {
let sup_trait = if sup_trait.has_qvar() {
subst_ctx
.substitute(sup_trait.clone(), self, Location::Unknown)
.unwrap()
} else {
sup_trait.clone()
};
if self.sup_conforms(lhs, rhs, &sup_trait) {
return true;
}
}
}
if let Some(sup_classes) = self.get_nominal_super_class_ctxs(rhs) {
for (sup_class, _) in sup_classes {
if self.cyclic_supertype_of(lhs, sup_class) {
if let Some(sup_classes) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_classes) {
for sup_class in sup_classes {
let sup_class = if sup_class.has_qvar() {
subst_ctx
.substitute(sup_class.clone(), self, Location::Unknown)
.unwrap()
} else {
sup_class.clone()
};
if self.cyclic_supertype_of(lhs, &sup_class) {
return true;
}
}
Expand All @@ -702,7 +734,7 @@ impl Context {
lparams: &[TyParam],
rparams: &[TyParam],
) -> bool {
let (_, ctx) = self
let ctx = self
.get_nominal_type_ctx(typ)
.unwrap_or_else(|| panic!("{typ} is not found"));
let variances = ctx.type_params_variance();
Expand Down Expand Up @@ -838,8 +870,9 @@ impl Context {

/// returns union of two types (A or B)
pub(crate) fn union(&self, lhs: &Type, rhs: &Type) -> Type {
// ?T or ?U will not be unified
if lhs.has_no_unbound_var() && rhs.has_no_unbound_var() {
// `?T or ?U` will not be unified
// `Set!(?T, 3) or Set(?T, 3)` wii be unified to Set(?T, 3)
if !lhs.is_unbound_var() && !rhs.is_unbound_var() {
match (self.supertype_of(lhs, rhs), self.subtype_of(lhs, rhs)) {
(true, true) => return lhs.clone(), // lhs = rhs
(true, false) => return lhs.clone(), // lhs :> rhs
Expand Down
33 changes: 24 additions & 9 deletions compiler/erg_compiler/context/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,13 @@ pub(crate) fn eval_lit(lit: &Literal) -> ValueObj {
ValueObj::from_str(t, lit.token.content.clone())
}

/// Instantiate the polymorphic type from the quantified state.
///
/// e.g.
/// ```
/// SubstContext::new(Array(?T, 0), Context(Array('T, 'N))) => SubstContext{ params: { 'T: ?T; 'N: 0 } } => ctx
/// ctx.substitute(Array!('T; !'N)): Array(?T, !0)
/// ```
#[derive(Debug)]
pub struct SubstContext {
bounds: Set<TyBound>,
Expand All @@ -125,6 +130,9 @@ impl fmt::Display for SubstContext {
}

impl SubstContext {
/// `substituted` is used to obtain real argument information. So it must be instantiated as `Array(?T, 0)` and so on.
///
/// `ty_ctx` is used to obtain information on the names and variance of the parameters.
pub fn new(substituted: &Type, ty_ctx: &Context) -> Self {
let bounds = ty_ctx.type_params_bounds();
let param_names = ty_ctx.params.iter().map(|(opt_name, _)| {
Expand All @@ -135,17 +143,22 @@ impl SubstContext {
if param_names.len() != substituted.typarams().len() {
let param_names = param_names.collect::<Vec<_>>();
panic!(
"{param_names:?} != {}",
"{param_names:?} != [{}]",
erg_common::fmt_vec(&substituted.typarams())
);
}
// REVIEW: 順番は保証されるか? 引数がunnamed_paramsに入る可能性は?
SubstContext {
bounds,
params: param_names
.zip(substituted.typarams().into_iter())
.collect(),
let params = param_names
.zip(substituted.typarams().into_iter())
.collect::<Dict<_, _>>();
if cfg!(feature = "debug") {
for v in params.values() {
if v.has_qvar() {
panic!("{} has qvar", v);
}
}
}
// REVIEW: 順番は保証されるか? 引数がunnamed_paramsに入る可能性は?
SubstContext { bounds, params }
}

pub fn substitute(&self, quant_t: Type, ctx: &Context, loc: Location) -> TyCheckResult<Type> {
Expand Down Expand Up @@ -294,7 +307,7 @@ impl Context {
}
if let ValueObj::Type(t) = &obj {
if let Some(sups) = self.get_nominal_super_type_ctxs(t.typ()) {
for (_, ctx) in sups {
for ctx in sups {
if let Some(val) = ctx.consts.get(ident.inspect()) {
return Ok(val.clone());
}
Expand Down Expand Up @@ -799,7 +812,7 @@ impl Context {
if sub == Type::Never {
return Ok(mono_proj(*lhs, rhs));
}
for (_ty, ty_ctx) in self.get_nominal_super_type_ctxs(&sub).ok_or_else(|| {
for ty_ctx in self.get_nominal_super_type_ctxs(&sub).ok_or_else(|| {
EvalError::no_var_error(
self.cfg.input.clone(),
line!() as usize,
Expand Down Expand Up @@ -1063,6 +1076,8 @@ impl Context {
true
}
}
(TyParam::Erased(t), _) => t.as_ref() == &self.get_tp_t(rhs).unwrap(),
(_, TyParam::Erased(t)) => t.as_ref() == &self.get_tp_t(lhs).unwrap(),
(TyParam::MonoQVar(_), _) | (_, TyParam::MonoQVar(_)) => false,
(l, r) => todo!("l: {l}, r: {r}"),
}
Expand Down
Loading

0 comments on commit db344a1

Please sign in to comment.