mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +00:00
feat: support var-args lambda function
This commit is contained in:
parent
cb00efca54
commit
3d310714f1
13 changed files with 123 additions and 47 deletions
|
@ -69,7 +69,10 @@ impl<Checker: BuildRunnable> Server<Checker> {
|
|||
|
||||
pub(crate) fn quick_check_file(&mut self, uri: Url) -> ELSResult<()> {
|
||||
// send_log(format!("checking {uri}"))?;
|
||||
let mut parser = Parser::new(self.file_cache.get_token_stream(&uri).unwrap().clone());
|
||||
let Some(ts) = self.file_cache.get_token_stream(&uri) else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut parser = Parser::new(ts.clone());
|
||||
if parser.parse().is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -2782,7 +2782,7 @@ impl PyCodeGenerator {
|
|||
let mut ident = Identifier::public_with_line(DOT, Str::ever("__init__"), line);
|
||||
ident.vi.t = __new__.clone();
|
||||
let self_param = VarName::from_str_and_line(Str::ever("self"), line);
|
||||
let vi = VarInfo::parameter(
|
||||
let vi = VarInfo::nd_parameter(
|
||||
__new__.return_t().unwrap().clone(),
|
||||
ident.vi.def_loc.clone(),
|
||||
);
|
||||
|
@ -2797,7 +2797,7 @@ impl PyCodeGenerator {
|
|||
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
|
||||
let raw =
|
||||
erg_parser::ast::NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
|
||||
let vi = VarInfo::parameter(new_first_param.typ().clone(), ident.vi.def_loc.clone());
|
||||
let vi = VarInfo::nd_parameter(new_first_param.typ().clone(), ident.vi.def_loc.clone());
|
||||
let param = NonDefaultParamSignature::new(raw, vi, None);
|
||||
let params = Params::new(vec![self_param, param], None, vec![], None);
|
||||
(param_name, params)
|
||||
|
@ -2876,7 +2876,7 @@ impl PyCodeGenerator {
|
|||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(fresh_varname);
|
||||
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
|
||||
let vi = VarInfo::parameter(new_first_param.typ().clone(), ident.vi.def_loc.clone());
|
||||
let vi = VarInfo::nd_parameter(new_first_param.typ().clone(), ident.vi.def_loc.clone());
|
||||
let raw =
|
||||
erg_parser::ast::NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
|
||||
let param = NonDefaultParamSignature::new(raw, vi, None);
|
||||
|
|
|
@ -672,6 +672,9 @@ impl Context {
|
|||
(Structural(l), Structural(r)) => self.structural_supertype_of(l, r),
|
||||
// TODO: If visibility does not match, it should be reported as a cause of an error
|
||||
(Structural(l), r) => {
|
||||
if self.supertype_of(l, r) {
|
||||
return true;
|
||||
}
|
||||
let r_fields = self.fields(r);
|
||||
for (l_field, l_ty) in self.fields(l) {
|
||||
if let Some((r_field, r_ty)) = r_fields.get_key_value(&l_field) {
|
||||
|
|
|
@ -67,8 +67,8 @@ impl Context {
|
|||
let t_show = fn0_met(ref_(Slf), Str).quantify();
|
||||
show.register_builtin_py_decl(TO_STR, t_show, Public, Some(FUNDAMENTAL_STR));
|
||||
/* In */
|
||||
let mut in_ = Self::builtin_poly_trait(IN, vec![PS::t(TY_T, NonDefault)], 2);
|
||||
let params = vec![PS::t(TY_T, NonDefault)];
|
||||
let mut in_ = Self::builtin_poly_trait(IN, vec![PS::t_nd(TY_T)], 2);
|
||||
let params = vec![PS::t_nd(TY_T)];
|
||||
let input = Self::builtin_poly_trait(INPUT, params.clone(), 2);
|
||||
let output = Self::builtin_poly_trait(OUTPUT, params, 2);
|
||||
let T = mono_q(TY_T, instanceof(Type));
|
||||
|
@ -100,7 +100,7 @@ impl Context {
|
|||
poly(MUL, vec![]),
|
||||
], */
|
||||
/* Seq */
|
||||
let mut seq = Self::builtin_poly_trait(SEQ, vec![PS::t(TY_T, NonDefault)], 2);
|
||||
let mut seq = Self::builtin_poly_trait(SEQ, vec![PS::t_nd(TY_T)], 2);
|
||||
seq.register_superclass(poly(OUTPUT, vec![ty_tp(T.clone())]), &output);
|
||||
let Slf = mono_q(SELF, subtypeof(poly(SEQ, vec![TyParam::erased(Type)])));
|
||||
let t = fn0_met(Slf.clone(), Nat).quantify();
|
||||
|
@ -109,14 +109,14 @@ impl Context {
|
|||
// Seq.get: |Self <: Seq(T)| Self.(Nat) -> T
|
||||
seq.register_builtin_erg_decl(FUNC_GET, t, Public);
|
||||
/* Iterable */
|
||||
let mut iterable = Self::builtin_poly_trait(ITERABLE, vec![PS::t(TY_T, NonDefault)], 2);
|
||||
let mut iterable = Self::builtin_poly_trait(ITERABLE, vec![PS::t_nd(TY_T)], 2);
|
||||
iterable.register_superclass(poly(OUTPUT, vec![ty_tp(T.clone())]), &output);
|
||||
let Slf = mono_q(SELF, subtypeof(poly(ITERABLE, vec![ty_tp(T.clone())])));
|
||||
let t = fn0_met(Slf.clone(), proj(Slf, ITER)).quantify();
|
||||
iterable.register_builtin_py_decl(FUNC_ITER, t, Public, Some(FUNDAMENTAL_ITER));
|
||||
iterable.register_builtin_erg_decl(ITER, Type, Public);
|
||||
let R = mono_q(TY_R, instanceof(Type));
|
||||
let params = vec![PS::t(TY_R, WithDefault)];
|
||||
let params = vec![PS::t(TY_R, false, WithDefault)];
|
||||
let ty_params = vec![ty_tp(R.clone())];
|
||||
/* Num */
|
||||
/* Add */
|
||||
|
|
|
@ -97,7 +97,7 @@ impl ClassDefType {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DefaultInfo {
|
||||
NonDefault,
|
||||
NonDefault, // var-args should be non-default
|
||||
WithDefault,
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ pub struct ParamSpec {
|
|||
pub(crate) name: Option<&'static str>,
|
||||
// TODO: `:` or `<:`
|
||||
pub(crate) t: Type,
|
||||
pub is_var_params: bool,
|
||||
pub default_info: DefaultInfo,
|
||||
loc: AbsLocation,
|
||||
}
|
||||
|
@ -149,38 +150,59 @@ impl ParamSpec {
|
|||
pub const fn new(
|
||||
name: Option<&'static str>,
|
||||
t: Type,
|
||||
is_var_params: bool,
|
||||
default: DefaultInfo,
|
||||
loc: AbsLocation,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
t,
|
||||
is_var_params,
|
||||
default_info: default,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn named(name: &'static str, t: Type, default: DefaultInfo) -> Self {
|
||||
Self::new(Some(name), t, default, AbsLocation::unknown())
|
||||
pub const fn named(
|
||||
name: &'static str,
|
||||
t: Type,
|
||||
is_var_params: bool,
|
||||
default: DefaultInfo,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
t,
|
||||
is_var_params,
|
||||
default,
|
||||
AbsLocation::unknown(),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn named_nd(name: &'static str, t: Type) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
t,
|
||||
false,
|
||||
DefaultInfo::NonDefault,
|
||||
AbsLocation::unknown(),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn t(name: &'static str, default: DefaultInfo) -> Self {
|
||||
Self::new(Some(name), Type, default, AbsLocation::unknown())
|
||||
pub const fn t(name: &'static str, is_var_params: bool, default: DefaultInfo) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
Type,
|
||||
is_var_params,
|
||||
default,
|
||||
AbsLocation::unknown(),
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn t_nd(name: &'static str) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
Type,
|
||||
false,
|
||||
DefaultInfo::NonDefault,
|
||||
AbsLocation::unknown(),
|
||||
)
|
||||
|
@ -487,12 +509,12 @@ impl Context {
|
|||
for param in params.into_iter() {
|
||||
let id = DefId(get_hash(&(&name, ¶m)));
|
||||
if let Some(name) = param.name {
|
||||
let kind = VarKind::parameter(id, param.default_info);
|
||||
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
|
||||
let muty = Mutability::from(name);
|
||||
let vi = VarInfo::new(param.t, muty, Private, kind, None, None, None, param.loc);
|
||||
params_.push((Some(VarName::new(Token::static_symbol(name))), vi));
|
||||
} else {
|
||||
let kind = VarKind::parameter(id, param.default_info);
|
||||
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
|
||||
let muty = Mutability::Immutable;
|
||||
let vi = VarInfo::new(param.t, muty, Private, kind, None, None, None, param.loc);
|
||||
params_.push((None, vi));
|
||||
|
|
|
@ -321,7 +321,7 @@ impl Context {
|
|||
Err(errs) => (Type::Failure, errs),
|
||||
};
|
||||
let def_id = DefId(get_hash(&(&self.name, "_")));
|
||||
let kind = VarKind::parameter(def_id, DefaultInfo::NonDefault);
|
||||
let kind = VarKind::parameter(def_id, is_var_params, DefaultInfo::NonDefault);
|
||||
let vi = VarInfo::new(
|
||||
spec_t,
|
||||
Immutable,
|
||||
|
@ -382,7 +382,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
let def_id = DefId(get_hash(&(&self.name, name)));
|
||||
let kind = VarKind::parameter(def_id, default);
|
||||
let kind = VarKind::parameter(def_id, is_var_params, default);
|
||||
let muty = Mutability::from(&name.inspect()[..]);
|
||||
let vi = VarInfo::new(
|
||||
spec_t,
|
||||
|
@ -440,7 +440,11 @@ impl Context {
|
|||
log!(err "self_t is None");
|
||||
}
|
||||
}
|
||||
let kind = VarKind::parameter(DefId(get_hash(&(&self.name, name))), default);
|
||||
let kind = VarKind::parameter(
|
||||
DefId(get_hash(&(&self.name, name))),
|
||||
is_var_params,
|
||||
default,
|
||||
);
|
||||
let vi = VarInfo::new(
|
||||
spec_t,
|
||||
Immutable,
|
||||
|
@ -499,7 +503,11 @@ impl Context {
|
|||
log!(err "self_t is None");
|
||||
}
|
||||
}
|
||||
let kind = VarKind::parameter(DefId(get_hash(&(&self.name, name))), default);
|
||||
let kind = VarKind::parameter(
|
||||
DefId(get_hash(&(&self.name, name))),
|
||||
is_var_params,
|
||||
default,
|
||||
);
|
||||
let vi = VarInfo::new(
|
||||
spec_t,
|
||||
Immutable,
|
||||
|
@ -577,6 +585,11 @@ impl Context {
|
|||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
if let Some(var_params) = &mut params.var_params {
|
||||
if let Err(es) = self.assign_param(var_params, None, ParamKind::VarParams) {
|
||||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
for default in params.defaults.iter_mut() {
|
||||
if let Err(es) = self.assign_param(
|
||||
&mut default.sig,
|
||||
|
|
|
@ -1008,7 +1008,6 @@ impl ASTLowerer {
|
|||
}
|
||||
}
|
||||
|
||||
/// TODO: varargs
|
||||
fn lower_lambda(&mut self, lambda: ast::Lambda) -> LowerResult<hir::Lambda> {
|
||||
log!(info "entered {}({lambda})", fn_name!());
|
||||
let in_statement = cfg!(feature = "py_compatible")
|
||||
|
@ -1074,7 +1073,20 @@ impl ASTLowerer {
|
|||
.iter()
|
||||
.partition(|(_, vi)| !vi.kind.has_default());
|
||||
#[cfg(not(feature = "py_compatible"))]
|
||||
let non_default_params = non_default_params.into_iter();
|
||||
let (var_params, non_default_params) = {
|
||||
let (var_params, non_default_params): (Vec<_>, Vec<_>) = non_default_params
|
||||
.into_iter()
|
||||
.partition(|(_, vi)| vi.kind.is_var_params());
|
||||
// vi.t: `[T; _]`
|
||||
// pt: `name: T`
|
||||
let var_params = var_params.get(0).map(|(name, vi)| {
|
||||
ParamTy::pos(
|
||||
name.as_ref().map(|n| n.inspect().clone()),
|
||||
vi.t.inner_ts().remove(0),
|
||||
)
|
||||
});
|
||||
(var_params, non_default_params.into_iter())
|
||||
};
|
||||
#[cfg(feature = "py_compatible")]
|
||||
let non_default_params = non_default_params.into_iter().filter(|(name, _)| {
|
||||
params
|
||||
|
@ -1117,9 +1129,19 @@ impl ASTLowerer {
|
|||
self.pop_append_errs();
|
||||
}
|
||||
let ty = if is_procedural {
|
||||
proc(non_default_param_tys, None, default_param_tys, body.t())
|
||||
proc(
|
||||
non_default_param_tys,
|
||||
var_params,
|
||||
default_param_tys,
|
||||
body.t(),
|
||||
)
|
||||
} else {
|
||||
func(non_default_param_tys, None, default_param_tys, body.t())
|
||||
func(
|
||||
non_default_param_tys,
|
||||
var_params,
|
||||
default_param_tys,
|
||||
body.t(),
|
||||
)
|
||||
};
|
||||
let t = if ty.has_qvar() { ty.quantify() } else { ty };
|
||||
Ok(hir::Lambda::new(id, params, lambda.op, body, t))
|
||||
|
|
|
@ -41,8 +41,11 @@ use Mutability::*;
|
|||
pub enum VarKind {
|
||||
Defined(DefId),
|
||||
Declared,
|
||||
// TODO: flatten
|
||||
Parameter { def_id: DefId, default: DefaultInfo },
|
||||
Parameter {
|
||||
def_id: DefId,
|
||||
var: bool,
|
||||
default: DefaultInfo,
|
||||
},
|
||||
Auto,
|
||||
FixedAuto,
|
||||
DoesNotExist,
|
||||
|
@ -50,8 +53,12 @@ pub enum VarKind {
|
|||
}
|
||||
|
||||
impl VarKind {
|
||||
pub const fn parameter(def_id: DefId, default: DefaultInfo) -> Self {
|
||||
Self::Parameter { def_id, default }
|
||||
pub const fn parameter(def_id: DefId, var: bool, default: DefaultInfo) -> Self {
|
||||
Self::Parameter {
|
||||
def_id,
|
||||
var,
|
||||
default,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn has_default(&self) -> bool {
|
||||
|
@ -65,6 +72,13 @@ impl VarKind {
|
|||
matches!(self, Self::Parameter { .. })
|
||||
}
|
||||
|
||||
pub const fn is_var_params(&self) -> bool {
|
||||
match self {
|
||||
Self::Parameter { var, .. } => *var,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_defined(&self) -> bool {
|
||||
matches!(self, Self::Defined(_))
|
||||
}
|
||||
|
@ -236,9 +250,10 @@ impl VarInfo {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parameter(t: Type, def_loc: AbsLocation) -> Self {
|
||||
pub fn nd_parameter(t: Type, def_loc: AbsLocation) -> Self {
|
||||
let kind = VarKind::Parameter {
|
||||
def_id: DefId(0),
|
||||
var: false,
|
||||
default: DefaultInfo::NonDefault,
|
||||
};
|
||||
Self::new(t, Immutable, Private, kind, None, None, None, def_loc)
|
||||
|
|
|
@ -685,23 +685,6 @@ impl Parser {
|
|||
debug_exit_info!(self);
|
||||
Ok(sig)
|
||||
}
|
||||
Expr::UnaryOp(unary) => match unary.op.kind {
|
||||
TokenKind::PreStar => {
|
||||
let mut exprs = unary.args.into_iter();
|
||||
let param = self
|
||||
.convert_rhs_to_param(*exprs.next().unwrap(), false)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
let params = Params::new(vec![], Some(param), vec![], None);
|
||||
debug_exit_info!(self);
|
||||
Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty()))
|
||||
}
|
||||
_ => {
|
||||
let err = ParseError::simple_syntax_error(line!() as usize, unary.op.loc());
|
||||
self.errs.push(err);
|
||||
debug_exit_info!(self);
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
other => {
|
||||
let err = ParseError::simple_syntax_error(line!() as usize, other.loc());
|
||||
self.errs.push(err);
|
||||
|
|
|
@ -1703,6 +1703,15 @@ impl Parser {
|
|||
debug_exit_info!(self);
|
||||
Ok(call_or_acc)
|
||||
}
|
||||
Some(t) if t.is(PreStar) => {
|
||||
let _ = self.lpop();
|
||||
let expr = self.try_reduce_expr(false, in_type_args, in_brace, false)?;
|
||||
let tuple = self
|
||||
.try_reduce_nonempty_tuple(ArgKind::Var(PosArg::new(expr)), false)
|
||||
.map_err(|_| self.stack_dec(fn_name!()))?;
|
||||
debug_exit_info!(self);
|
||||
Ok(Expr::Tuple(tuple))
|
||||
}
|
||||
Some(t) if t.category_is(TC::UnaryOp) => {
|
||||
let unaryop = self
|
||||
.try_reduce_unary()
|
||||
|
|
|
@ -3,3 +3,6 @@ p! 1, 2, "a" # ERR
|
|||
|
||||
first *x = x[0]
|
||||
assert first(1, 2, 3) == "b" # ERR
|
||||
|
||||
f = (*_: Int) -> None
|
||||
f "a", 1, 2
|
||||
|
|
|
@ -7,3 +7,6 @@ assert first(1, 2, 3) == 1
|
|||
sum_ start: Nat, *args: Int =
|
||||
sum(args, start:=start)
|
||||
assert sum_(0, 1, 2, 3) == 6
|
||||
|
||||
f = (*_: Int) -> None
|
||||
f(1, 2, 3)
|
||||
|
|
|
@ -303,7 +303,7 @@ fn exec_var_args() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_var_args_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/var_args.er", 2)
|
||||
expect_failure("tests/should_err/var_args.er", 3)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue