mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 12:51:10 +00:00
commit
38d69e6b96
6 changed files with 201 additions and 92 deletions
|
@ -1926,9 +1926,10 @@ impl PyCodeGenerator {
|
|||
self.emit_call_local(ident, call.args)
|
||||
}
|
||||
other => {
|
||||
let is_py_api = other.is_py_api();
|
||||
self.emit_push_null();
|
||||
self.emit_expr(other);
|
||||
self.emit_args_311(call.args, Name);
|
||||
self.emit_args_311(call.args, Name, is_py_api);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1952,11 +1953,12 @@ impl PyCodeGenerator {
|
|||
Some(8) => self.emit_with_instr_308(args),
|
||||
_ => todo!(),
|
||||
},
|
||||
// "pyimport" | "py" |
|
||||
// "pyimport" | "py" are here
|
||||
_ => {
|
||||
let is_py_api = local.is_py_api();
|
||||
self.emit_push_null();
|
||||
self.emit_load_name_instr(local);
|
||||
self.emit_args_311(args, Name);
|
||||
self.emit_args_311(args, Name, is_py_api);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1973,9 +1975,10 @@ impl PyCodeGenerator {
|
|||
} else if let Some(func_name) = fake_method_to_func(&class, method_name.inspect()) {
|
||||
return self.emit_call_fake_method(obj, func_name, method_name, args);
|
||||
}
|
||||
let is_py_api = obj.is_py_api();
|
||||
self.emit_expr(obj);
|
||||
self.emit_load_method_instr(method_name);
|
||||
self.emit_args_311(args, Method);
|
||||
self.emit_args_311(args, Method, is_py_api);
|
||||
}
|
||||
|
||||
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
|
||||
|
@ -2004,7 +2007,7 @@ impl PyCodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
fn emit_args_311(&mut self, mut args: Args, kind: AccessKind) {
|
||||
fn emit_args_311(&mut self, mut args: Args, kind: AccessKind, is_py_api: bool) {
|
||||
let argc = args.len();
|
||||
let pos_len = args.pos_args.len();
|
||||
let mut kws = Vec::with_capacity(args.kw_len());
|
||||
|
@ -2019,7 +2022,12 @@ impl PyCodeGenerator {
|
|||
}
|
||||
}
|
||||
while let Some(arg) = args.try_remove_kw(0) {
|
||||
kws.push(ValueObj::Str(arg.keyword.content.clone()));
|
||||
let kw = if is_py_api {
|
||||
arg.keyword.content
|
||||
} else {
|
||||
Str::from(format!("::{}", arg.keyword.content))
|
||||
};
|
||||
kws.push(ValueObj::Str(kw));
|
||||
self.emit_expr(arg.expr);
|
||||
}
|
||||
let kwsc = if !kws.is_empty() {
|
||||
|
@ -2095,7 +2103,7 @@ impl PyCodeGenerator {
|
|||
self.emit_push_null();
|
||||
self.emit_load_name_instr(method_name);
|
||||
args.insert_pos(0, PosArg::new(obj));
|
||||
self.emit_args_311(args, Name);
|
||||
self.emit_args_311(args, Name, true);
|
||||
}
|
||||
|
||||
// assert takes 1 or 2 arguments (0: cond, 1: message)
|
||||
|
|
|
@ -931,38 +931,25 @@ impl Context {
|
|||
if (params_len < pos_args.len() || params_len < pos_args.len() + kw_args.len())
|
||||
&& subr.var_params.is_none()
|
||||
{
|
||||
return Err(TyCheckErrors::from(TyCheckError::too_many_args_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
callee.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
params_len,
|
||||
pos_args.len(),
|
||||
kw_args.len(),
|
||||
)));
|
||||
return Err(self.gen_too_many_args_error(&callee, subr, pos_args, kw_args));
|
||||
}
|
||||
let mut passed_params = set! {};
|
||||
let non_default_params_len = if attr_name.is_some() && is_method {
|
||||
subr.non_default_params.len() - 1
|
||||
let non_default_params = if is_method {
|
||||
let mut non_default_params = subr.non_default_params.iter();
|
||||
let self_pt = non_default_params.next().unwrap();
|
||||
if let Err(mut es) =
|
||||
self.sub_unify(obj.ref_t(), self_pt.typ(), obj.loc(), self_pt.name())
|
||||
{
|
||||
errs.append(&mut es);
|
||||
}
|
||||
non_default_params
|
||||
} else {
|
||||
subr.non_default_params.len()
|
||||
subr.non_default_params.iter()
|
||||
};
|
||||
let non_default_params_len = non_default_params.len();
|
||||
let mut nth = 1;
|
||||
if pos_args.len() >= non_default_params_len {
|
||||
let (non_default_args, var_args) = pos_args.split_at(non_default_params_len);
|
||||
let non_default_params = if is_method {
|
||||
let mut non_default_params = subr.non_default_params.iter();
|
||||
let self_pt = non_default_params.next().unwrap();
|
||||
if let Err(mut es) =
|
||||
self.sub_unify(obj.ref_t(), self_pt.typ(), obj.loc(), self_pt.name())
|
||||
{
|
||||
errs.append(&mut es);
|
||||
}
|
||||
non_default_params
|
||||
} else {
|
||||
subr.non_default_params.iter()
|
||||
};
|
||||
for (nd_arg, nd_param) in non_default_args.iter().zip(non_default_params) {
|
||||
if let Err(mut es) = self.substitute_pos_arg(
|
||||
&callee,
|
||||
|
@ -1010,7 +997,7 @@ impl Context {
|
|||
attr_name,
|
||||
kw_arg,
|
||||
nth,
|
||||
&subr.default_params,
|
||||
subr,
|
||||
&mut passed_params,
|
||||
) {
|
||||
errs.append(&mut es);
|
||||
|
@ -1031,24 +1018,41 @@ impl Context {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let missing_len = subr.non_default_params.len() - pos_args.len();
|
||||
let missing_params = subr
|
||||
.non_default_params
|
||||
.iter()
|
||||
.rev()
|
||||
.take(missing_len)
|
||||
.rev()
|
||||
.map(|pt| pt.name().cloned().unwrap_or(Str::ever("")))
|
||||
.collect();
|
||||
return Err(TyCheckErrors::from(TyCheckError::args_missing_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
callee.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
missing_len,
|
||||
missing_params,
|
||||
)));
|
||||
// pos_args.len() < non_default_params_len
|
||||
for kw_arg in kw_args.iter() {
|
||||
if let Err(mut es) = self.substitute_kw_arg(
|
||||
&callee,
|
||||
attr_name,
|
||||
kw_arg,
|
||||
nth,
|
||||
subr,
|
||||
&mut passed_params,
|
||||
) {
|
||||
errs.append(&mut es);
|
||||
}
|
||||
nth += 1;
|
||||
}
|
||||
let missing_len = non_default_params_len - pos_args.len() - passed_params.len();
|
||||
if missing_len > 0 {
|
||||
let missing_params = subr
|
||||
.non_default_params
|
||||
.iter()
|
||||
.rev()
|
||||
.take(missing_len)
|
||||
.rev()
|
||||
.map(|pt| pt.name().cloned().unwrap_or(Str::ever("")))
|
||||
.filter(|pt| !passed_params.contains(pt))
|
||||
.collect();
|
||||
return Err(TyCheckErrors::from(TyCheckError::args_missing_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
callee.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
missing_len,
|
||||
missing_params,
|
||||
)));
|
||||
}
|
||||
}
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
|
@ -1088,6 +1092,76 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_too_many_args_error(
|
||||
&self,
|
||||
callee: &hir::Expr,
|
||||
subr_ty: &SubrType,
|
||||
pos_args: &[hir::PosArg],
|
||||
kw_args: &[hir::KwArg],
|
||||
) -> TyCheckErrors {
|
||||
let mut unknown_args = vec![];
|
||||
let mut passed_args: Vec<&hir::KwArg> = vec![];
|
||||
let mut duplicated_args = vec![];
|
||||
for kw_arg in kw_args.iter() {
|
||||
if subr_ty
|
||||
.non_default_params
|
||||
.iter()
|
||||
.all(|pt| pt.name() != Some(kw_arg.keyword.inspect()))
|
||||
&& subr_ty
|
||||
.var_params
|
||||
.as_ref()
|
||||
.map(|pt| pt.name() != Some(kw_arg.keyword.inspect()))
|
||||
.unwrap_or(true)
|
||||
&& subr_ty
|
||||
.default_params
|
||||
.iter()
|
||||
.all(|pt| pt.name() != Some(kw_arg.keyword.inspect()))
|
||||
{
|
||||
unknown_args.push(kw_arg);
|
||||
}
|
||||
if passed_args.iter().any(|a| a.keyword == kw_arg.keyword) {
|
||||
duplicated_args.push(kw_arg);
|
||||
} else {
|
||||
passed_args.push(kw_arg);
|
||||
}
|
||||
}
|
||||
if unknown_args.is_empty() && duplicated_args.is_empty() {
|
||||
let params_len = subr_ty.non_default_params.len() + subr_ty.default_params.len();
|
||||
TyCheckErrors::from(TyCheckError::too_many_args_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
callee.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
params_len,
|
||||
pos_args.len(),
|
||||
kw_args.len(),
|
||||
))
|
||||
} else {
|
||||
let unknown_arg_errors = unknown_args.into_iter().map(|arg| {
|
||||
TyCheckError::unexpected_kw_arg_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
arg.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
arg.keyword.inspect(),
|
||||
)
|
||||
});
|
||||
let duplicated_arg_errors = duplicated_args.into_iter().map(|arg| {
|
||||
TyCheckError::multiple_args_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
arg.loc(),
|
||||
&callee.to_string(),
|
||||
self.caused_by(),
|
||||
arg.keyword.inspect(),
|
||||
)
|
||||
});
|
||||
unknown_arg_errors.chain(duplicated_arg_errors).collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn substitute_pos_arg(
|
||||
&self,
|
||||
callee: &hir::Expr,
|
||||
|
@ -1190,7 +1264,7 @@ impl Context {
|
|||
attr_name: &Option<Identifier>,
|
||||
arg: &hir::KwArg,
|
||||
nth: usize,
|
||||
default_params: &[ParamTy],
|
||||
subr_ty: &SubrType,
|
||||
passed_params: &mut Set<Str>,
|
||||
) -> TyCheckResult<()> {
|
||||
let arg_t = arg.expr.ref_t();
|
||||
|
@ -1207,8 +1281,10 @@ impl Context {
|
|||
} else {
|
||||
passed_params.insert(kw_name.clone());
|
||||
}
|
||||
if let Some(pt) = default_params
|
||||
if let Some(pt) = subr_ty
|
||||
.non_default_params
|
||||
.iter()
|
||||
.chain(subr_ty.default_params.iter())
|
||||
.find(|pt| pt.name().unwrap() == kw_name)
|
||||
{
|
||||
self.sub_unify(arg_t, pt.typ(), arg.loc(), Some(kw_name))
|
||||
|
@ -1263,7 +1339,6 @@ impl Context {
|
|||
) -> TyCheckResult<VarInfo> {
|
||||
if let hir::Expr::Accessor(hir::Accessor::Ident(local)) = obj {
|
||||
if local.vis().is_private() {
|
||||
#[allow(clippy::single_match)]
|
||||
match &local.inspect()[..] {
|
||||
"match" => {
|
||||
return self.get_match_call_t(SubrKind::Func, pos_args, kw_args);
|
||||
|
@ -1271,20 +1346,6 @@ impl Context {
|
|||
"match!" => {
|
||||
return self.get_match_call_t(SubrKind::Proc, pos_args, kw_args);
|
||||
}
|
||||
/*"import" | "pyimport" | "py" => {
|
||||
return self.get_import_call_t(pos_args, kw_args);
|
||||
}*/
|
||||
// handle assert casting
|
||||
/*"assert" => {
|
||||
if let Some(arg) = pos_args.first() {
|
||||
match &arg.expr {
|
||||
hir::Expr::BinOp(bin) if bin.op.is(TokenKind::InOp) && bin.rhs.ref_t() == &Type => {
|
||||
let t = self.eval_const_expr(bin.lhs.as_ref(), None)?.as_type().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},*/
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -422,6 +422,10 @@ impl Identifier {
|
|||
Self::new(dot, name, None, VarInfo::const_default())
|
||||
}
|
||||
|
||||
pub fn is_py_api(&self) -> bool {
|
||||
self.vi.py_name.is_some()
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.name.is_const()
|
||||
}
|
||||
|
@ -539,6 +543,13 @@ impl Accessor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_py_api(&self) -> bool {
|
||||
match self {
|
||||
Self::Ident(ident) => ident.is_py_api(),
|
||||
Self::Attr(attr) => attr.ident.is_py_api(),
|
||||
}
|
||||
}
|
||||
|
||||
// 参照するオブジェクト自体が持っている固有の名前(クラス、モジュールなど)
|
||||
pub fn qual_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
|
@ -1919,6 +1930,13 @@ impl Expr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_py_api(&self) -> bool {
|
||||
match self {
|
||||
Expr::Accessor(acc) => acc.is_py_api(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_type_asc(&self) -> bool {
|
||||
matches!(self, Expr::TypeAsc(_))
|
||||
}
|
||||
|
|
|
@ -136,20 +136,23 @@ impl OwnershipChecker {
|
|||
} else {
|
||||
args_owns.non_defaults.len()
|
||||
};
|
||||
let (non_default_args, var_args) = call.args.pos_args.split_at(non_defaults_len);
|
||||
for (nd_arg, ownership) in
|
||||
non_default_args.iter().zip(args_owns.non_defaults.iter())
|
||||
{
|
||||
self.check_expr(&nd_arg.expr, *ownership, false);
|
||||
}
|
||||
if let Some(ownership) = args_owns.var_params.as_ref() {
|
||||
for var_arg in var_args.iter() {
|
||||
self.check_expr(&var_arg.expr, *ownership, false);
|
||||
if call.args.pos_args.len() > non_defaults_len {
|
||||
let (non_default_args, var_args) =
|
||||
call.args.pos_args.split_at(non_defaults_len);
|
||||
for (nd_arg, (_, ownership)) in
|
||||
non_default_args.iter().zip(args_owns.non_defaults.iter())
|
||||
{
|
||||
self.check_expr(&nd_arg.expr, *ownership, false);
|
||||
}
|
||||
} else {
|
||||
let kw_args = var_args;
|
||||
for (arg, (_, ownership)) in kw_args.iter().zip(args_owns.defaults.iter()) {
|
||||
self.check_expr(&arg.expr, *ownership, false);
|
||||
if let Some((_, ownership)) = args_owns.var_params.as_ref() {
|
||||
for var_arg in var_args.iter() {
|
||||
self.check_expr(&var_arg.expr, *ownership, false);
|
||||
}
|
||||
} else {
|
||||
let kw_args = var_args;
|
||||
for (arg, (_, ownership)) in kw_args.iter().zip(args_owns.defaults.iter()) {
|
||||
self.check_expr(&arg.expr, *ownership, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
for kw_arg in call.args.kw_args.iter() {
|
||||
|
@ -159,6 +162,12 @@ impl OwnershipChecker {
|
|||
.find(|(k, _)| k == kw_arg.keyword.inspect())
|
||||
{
|
||||
self.check_expr(&kw_arg.expr, *ownership, false);
|
||||
} else if let Some((_, ownership)) = args_owns
|
||||
.non_defaults
|
||||
.iter()
|
||||
.find(|(k, _)| k.as_ref() == Some(kw_arg.keyword.inspect()))
|
||||
{
|
||||
self.check_expr(&kw_arg.expr, *ownership, false);
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -431,6 +431,7 @@ impl ScriptGenerator {
|
|||
}
|
||||
|
||||
fn transpile_simple_call(&mut self, mut call: Call) -> String {
|
||||
let is_py_api = call.obj.is_py_api();
|
||||
let mut code = format!("({})", self.transpile_expr(*call.obj));
|
||||
if let Some(attr) = call.attr_name {
|
||||
code += &format!(".{}", Self::transpile_ident(attr));
|
||||
|
@ -441,7 +442,12 @@ impl ScriptGenerator {
|
|||
code.push(',');
|
||||
}
|
||||
while let Some(arg) = call.args.try_remove_kw(0) {
|
||||
code += &format!("{}={},", arg.keyword, self.transpile_expr(arg.expr));
|
||||
let escape = if is_py_api { "" } else { "__" };
|
||||
code += &format!(
|
||||
"{}{escape}={},",
|
||||
arg.keyword.content,
|
||||
self.transpile_expr(arg.expr)
|
||||
);
|
||||
}
|
||||
code.push(')');
|
||||
code
|
||||
|
|
|
@ -882,22 +882,26 @@ impl Ownership {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArgsOwnership {
|
||||
pub non_defaults: Vec<Ownership>,
|
||||
pub var_params: Option<Ownership>,
|
||||
pub non_defaults: Vec<(Option<Str>, Ownership)>,
|
||||
pub var_params: Option<(Str, Ownership)>,
|
||||
pub defaults: Vec<(Str, Ownership)>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ArgsOwnership {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "(")?;
|
||||
for (i, o) in self.non_defaults.iter().enumerate() {
|
||||
for (i, (name, o)) in self.non_defaults.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{o:?}")?;
|
||||
if let Some(name) = name {
|
||||
write!(f, "{name}: {o:?}")?;
|
||||
} else {
|
||||
write!(f, "{o:?}")?;
|
||||
}
|
||||
}
|
||||
if let Some(o) = self.var_params.as_ref() {
|
||||
write!(f, ", ...{o:?}")?;
|
||||
if let Some((name, o)) = self.var_params.as_ref() {
|
||||
write!(f, ", ...{name}: {o:?}")?;
|
||||
}
|
||||
for (name, o) in self.defaults.iter() {
|
||||
write!(f, ", {name} := {o:?}")?;
|
||||
|
@ -909,8 +913,8 @@ impl fmt::Display for ArgsOwnership {
|
|||
|
||||
impl ArgsOwnership {
|
||||
pub const fn new(
|
||||
non_defaults: Vec<Ownership>,
|
||||
var_params: Option<Ownership>,
|
||||
non_defaults: Vec<(Option<Str>, Ownership)>,
|
||||
var_params: Option<(Str, Ownership)>,
|
||||
defaults: Vec<(Str, Ownership)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -1678,9 +1682,12 @@ impl Type {
|
|||
Self::RefMut { .. } => Ownership::RefMut,
|
||||
_ => Ownership::Owned,
|
||||
};
|
||||
nd_args.push(ownership);
|
||||
nd_args.push((nd_param.name().cloned(), ownership));
|
||||
}
|
||||
let var_args = subr.var_params.as_ref().map(|t| t.typ().ownership());
|
||||
let var_args = subr
|
||||
.var_params
|
||||
.as_ref()
|
||||
.map(|t| (t.name().unwrap().clone(), t.typ().ownership()));
|
||||
let mut d_args = vec![];
|
||||
for d_param in subr.default_params.iter() {
|
||||
let ownership = match d_param.typ() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue