mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-27 19:59:07 +00:00
feat: support refinement class
This commit is contained in:
parent
c72de02c2c
commit
678c02faf9
9 changed files with 94 additions and 4 deletions
|
@ -536,6 +536,9 @@ impl Context {
|
|||
(Mono(n), NamedTuple(_)) => &n[..] == "GenericNamedTuple" || &n[..] == "GenericTuple",
|
||||
(Mono(n), Record(_)) => &n[..] == "Record",
|
||||
(Type, Subr(subr)) => self.supertype_of(&Type, &subr.return_t),
|
||||
(Type, Poly { name, params }) if &name[..] == "Set" => {
|
||||
self.convert_tp_into_value(params[0].clone()).is_ok()
|
||||
}
|
||||
(Type, Poly { name, params })
|
||||
if &name[..] == "Array" || &name[..] == "UnsizedArray" || &name[..] == "Set" =>
|
||||
{
|
||||
|
|
|
@ -20,8 +20,9 @@ use erg_parser::desugar::Desugarer;
|
|||
use erg_parser::token::{Token, TokenKind};
|
||||
|
||||
use crate::ty::constructors::{
|
||||
array_t, bounded, dict_t, mono, mono_q, named_free_var, poly, proj, proj_call, ref_, ref_mut,
|
||||
refinement, set_t, subr_t, subtypeof, tp_enum, tuple_t, unknown_len_array_t, v_enum,
|
||||
array_t, bounded, closed_range, dict_t, mono, mono_q, named_free_var, poly, proj, proj_call,
|
||||
ref_, ref_mut, refinement, set_t, subr_t, subtypeof, tp_enum, tuple_t, unknown_len_array_t,
|
||||
v_enum,
|
||||
};
|
||||
use crate::ty::free::HasLevel;
|
||||
use crate::ty::typaram::{OpKind, TyParam};
|
||||
|
@ -93,6 +94,10 @@ fn op_to_name(op: OpKind) -> &'static str {
|
|||
OpKind::BitXor => "__bitxor__",
|
||||
OpKind::Shl => "__shl__",
|
||||
OpKind::Shr => "__shr__",
|
||||
OpKind::ClosedRange => "__rng__",
|
||||
OpKind::LeftOpenRange => "__lorng__",
|
||||
OpKind::RightOpenRange => "__rorng__",
|
||||
OpKind::OpenRange => "__orng__",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,6 +418,10 @@ impl Context {
|
|||
TokenKind::PrePlus => Ok(OpKind::Pos),
|
||||
TokenKind::PreMinus => Ok(OpKind::Neg),
|
||||
TokenKind::PreBitNot => Ok(OpKind::Invert),
|
||||
TokenKind::Closed => Ok(OpKind::ClosedRange),
|
||||
TokenKind::LeftOpen => Ok(OpKind::LeftOpenRange),
|
||||
TokenKind::RightOpen => Ok(OpKind::RightOpenRange),
|
||||
TokenKind::Open => Ok(OpKind::OpenRange),
|
||||
_other => Err(EvalErrors::from(EvalError::not_const_expr(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -1134,6 +1143,7 @@ impl Context {
|
|||
line!(),
|
||||
))),
|
||||
},
|
||||
ClosedRange => Ok(ValueObj::range(lhs, rhs)),
|
||||
_other => Err(EvalErrors::from(EvalError::unreachable(
|
||||
self.cfg.input.clone(),
|
||||
fn_name!(),
|
||||
|
@ -2014,6 +2024,11 @@ impl Context {
|
|||
Ok(dict_t(TyParam::Dict(dic)))
|
||||
}
|
||||
ValueObj::Subr(subr) => subr.as_type(self).ok_or(ValueObj::Subr(subr)),
|
||||
ValueObj::DataClass { name, fields } if &name == "Range" => {
|
||||
let start = fields["start"].clone();
|
||||
let end = fields["end"].clone();
|
||||
Ok(closed_range(start.class(), start, end))
|
||||
}
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,9 @@
|
|||
__eq__ self, other: .SemVer =
|
||||
self.major == other.major and self.minor == other.minor and self.patch == other.patch and self.pre == other.pre
|
||||
|
||||
.SemVerPrefix = Class { "~", "==", "<", ">", "<=", "<=", }
|
||||
.SemVerSpec = Class { .prefix = .SemVerPrefix; .version = .SemVer }
|
||||
|
||||
if! __name__ == "__main__", do!:
|
||||
v = .SemVer.new(0, 0, 1)
|
||||
assert v.minor == 0
|
||||
|
|
|
@ -133,6 +133,28 @@ pub fn singleton(ty: Type, tp: TyParam) -> Type {
|
|||
|
||||
#[inline]
|
||||
pub fn int_interval<P, PErr, Q, QErr>(op: IntervalOp, l: P, r: Q) -> Type
|
||||
where
|
||||
P: TryInto<TyParam, Error = PErr>,
|
||||
PErr: fmt::Debug,
|
||||
Q: TryInto<TyParam, Error = QErr>,
|
||||
QErr: fmt::Debug,
|
||||
{
|
||||
interval(op, Type::Int, l, r)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn closed_range<P, PErr, Q, QErr>(t: Type, l: P, r: Q) -> Type
|
||||
where
|
||||
P: TryInto<TyParam, Error = PErr>,
|
||||
PErr: fmt::Debug,
|
||||
Q: TryInto<TyParam, Error = QErr>,
|
||||
QErr: fmt::Debug,
|
||||
{
|
||||
interval(IntervalOp::Closed, t, l, r)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interval<P, PErr, Q, QErr>(op: IntervalOp, t: Type, l: P, r: Q) -> Type
|
||||
where
|
||||
P: TryInto<TyParam, Error = PErr>,
|
||||
PErr: fmt::Debug,
|
||||
|
@ -161,7 +183,7 @@ where
|
|||
Predicate::le(name.clone(), r),
|
||||
),
|
||||
IntervalOp::Open if l == TyParam::value(NegInf) && r == TyParam::value(Inf) => {
|
||||
return refinement(name, Type::Int, Predicate::TRUE)
|
||||
return refinement(name, t, Predicate::TRUE)
|
||||
}
|
||||
// l<..<r => {I: classof(l) | I >= l+ε and I <= r-ε}
|
||||
IntervalOp::Open => Predicate::and(
|
||||
|
@ -169,7 +191,7 @@ where
|
|||
Predicate::le(name.clone(), TyParam::pred(r)),
|
||||
),
|
||||
};
|
||||
refinement(name, Type::Int, pred)
|
||||
refinement(name, t, pred)
|
||||
}
|
||||
|
||||
pub fn iter(t: Type) -> Type {
|
||||
|
|
|
@ -49,6 +49,10 @@ pub enum OpKind {
|
|||
BitXor,
|
||||
Shl,
|
||||
Shr,
|
||||
ClosedRange,
|
||||
LeftOpenRange,
|
||||
RightOpenRange,
|
||||
OpenRange,
|
||||
}
|
||||
|
||||
impl fmt::Display for OpKind {
|
||||
|
@ -79,6 +83,10 @@ impl fmt::Display for OpKind {
|
|||
Self::BitXor => write!(f, "^^"),
|
||||
Self::Shl => write!(f, "<<"),
|
||||
Self::Shr => write!(f, ">>"),
|
||||
Self::ClosedRange => write!(f, ".."),
|
||||
Self::LeftOpenRange => write!(f, "<.."),
|
||||
Self::RightOpenRange => write!(f, "..<"),
|
||||
Self::OpenRange => write!(f, "<..<"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +117,10 @@ impl TryFrom<TokenKind> for OpKind {
|
|||
TokenKind::BitXor => Ok(Self::BitXor),
|
||||
TokenKind::Shl => Ok(Self::Shl),
|
||||
TokenKind::Shr => Ok(Self::Shr),
|
||||
TokenKind::Closed => Ok(Self::ClosedRange),
|
||||
TokenKind::LeftOpen => Ok(Self::LeftOpenRange),
|
||||
TokenKind::RightOpen => Ok(Self::RightOpenRange),
|
||||
TokenKind::Open => Ok(Self::OpenRange),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -955,6 +955,7 @@ impl ValueObj {
|
|||
ValueObj::Type(TypeObj::Generated(gen))
|
||||
}
|
||||
|
||||
/// closed range (..)
|
||||
pub fn range(start: Self, end: Self) -> Self {
|
||||
Self::DataClass {
|
||||
name: "Range".into(),
|
||||
|
|
7
tests/should_err/refinement_class.er
Normal file
7
tests/should_err/refinement_class.er
Normal file
|
@ -0,0 +1,7 @@
|
|||
Suite = Class { "Heart", "Diamond", "Spade", "Club" }
|
||||
|
||||
_ = Suite.new "Invalid" # ERR
|
||||
|
||||
Month = Class 1..12
|
||||
|
||||
_ = Month.new 13 # ERR
|
17
tests/should_ok/refinement_class.er
Normal file
17
tests/should_ok/refinement_class.er
Normal file
|
@ -0,0 +1,17 @@
|
|||
Suite = Class { "Heart", "Diamond", "Spade", "Club" }
|
||||
Suite.
|
||||
is_heart self = self::base == "Heart"
|
||||
|
||||
h = Suite.new "Heart"
|
||||
d = Suite.new "Diamond"
|
||||
assert h.is_heart()
|
||||
assert not d.is_heart()
|
||||
|
||||
Month = Class 1..12
|
||||
Month.
|
||||
is_first_half self = self::base <= 6
|
||||
|
||||
jan = Month.new 1
|
||||
dec = Month.new 12
|
||||
assert jan.is_first_half()
|
||||
assert not dec.is_first_half()
|
|
@ -327,6 +327,11 @@ fn exec_refinement() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/refinement.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_refinement_class() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/refinement_class.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_return() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/return.er", 0)
|
||||
|
@ -603,6 +608,11 @@ fn exec_refinement_err() -> Result<(), ()> {
|
|||
expect_failure("tests/should_err/refinement.er", 0, 8)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_refinement_class_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/refinement_class.er", 0, 2)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_var_args_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/var_args.er", 0, 3)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue