mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 18:58:30 +00:00
feat: refinement type specification
This commit is contained in:
parent
b04429b3fd
commit
06001bfc13
15 changed files with 550 additions and 87 deletions
|
@ -721,7 +721,7 @@ impl Context {
|
|||
self.supertype_of(<, &rt)
|
||||
&& self
|
||||
.try_cmp(&llen, &rlen)
|
||||
.map(|ord| ord.is_le())
|
||||
.map(|ord| ord.canbe_eq() || ord.canbe_lt())
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
self.poly_supertype_of(lhs, lparams, rparams)
|
||||
|
@ -842,6 +842,9 @@ impl Context {
|
|||
},
|
||||
(TyParam::Array(sup), TyParam::Array(sub))
|
||||
| (TyParam::Tuple(sup), TyParam::Tuple(sub)) => {
|
||||
if sup.len() > sub.len() {
|
||||
return false;
|
||||
}
|
||||
for (sup_p, sub_p) in sup.iter().zip(sub.iter()) {
|
||||
if !self.supertype_of_tp(sup_p, sub_p, variance) {
|
||||
return false;
|
||||
|
@ -868,48 +871,11 @@ impl Context {
|
|||
}
|
||||
true
|
||||
}
|
||||
(TyParam::Value(ValueObj::Dict(sup_d)), TyParam::Dict(sub_d)) => {
|
||||
if sup_d.len() > sub_d.len() {
|
||||
return false;
|
||||
}
|
||||
let sup_d = sup_d
|
||||
.iter()
|
||||
.map(|(k, v)| (TyParam::from(k.clone()), TyParam::from(v.clone())))
|
||||
.collect();
|
||||
self.supertype_of_tp(&TyParam::Dict(sup_d), sub_p, variance)
|
||||
}
|
||||
(TyParam::Dict(sup_d), TyParam::Value(ValueObj::Dict(sub_d))) => {
|
||||
if sup_d.len() > sub_d.len() {
|
||||
return false;
|
||||
}
|
||||
let sub_d = sub_d
|
||||
.iter()
|
||||
.map(|(k, v)| (TyParam::from(k.clone()), TyParam::from(v.clone())))
|
||||
.collect();
|
||||
self.supertype_of_tp(sup_p, &TyParam::Dict(sub_d), variance)
|
||||
}
|
||||
(TyParam::Type(sup), TyParam::Type(sub)) => match variance {
|
||||
Variance::Contravariant => self.subtype_of(sup, sub),
|
||||
Variance::Covariant => self.supertype_of(sup, sub),
|
||||
Variance::Invariant => self.same_type_of(sup, sub),
|
||||
},
|
||||
(TyParam::Type(sup), TyParam::Value(ValueObj::Type(sub))) => match variance {
|
||||
Variance::Contravariant => self.subtype_of(sup, sub.typ()),
|
||||
Variance::Covariant => self.supertype_of(sup, sub.typ()),
|
||||
Variance::Invariant => self.same_type_of(sup, sub.typ()),
|
||||
},
|
||||
(TyParam::Value(ValueObj::Type(sup)), TyParam::Type(sub)) => match variance {
|
||||
Variance::Contravariant => self.subtype_of(sup.typ(), sub),
|
||||
Variance::Covariant => self.supertype_of(sup.typ(), sub),
|
||||
Variance::Invariant => self.same_type_of(sup.typ(), sub),
|
||||
},
|
||||
(TyParam::Value(ValueObj::Type(sup)), TyParam::Value(ValueObj::Type(sub))) => {
|
||||
match variance {
|
||||
Variance::Contravariant => self.subtype_of(sup.typ(), sub.typ()),
|
||||
Variance::Covariant => self.supertype_of(sup.typ(), sub.typ()),
|
||||
Variance::Invariant => self.same_type_of(sup.typ(), sub.typ()),
|
||||
}
|
||||
}
|
||||
(TyParam::FreeVar(fv), _) if fv.is_unbound() => {
|
||||
let Some(fv_t) = fv.get_type() else {
|
||||
return false;
|
||||
|
@ -929,6 +895,20 @@ impl Context {
|
|||
self.same_type_of(&fv_t, &sub_t) || self.same_type_of(&fv_t, &sub_t.derefine())
|
||||
}
|
||||
}
|
||||
(TyParam::Value(sup), _) => {
|
||||
if let Ok(sup) = Self::convert_value_into_tp(sup.clone()) {
|
||||
self.supertype_of_tp(&sup, sub_p, variance)
|
||||
} else {
|
||||
self.eq_tp(sup_p, sub_p)
|
||||
}
|
||||
}
|
||||
(_, TyParam::Value(sub)) => {
|
||||
if let Ok(sub) = Self::convert_value_into_tp(sub.clone()) {
|
||||
self.supertype_of_tp(sup_p, &sub, variance)
|
||||
} else {
|
||||
self.eq_tp(sup_p, sub_p)
|
||||
}
|
||||
}
|
||||
_ => self.eq_tp(sup_p, sub_p),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ use erg_parser::ast::*;
|
|||
use erg_parser::token::{Token, TokenKind};
|
||||
|
||||
use crate::ty::constructors::{
|
||||
array_t, dict_t, mono, poly, proj, proj_call, ref_, ref_mut, refinement, subr_t, tuple_t,
|
||||
v_enum,
|
||||
array_t, dict_t, mono, poly, proj, proj_call, ref_, ref_mut, refinement, set_t, subr_t,
|
||||
tuple_t, v_enum,
|
||||
};
|
||||
use crate::ty::free::{FreeTyVar, HasLevel};
|
||||
use crate::ty::typaram::{OpKind, TyParam};
|
||||
|
@ -1003,6 +1003,13 @@ impl Context {
|
|||
}
|
||||
Ok(TyParam::Dict(new_dic))
|
||||
}
|
||||
TyParam::Set(set) => {
|
||||
let mut new_set = set! {};
|
||||
for v in set.into_iter() {
|
||||
new_set.insert(self.eval_tp(v)?);
|
||||
}
|
||||
Ok(TyParam::Set(new_set))
|
||||
}
|
||||
TyParam::Type(_) | TyParam::Erased(_) | TyParam::Value(_) => Ok(p.clone()),
|
||||
_other => feature_error!(self, Location::Unknown, "???"),
|
||||
}
|
||||
|
@ -1327,6 +1334,7 @@ impl Context {
|
|||
|
||||
pub(crate) fn convert_value_into_type(&self, val: ValueObj) -> Result<Type, ValueObj> {
|
||||
match val {
|
||||
ValueObj::Ellipsis => Ok(Type::Ellipsis),
|
||||
ValueObj::Type(t) => match t {
|
||||
TypeObj::Builtin { t, .. } => Ok(t),
|
||||
TypeObj::Generated(gen) => Ok(gen.into_typ()),
|
||||
|
@ -1345,11 +1353,72 @@ impl Context {
|
|||
}
|
||||
Ok(tuple_t(new_ts))
|
||||
}
|
||||
ValueObj::Array(arr) => {
|
||||
let len = TyParam::value(arr.len());
|
||||
let mut union = Type::Never;
|
||||
for v in arr.iter().cloned() {
|
||||
union = self.union(&union, &self.convert_value_into_type(v)?);
|
||||
}
|
||||
Ok(array_t(union, len))
|
||||
}
|
||||
ValueObj::Set(set) => {
|
||||
let len = TyParam::value(set.len());
|
||||
let mut union = Type::Never;
|
||||
for v in set.into_iter() {
|
||||
union = self.union(&union, &self.convert_value_into_type(v)?);
|
||||
}
|
||||
Ok(set_t(union, len))
|
||||
}
|
||||
ValueObj::Subr(subr) => subr.as_type(self).ok_or(ValueObj::Subr(subr)),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_value_into_tp(value: ValueObj) -> Result<TyParam, ValueObj> {
|
||||
match value {
|
||||
ValueObj::Type(t) => Ok(TyParam::t(t.into_typ())),
|
||||
ValueObj::Array(arr) => {
|
||||
let mut new_arr = vec![];
|
||||
for v in arr.iter().cloned() {
|
||||
new_arr.push(Self::convert_value_into_tp(v)?);
|
||||
}
|
||||
Ok(TyParam::Array(new_arr))
|
||||
}
|
||||
ValueObj::Tuple(ts) => {
|
||||
let mut new_ts = vec![];
|
||||
for v in ts.iter().cloned() {
|
||||
new_ts.push(Self::convert_value_into_tp(v)?);
|
||||
}
|
||||
Ok(TyParam::Tuple(new_ts))
|
||||
}
|
||||
ValueObj::Dict(dict) => {
|
||||
let mut new_dict = dict! {};
|
||||
for (k, v) in dict.into_iter() {
|
||||
new_dict.insert(
|
||||
Self::convert_value_into_tp(k)?,
|
||||
Self::convert_value_into_tp(v)?,
|
||||
);
|
||||
}
|
||||
Ok(TyParam::Dict(new_dict))
|
||||
}
|
||||
ValueObj::Set(set) => {
|
||||
let mut new_set = set! {};
|
||||
for v in set.into_iter() {
|
||||
new_set.insert(Self::convert_value_into_tp(v)?);
|
||||
}
|
||||
Ok(TyParam::Set(new_set))
|
||||
}
|
||||
ValueObj::Record(rec) => {
|
||||
let mut new_rec = dict! {};
|
||||
for (k, v) in rec.into_iter() {
|
||||
new_rec.insert(k, Self::convert_value_into_tp(v)?);
|
||||
}
|
||||
Ok(TyParam::Record(new_rec))
|
||||
}
|
||||
_ => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn _convert_type_to_dict_type(&self, ty: Type) -> Result<Dict<Type, Type>, ()> {
|
||||
match ty {
|
||||
Type::Poly { name, params } if &name[..] == "Dict" => {
|
||||
|
@ -1739,7 +1808,13 @@ impl Context {
|
|||
}
|
||||
|
||||
pub(crate) fn get_tp_t(&self, p: &TyParam) -> EvalResult<Type> {
|
||||
let p = self.eval_tp(p.clone())?;
|
||||
let p = self
|
||||
.eval_tp(p.clone())
|
||||
.map_err(|errs| {
|
||||
log!(err "{errs}");
|
||||
errs
|
||||
})
|
||||
.unwrap_or(p.clone());
|
||||
match p {
|
||||
TyParam::Value(v) => Ok(v_enum(set![v])),
|
||||
TyParam::Erased(t) => Ok((*t).clone()),
|
||||
|
@ -1808,15 +1883,44 @@ impl Context {
|
|||
}
|
||||
Ok(tuple_t(tps_t))
|
||||
}
|
||||
dict @ TyParam::Dict(_) => Ok(dict_t(dict)),
|
||||
TyParam::BinOp { op, lhs, rhs } => {
|
||||
let op_name = op_to_name(op);
|
||||
feature_error!(
|
||||
self,
|
||||
Location::Unknown,
|
||||
&format!("get type: {op_name}({lhs}, {rhs})")
|
||||
)
|
||||
TyParam::Set(tps) => {
|
||||
let len = TyParam::value(tps.len());
|
||||
let mut union = Type::Never;
|
||||
for tp in tps {
|
||||
let tp_t = self.get_tp_t(&tp)?;
|
||||
union = self.union(&union, &tp_t);
|
||||
}
|
||||
Ok(set_t(union, len))
|
||||
}
|
||||
dict @ TyParam::Dict(_) => Ok(dict_t(dict)),
|
||||
TyParam::BinOp { op, lhs, rhs } => match op {
|
||||
OpKind::Or | OpKind::And => {
|
||||
let lhs = self.get_tp_t(&lhs)?;
|
||||
let rhs = self.get_tp_t(&rhs)?;
|
||||
if self.subtype_of(&lhs, &Type::Bool) && self.subtype_of(&rhs, &Type::Bool) {
|
||||
Ok(Type::Bool)
|
||||
} else if self.subtype_of(&lhs, &Type::Type)
|
||||
&& self.subtype_of(&rhs, &Type::Type)
|
||||
{
|
||||
Ok(Type::Type)
|
||||
} else {
|
||||
let op_name = op_to_name(op);
|
||||
feature_error!(
|
||||
self,
|
||||
Location::Unknown,
|
||||
&format!("get type: {op_name}({lhs}, {rhs})")
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let op_name = op_to_name(op);
|
||||
feature_error!(
|
||||
self,
|
||||
Location::Unknown,
|
||||
&format!("get type: {op_name}({lhs}, {rhs})")
|
||||
)
|
||||
}
|
||||
},
|
||||
other => feature_error!(
|
||||
self,
|
||||
Location::Unknown,
|
||||
|
|
|
@ -11,7 +11,6 @@ use erg_common::traits::Locational;
|
|||
use erg_common::Str;
|
||||
use erg_parser::ast::VarName;
|
||||
|
||||
use crate::feature_error;
|
||||
use crate::ty::constructors::*;
|
||||
use crate::ty::free::{Constraint, HasLevel};
|
||||
use crate::ty::typaram::{TyParam, TyParamLambda};
|
||||
|
|
|
@ -10,15 +10,16 @@ use ast::{
|
|||
NonDefaultParamSignature, ParamTySpec, PolyTypeSpec, PreDeclTypeSpec, TypeBoundSpec,
|
||||
TypeBoundSpecs, TypeSpec,
|
||||
};
|
||||
use erg_parser::ast::{self, ConstArray, Identifier, VarName, VisModifierSpec, VisRestriction};
|
||||
use erg_parser::ast::{
|
||||
self, ConstArray, ConstSet, Identifier, VarName, VisModifierSpec, VisRestriction,
|
||||
};
|
||||
use erg_parser::token::TokenKind;
|
||||
use erg_parser::Parser;
|
||||
|
||||
use crate::feature_error;
|
||||
use crate::ty::free::{CanbeFree, Constraint};
|
||||
use crate::ty::typaram::{IntervalOp, OpKind, TyParam, TyParamLambda, TyParamOrdering};
|
||||
use crate::ty::value::ValueObj;
|
||||
use crate::ty::{constructors::*, VisibilityModifier};
|
||||
use crate::ty::{constructors::*, Predicate, RefinementType, VisibilityModifier};
|
||||
use crate::ty::{Field, HasType, ParamTy, SubrKind, SubrType, Type};
|
||||
use crate::type_feature_error;
|
||||
use TyParamOrdering::*;
|
||||
|
@ -864,7 +865,7 @@ impl Context {
|
|||
&format!("instantiating const expression {expr}")
|
||||
)
|
||||
}
|
||||
ast::ConstExpr::Set(set) => {
|
||||
ast::ConstExpr::Set(ConstSet::Normal(set)) => {
|
||||
let mut tp_set = set! {};
|
||||
for (i, elem) in set.elems.pos_args().enumerate() {
|
||||
let el = self.instantiate_const_expr(
|
||||
|
@ -877,6 +878,25 @@ impl Context {
|
|||
}
|
||||
Ok(TyParam::Set(tp_set))
|
||||
}
|
||||
ast::ConstExpr::Set(ConstSet::Comprehension(set)) => {
|
||||
if set.op.is(TokenKind::Colon) {
|
||||
let iter = self.instantiate_const_expr(
|
||||
&set.iter,
|
||||
erased_idx,
|
||||
tmp_tv_cache,
|
||||
not_found_is_qvar,
|
||||
)?;
|
||||
let pred = self.instantiate_pred(&set.pred, tmp_tv_cache)?;
|
||||
if let Ok(t) = self.instantiate_tp_as_type(iter, set) {
|
||||
return Ok(TyParam::t(refinement(set.var.inspect().clone(), t, pred)));
|
||||
}
|
||||
}
|
||||
type_feature_error!(
|
||||
self,
|
||||
set.loc(),
|
||||
&format!("instantiating const expression {expr}")
|
||||
)
|
||||
}
|
||||
ast::ConstExpr::Dict(dict) => {
|
||||
let mut tp_dict = dict! {};
|
||||
for (i, elem) in dict.kvs.iter().enumerate() {
|
||||
|
@ -1079,6 +1099,15 @@ impl Context {
|
|||
TyParam::Value(value) => self.convert_value_into_type(value).or_else(|value| {
|
||||
type_feature_error!(self, loc.loc(), &format!("instantiate `{value}` as type"))
|
||||
}),
|
||||
TyParam::Array(arr) => {
|
||||
let len = TyParam::value(arr.len());
|
||||
let mut union = Type::Never;
|
||||
for tp in arr {
|
||||
let t = self.instantiate_tp_as_type(tp, loc)?;
|
||||
union = self.union(&union, &t);
|
||||
}
|
||||
Ok(array_t(union, len))
|
||||
}
|
||||
TyParam::Set(set) => {
|
||||
let t = set
|
||||
.iter()
|
||||
|
@ -1115,6 +1144,23 @@ impl Context {
|
|||
);
|
||||
Ok(Type::Subr(subr))
|
||||
}
|
||||
TyParam::BinOp { op, lhs, rhs } => match op {
|
||||
OpKind::And => {
|
||||
let lhs = self.instantiate_tp_as_type(*lhs, loc)?;
|
||||
let rhs = self.instantiate_tp_as_type(*rhs, loc)?;
|
||||
Ok(lhs & rhs)
|
||||
}
|
||||
OpKind::Or => {
|
||||
let lhs = self.instantiate_tp_as_type(*lhs, loc)?;
|
||||
let rhs = self.instantiate_tp_as_type(*rhs, loc)?;
|
||||
Ok(lhs | rhs)
|
||||
}
|
||||
_ => type_feature_error!(
|
||||
self,
|
||||
loc.loc(),
|
||||
&format!("instantiate `{lhs} {op} {rhs}` as type")
|
||||
),
|
||||
},
|
||||
other => {
|
||||
type_feature_error!(self, loc.loc(), &format!("instantiate `{other}` as type"))
|
||||
}
|
||||
|
@ -1144,6 +1190,86 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn instantiate_pred(
|
||||
&self,
|
||||
expr: &ast::ConstExpr,
|
||||
_tmp_tv_cache: &mut TyVarCache,
|
||||
) -> TyCheckResult<Predicate> {
|
||||
match expr {
|
||||
ast::ConstExpr::Lit(lit) => {
|
||||
let value = self.eval_lit(lit)?;
|
||||
Ok(Predicate::Value(value))
|
||||
}
|
||||
ast::ConstExpr::Accessor(ast::ConstAccessor::Local(local)) => {
|
||||
Ok(Predicate::Const(local.inspect().clone()))
|
||||
}
|
||||
ast::ConstExpr::BinOp(bin) => {
|
||||
let lhs = self.instantiate_pred(&bin.lhs, _tmp_tv_cache)?;
|
||||
let rhs = self.instantiate_pred(&bin.rhs, _tmp_tv_cache)?;
|
||||
match bin.op.kind {
|
||||
TokenKind::DblEq
|
||||
| TokenKind::NotEq
|
||||
| TokenKind::Less
|
||||
| TokenKind::LessEq
|
||||
| TokenKind::Gre
|
||||
| TokenKind::GreEq => {
|
||||
let Predicate::Const(var) = lhs else {
|
||||
return type_feature_error!(
|
||||
self,
|
||||
bin.loc(),
|
||||
&format!("instantiating predicate `{expr}`")
|
||||
);
|
||||
};
|
||||
let rhs = match rhs {
|
||||
Predicate::Value(value) => TyParam::Value(value),
|
||||
Predicate::Const(var) => TyParam::Mono(var),
|
||||
_ => {
|
||||
return type_feature_error!(
|
||||
self,
|
||||
bin.loc(),
|
||||
&format!("instantiating predicate `{expr}`")
|
||||
);
|
||||
}
|
||||
};
|
||||
let pred = match bin.op.kind {
|
||||
TokenKind::DblEq => Predicate::eq(var, rhs),
|
||||
TokenKind::NotEq => Predicate::ne(var, rhs),
|
||||
TokenKind::Less => Predicate::lt(var, rhs),
|
||||
TokenKind::LessEq => Predicate::le(var, rhs),
|
||||
TokenKind::Gre => Predicate::gt(var, rhs),
|
||||
TokenKind::GreEq => Predicate::ge(var, rhs),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(pred)
|
||||
}
|
||||
TokenKind::OrOp => Ok(lhs | rhs),
|
||||
TokenKind::AndOp => Ok(lhs & rhs),
|
||||
_ => type_feature_error!(
|
||||
self,
|
||||
bin.loc(),
|
||||
&format!("instantiating predicate `{expr}`")
|
||||
),
|
||||
}
|
||||
}
|
||||
ast::ConstExpr::UnaryOp(unop) => {
|
||||
let pred = self.instantiate_pred(&unop.expr, _tmp_tv_cache)?;
|
||||
match unop.op.kind {
|
||||
TokenKind::PreBitNot => Ok(!pred),
|
||||
_ => type_feature_error!(
|
||||
self,
|
||||
unop.loc(),
|
||||
&format!("instantiating predicate `{expr}`")
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => type_feature_error!(
|
||||
self,
|
||||
expr.loc(),
|
||||
&format!("instantiating predicate `{expr}`")
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn instantiate_typespec_full(
|
||||
&self,
|
||||
t_spec: &TypeSpec,
|
||||
|
@ -1371,6 +1497,19 @@ impl Context {
|
|||
&format!("instantiating type spec {spec}{args}")
|
||||
)
|
||||
}
|
||||
TypeSpec::Refinement(refine) => {
|
||||
let t = self.instantiate_typespec_full(
|
||||
&refine.typ,
|
||||
opt_decl_t,
|
||||
tmp_tv_cache,
|
||||
mode,
|
||||
not_found_is_qvar,
|
||||
)?;
|
||||
let pred = self.instantiate_pred(&refine.pred, tmp_tv_cache)?;
|
||||
let refine =
|
||||
Type::Refinement(RefinementType::new(refine.var.inspect().clone(), t, pred));
|
||||
Ok(refine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1544,7 @@ impl Context {
|
|||
let mut namespaces = set! {};
|
||||
for ns in namespace.iter() {
|
||||
let ast::Accessor::Ident(ident) = ns else {
|
||||
return feature_error!(TyCheckErrors, TyCheckError, self, ns.loc(), "namespace accessors");
|
||||
return type_feature_error!(self, ns.loc(), "namespace accessors");
|
||||
};
|
||||
let vi = self
|
||||
.rec_get_var_info(ident, AccessKind::Name, &self.cfg.input, self)
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::ty::{Predicate, SubrType, Type};
|
|||
|
||||
use crate::context::{Context, Variance};
|
||||
use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult};
|
||||
use crate::{feature_error, type_feature_error};
|
||||
use crate::type_feature_error;
|
||||
|
||||
use Predicate as Pred;
|
||||
use Type::*;
|
||||
|
|
|
@ -335,6 +335,13 @@ impl ASTLowerer {
|
|||
elem,
|
||||
)))
|
||||
}
|
||||
// TODO:
|
||||
ast::Set::Comprehension(set) => Ok(hir::Set::Normal(hir::NormalSet::new(
|
||||
set.l_brace,
|
||||
set.r_brace,
|
||||
Type::Failure,
|
||||
hir::Args::empty(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,10 +60,10 @@ macro_rules! feature_error {
|
|||
#[macro_export]
|
||||
macro_rules! type_feature_error {
|
||||
($ctx: expr, $loc: expr, $name: expr) => {
|
||||
feature_error!(TyCheckErrors, TyCheckError, $ctx, $loc, $name)
|
||||
$crate::feature_error!(TyCheckErrors, TyCheckError, $ctx, $loc, $name)
|
||||
};
|
||||
(error $ctx: expr, $loc: expr, $name: expr) => {
|
||||
feature_error!(TyCheckError, $ctx, $loc, $name)
|
||||
$crate::feature_error!(TyCheckError, $ctx, $loc, $name)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -398,6 +398,13 @@ impl ASTLowerer {
|
|||
match set {
|
||||
ast::Set::Normal(set) => Ok(hir::Set::Normal(self.lower_normal_set(set)?)),
|
||||
ast::Set::WithLength(set) => Ok(hir::Set::WithLength(self.lower_set_with_length(set)?)),
|
||||
ast::Set::Comprehension(set) => feature_error!(
|
||||
LowerErrors,
|
||||
LowerError,
|
||||
self.module.context,
|
||||
set.loc(),
|
||||
"set comprehension"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1065,16 +1065,59 @@ impl SetWithLength {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SetComprehension {
|
||||
pub l_brace: Token,
|
||||
pub r_brace: Token,
|
||||
pub var: Token,
|
||||
pub op: Token, // <- or :
|
||||
pub iter: Box<Expr>,
|
||||
pub pred: Box<Expr>,
|
||||
}
|
||||
|
||||
impl NestedDisplay for SetComprehension {
|
||||
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{{{} {} {} | {}}}",
|
||||
self.var, self.op, self.iter, self.pred
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl_display_from_nested!(SetComprehension);
|
||||
impl_locational!(SetComprehension, l_brace, r_brace);
|
||||
|
||||
impl SetComprehension {
|
||||
pub fn new(
|
||||
l_brace: Token,
|
||||
r_brace: Token,
|
||||
var: Token,
|
||||
op: Token,
|
||||
iter: Expr,
|
||||
pred: Expr,
|
||||
) -> Self {
|
||||
Self {
|
||||
l_brace,
|
||||
r_brace,
|
||||
var,
|
||||
op,
|
||||
iter: Box::new(iter),
|
||||
pred: Box::new(pred),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Set {
|
||||
Normal(NormalSet),
|
||||
WithLength(SetWithLength),
|
||||
// Comprehension(SetComprehension),
|
||||
Comprehension(SetComprehension),
|
||||
}
|
||||
|
||||
impl_nested_display_for_enum!(Set; Normal, WithLength);
|
||||
impl_display_for_enum!(Set; Normal, WithLength);
|
||||
impl_locational_for_enum!(Set; Normal, WithLength);
|
||||
impl_nested_display_for_enum!(Set; Normal, WithLength, Comprehension);
|
||||
impl_display_for_enum!(Set; Normal, WithLength, Comprehension);
|
||||
impl_locational_for_enum!(Set; Normal, WithLength, Comprehension);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct BinOp {
|
||||
|
@ -1566,22 +1609,22 @@ impl ConstArrayWithLength {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ConstSet {
|
||||
pub struct ConstNormalSet {
|
||||
pub l_brace: Token,
|
||||
pub r_brace: Token,
|
||||
pub elems: ConstArgs,
|
||||
}
|
||||
|
||||
impl NestedDisplay for ConstSet {
|
||||
impl NestedDisplay for ConstNormalSet {
|
||||
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
|
||||
write!(f, "{{{}}}", self.elems)
|
||||
}
|
||||
}
|
||||
|
||||
impl_display_from_nested!(ConstSet);
|
||||
impl_locational!(ConstSet, l_brace, elems, r_brace);
|
||||
impl_display_from_nested!(ConstNormalSet);
|
||||
impl_locational!(ConstNormalSet, l_brace, elems, r_brace);
|
||||
|
||||
impl ConstSet {
|
||||
impl ConstNormalSet {
|
||||
pub fn new(l_brace: Token, r_brace: Token, elems: ConstArgs) -> Self {
|
||||
Self {
|
||||
l_brace,
|
||||
|
@ -1590,12 +1633,81 @@ impl ConstSet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> Set {
|
||||
Set::Normal(NormalSet::new(
|
||||
pub fn downgrade(self) -> NormalSet {
|
||||
NormalSet::new(self.l_brace, self.r_brace, self.elems.downgrade())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ConstSetComprehension {
|
||||
pub l_brace: Token,
|
||||
pub r_brace: Token,
|
||||
pub var: Token,
|
||||
pub op: Token,
|
||||
pub iter: Box<ConstExpr>,
|
||||
pub pred: Box<ConstExpr>,
|
||||
}
|
||||
|
||||
impl NestedDisplay for ConstSetComprehension {
|
||||
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{{{} {} {} | {}}}",
|
||||
self.var, self.op, self.iter, self.pred
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl_display_from_nested!(ConstSetComprehension);
|
||||
impl_locational!(ConstSetComprehension, l_brace, var, r_brace);
|
||||
|
||||
impl ConstSetComprehension {
|
||||
pub fn new(
|
||||
l_brace: Token,
|
||||
r_brace: Token,
|
||||
var: Token,
|
||||
op: Token,
|
||||
iter: ConstExpr,
|
||||
pred: ConstExpr,
|
||||
) -> Self {
|
||||
Self {
|
||||
l_brace,
|
||||
r_brace,
|
||||
var,
|
||||
op,
|
||||
iter: Box::new(iter),
|
||||
pred: Box::new(pred),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> SetComprehension {
|
||||
SetComprehension::new(
|
||||
self.l_brace,
|
||||
self.r_brace,
|
||||
self.elems.downgrade(),
|
||||
))
|
||||
self.var,
|
||||
self.op,
|
||||
self.iter.downgrade(),
|
||||
self.pred.downgrade(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ConstSet {
|
||||
Normal(ConstNormalSet),
|
||||
Comprehension(ConstSetComprehension),
|
||||
}
|
||||
|
||||
impl_nested_display_for_enum!(ConstSet; Normal, Comprehension);
|
||||
impl_display_from_nested!(ConstSet);
|
||||
impl_locational_for_enum!(ConstSet; Normal, Comprehension);
|
||||
|
||||
impl ConstSet {
|
||||
pub fn downgrade(self) -> Set {
|
||||
match self {
|
||||
Self::Normal(normal) => Set::Normal(normal.downgrade()),
|
||||
Self::Comprehension(comp) => Set::Comprehension(comp.downgrade()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2454,6 +2566,35 @@ impl TupleTypeSpec {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RefinementTypeSpec {
|
||||
pub var: Token,
|
||||
pub typ: Box<TypeSpec>,
|
||||
pub pred: ConstExpr,
|
||||
}
|
||||
|
||||
impl fmt::Display for RefinementTypeSpec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{{{}: {} | {}}}", self.var, self.typ, self.pred)
|
||||
}
|
||||
}
|
||||
|
||||
impl Locational for RefinementTypeSpec {
|
||||
fn loc(&self) -> Location {
|
||||
Location::concat(&self.var, &self.pred)
|
||||
}
|
||||
}
|
||||
|
||||
impl RefinementTypeSpec {
|
||||
pub fn new(var: Token, typ: TypeSpec, pred: ConstExpr) -> Self {
|
||||
Self {
|
||||
var,
|
||||
typ: Box::new(typ),
|
||||
pred,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// * Array: `[Int; 3]`, `[Int, Ratio, Complex]`, etc.
|
||||
/// * Dict: `[Str: Str]`, etc.
|
||||
/// * And (Intersection type): Add and Sub and Mul (== Num), etc.
|
||||
|
@ -2464,6 +2605,7 @@ impl TupleTypeSpec {
|
|||
/// * Record: {.into_s: Self.() -> Str }, etc.
|
||||
/// * Subr: Int -> Int, Int => None, T.(X) -> Int, etc.
|
||||
/// * TypeApp: F|...|
|
||||
/// * Refinement: {I: Int | I >= 0}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TypeSpec {
|
||||
Infer(Token),
|
||||
|
@ -2484,12 +2626,12 @@ pub enum TypeSpec {
|
|||
lhs: ConstExpr,
|
||||
rhs: ConstExpr,
|
||||
},
|
||||
// Record(),
|
||||
Subr(SubrTypeSpec),
|
||||
TypeApp {
|
||||
spec: Box<TypeSpec>,
|
||||
args: TypeAppArgs,
|
||||
},
|
||||
Refinement(RefinementTypeSpec),
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeSpec {
|
||||
|
@ -2527,6 +2669,7 @@ impl fmt::Display for TypeSpec {
|
|||
Self::Interval { op, lhs, rhs } => write!(f, "{lhs}{}{rhs}", op.inspect()),
|
||||
Self::Subr(s) => write!(f, "{s}"),
|
||||
Self::TypeApp { spec, args } => write!(f, "{spec}{args}"),
|
||||
Self::Refinement(r) => write!(f, "{r}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2549,6 +2692,7 @@ impl Locational for TypeSpec {
|
|||
Self::Interval { lhs, rhs, .. } => Location::concat(lhs, rhs),
|
||||
Self::Subr(s) => s.loc(),
|
||||
Self::TypeApp { spec, args } => Location::concat(spec.as_ref(), args),
|
||||
Self::Refinement(r) => r.loc(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ use crate::ast::{
|
|||
Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, MixedRecord, Module,
|
||||
NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple,
|
||||
ParamPattern, ParamRecordAttr, ParamTuplePattern, Params, PatchDef, PosArg, ReDef, Record,
|
||||
RecordAttrOrIdent, RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple,
|
||||
TupleTypeSpec, TypeAppArgs, TypeAppArgsKind, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp,
|
||||
VarName, VarPattern, VarRecordAttr, VarSignature, VisModifierSpec,
|
||||
RecordAttrOrIdent, RecordAttrs, Set as astSet, SetComprehension, SetWithLength, Signature,
|
||||
SubrSignature, Tuple, TupleTypeSpec, TypeAppArgs, TypeAppArgsKind, TypeBoundSpecs, TypeSpec,
|
||||
TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr, VarSignature, VisModifierSpec,
|
||||
};
|
||||
use crate::token::{Token, TokenKind, COLON, DOT};
|
||||
|
||||
|
@ -207,6 +207,19 @@ impl Desugarer {
|
|||
let set = SetWithLength::new(set.l_brace, set.r_brace, elem, len);
|
||||
Expr::Set(astSet::WithLength(set))
|
||||
}
|
||||
astSet::Comprehension(set) => {
|
||||
let iter = desugar(*set.iter);
|
||||
let pred = desugar(*set.pred);
|
||||
let set = SetComprehension::new(
|
||||
set.l_brace,
|
||||
set.r_brace,
|
||||
set.var,
|
||||
set.op,
|
||||
iter,
|
||||
pred,
|
||||
);
|
||||
Expr::Set(astSet::Comprehension(set))
|
||||
}
|
||||
},
|
||||
Expr::Dict(dict) => match dict {
|
||||
Dict::Normal(dic) => {
|
||||
|
|
|
@ -2162,11 +2162,11 @@ impl Parser {
|
|||
}
|
||||
// Dict
|
||||
other if self.cur_is(Colon) => {
|
||||
let dict = self
|
||||
.try_reduce_normal_dict(l_brace, other)
|
||||
let res = self
|
||||
.try_reduce_normal_dict_or_set_comp(l_brace, other)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
debug_exit_info!(self);
|
||||
Ok(BraceContainer::Dict(Dict::Normal(dict)))
|
||||
Ok(res)
|
||||
}
|
||||
other => {
|
||||
let set = self
|
||||
|
@ -2247,17 +2247,48 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_reduce_normal_dict_or_set_comp(
|
||||
&mut self,
|
||||
l_brace: Token,
|
||||
lhs: Expr,
|
||||
) -> ParseResult<BraceContainer> {
|
||||
debug_call_info!(self);
|
||||
let colon = expect_pop!(self, fail_next Colon);
|
||||
let rhs = self
|
||||
.try_reduce_expr(false, true, false, false)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
if self.cur_is(VBar) {
|
||||
self.skip();
|
||||
let Expr::Accessor(Accessor::Ident(var)) = lhs else {
|
||||
let err = ParseError::simple_syntax_error(line!() as usize, lhs.loc());
|
||||
self.errs.push(err);
|
||||
debug_exit_info!(self);
|
||||
return Err(());
|
||||
};
|
||||
let pred = self
|
||||
.try_reduce_chunk(false, false)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
let r_brace = expect_pop!(self, fail_next RBrace);
|
||||
let set_comp =
|
||||
SetComprehension::new(l_brace, r_brace, var.name.into_token(), colon, rhs, pred);
|
||||
debug_exit_info!(self);
|
||||
Ok(BraceContainer::Set(Set::Comprehension(set_comp)))
|
||||
} else {
|
||||
let dict = self
|
||||
.try_reduce_normal_dict(l_brace, lhs, rhs)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
debug_exit_info!(self);
|
||||
Ok(BraceContainer::Dict(Dict::Normal(dict)))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_reduce_normal_dict(
|
||||
&mut self,
|
||||
l_brace: Token,
|
||||
first_key: Expr,
|
||||
value: Expr,
|
||||
) -> ParseResult<NormalDict> {
|
||||
debug_call_info!(self);
|
||||
assert!(self.cur_is(Colon));
|
||||
self.skip();
|
||||
let value = self
|
||||
.try_reduce_chunk(false, false)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
let mut kvs = vec![KeyValue::new(first_key, value)];
|
||||
loop {
|
||||
match self.peek_kind() {
|
||||
|
|
|
@ -59,13 +59,26 @@ impl Parser {
|
|||
const_elems.push(ConstPosArg::new(const_expr));
|
||||
}
|
||||
let elems = ConstArgs::pos_only(const_elems, None);
|
||||
let const_set = ConstSet::new(set.l_brace, set.r_brace, elems);
|
||||
Ok(ConstExpr::Set(const_set))
|
||||
let const_set = ConstNormalSet::new(set.l_brace, set.r_brace, elems);
|
||||
Ok(ConstExpr::Set(ConstSet::Normal(const_set)))
|
||||
}
|
||||
Set::Comprehension(set) => {
|
||||
let iter = Self::validate_const_expr(*set.iter)?;
|
||||
let pred = Self::validate_const_expr(*set.pred)?;
|
||||
let const_set_comp = ConstSetComprehension::new(
|
||||
set.l_brace,
|
||||
set.r_brace,
|
||||
set.var,
|
||||
set.op,
|
||||
iter,
|
||||
pred,
|
||||
);
|
||||
Ok(ConstExpr::Set(ConstSet::Comprehension(const_set_comp)))
|
||||
}
|
||||
other => Err(ParseError::feature_error(
|
||||
line!() as usize,
|
||||
other.loc(),
|
||||
"const set comprehension",
|
||||
"const set with length",
|
||||
)),
|
||||
},
|
||||
Expr::Dict(dict) => match dict {
|
||||
|
@ -349,6 +362,16 @@ impl Parser {
|
|||
let len = Self::validate_const_expr(*set.len)?;
|
||||
Ok(TypeSpec::SetWithLen(SetWithLenTypeSpec::new(t_spec, len)))
|
||||
}
|
||||
Set::Comprehension(set) => {
|
||||
if set.op.is(TokenKind::Colon) {
|
||||
let typ = Self::expr_to_type_spec(*set.iter)?;
|
||||
let pred = Self::validate_const_expr(*set.pred)?;
|
||||
let refine = RefinementTypeSpec::new(set.var, typ, pred);
|
||||
Ok(TypeSpec::Refinement(refine))
|
||||
} else {
|
||||
Err(ParseError::simple_syntax_error(line!() as usize, set.loc()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
tests/should_err/tuple.er
Normal file
8
tests/should_err/tuple.er
Normal file
|
@ -0,0 +1,8 @@
|
|||
t0 _: Tuple([]) = None
|
||||
t1 _: Tuple([Int]) = None
|
||||
|
||||
_ = t0(()) # OK
|
||||
_ = t0((1,)) # OK
|
||||
_ = t1(()) # ERR
|
||||
_ = t1((1,)) # OK
|
||||
_ = t1((1, 2)) # OK
|
|
@ -1,3 +1,6 @@
|
|||
x: {1, 2, 3} = 1
|
||||
id|T: Type|(x: T): T = x
|
||||
add|R: Type, A <: Add(R)|(x: A, y: R): A.Output = x + y
|
||||
|
||||
f _: Tuple([{B: Bool | B == False} or Str, Int]) = None
|
||||
g _: Tuple([]) = None
|
||||
|
|
|
@ -8,7 +8,7 @@ fn exec_addition_ok() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_advanced_type_spec() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/advanced_type_spec.er", 3)
|
||||
expect_success("tests/should_ok/advanced_type_spec.er", 5)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -332,6 +332,11 @@ fn exec_subtyping_err() -> Result<(), ()> {
|
|||
expect_failure("tests/should_err/subtyping.er", 0, 13)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_tuple_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/tuple.er", 0, 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_callable() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/callable.er", 0, 6)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue