feat: refinement type specification

This commit is contained in:
Shunsuke Shibayama 2023-05-20 16:40:26 +09:00
parent b04429b3fd
commit 06001bfc13
15 changed files with 550 additions and 87 deletions

View file

@ -721,7 +721,7 @@ impl Context {
self.supertype_of(&lt, &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),
}
}

View file

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

View file

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

View file

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

View file

@ -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::*;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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