feat: support var-args lambda function

This commit is contained in:
Shunsuke Shibayama 2023-03-03 16:01:20 +09:00
parent cb00efca54
commit 3d310714f1
13 changed files with 123 additions and 47 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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, &param)));
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));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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