mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +00:00
chore: improve pattern match
This commit is contained in:
parent
5c757b83f5
commit
f561eebc2f
9 changed files with 343 additions and 125 deletions
|
@ -84,6 +84,7 @@ fn escape_name(name: &str, vis: &VisibilityModifier, def_line: u32, def_col: u32
|
|||
};
|
||||
Str::from(format!("::{name}{line_mangling}"))
|
||||
} else {
|
||||
// For public APIs, mangling is not performed because `hasattr`, etc. cannot be used.
|
||||
Str::from(name)
|
||||
}
|
||||
}
|
||||
|
@ -349,6 +350,7 @@ impl PyCodeGenerator {
|
|||
}
|
||||
|
||||
/// swap TOS and TOS1
|
||||
#[allow(unused)]
|
||||
fn rot2(&mut self) {
|
||||
if self.py_version.minor >= Some(11) {
|
||||
self.write_instr(Opcode311::SWAP);
|
||||
|
@ -359,6 +361,7 @@ impl PyCodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn dup_top(&mut self) {
|
||||
if self.py_version.minor >= Some(11) {
|
||||
self.write_instr(Opcode311::COPY);
|
||||
|
@ -2102,63 +2105,47 @@ impl PyCodeGenerator {
|
|||
pre_block: Block,
|
||||
is_last_arm: bool,
|
||||
) -> Option<usize> {
|
||||
log!(info "entered {}", fn_name!());
|
||||
let param = params.non_defaults.remove(0);
|
||||
log!(info "entered {}({param})", fn_name!());
|
||||
// for pre_block
|
||||
match ¶m.raw.pat {
|
||||
ParamPattern::VarName(name) => {
|
||||
let ident = erg_parser::ast::Identifier::private_from_varname(name.clone());
|
||||
let ident = Identifier::new(ident, None, param.vi.clone());
|
||||
self.emit_store_instr(ident.clone(), AccessKind::Name);
|
||||
self.emit_load_name_instr(ident);
|
||||
let ident = Identifier::new(ident, None, param.vi);
|
||||
self.emit_store_instr(ident, AccessKind::Name);
|
||||
}
|
||||
ParamPattern::Discard(_) => {
|
||||
self.emit_pop_top();
|
||||
}
|
||||
ParamPattern::Discard(_) => {}
|
||||
_other => unreachable!(),
|
||||
}
|
||||
self.emit_frameless_block(pre_block, vec![]);
|
||||
let mut pop_jump_point = None;
|
||||
// If it's the last arm, there's no need to inspect it
|
||||
match param.t_spec_as_expr {
|
||||
Some(t_spec) if !is_last_arm => {
|
||||
// < v3.11:
|
||||
// arg
|
||||
// ↓ LOAD_NAME(contains_operator)
|
||||
// arg contains_operator
|
||||
// ↓ ROT_TWO
|
||||
// contains_operator arg
|
||||
// ↓ load expr
|
||||
// contains_operator arg expr
|
||||
//
|
||||
// in v3.11:
|
||||
// arg null
|
||||
// ↓ SWAP 1
|
||||
// null arg
|
||||
// ↓ LOAD_NAME(contains_operator)
|
||||
// null arg contains_operator
|
||||
// ↓ SWAP 1
|
||||
// null contains_operator arg
|
||||
// ↓ load expr
|
||||
// null contains_operator arg expr
|
||||
// ↓ SWAP 1
|
||||
// null contains_operator expr arg
|
||||
if self.py_version.minor >= Some(11) {
|
||||
self.emit_push_null();
|
||||
self.rot2();
|
||||
if !is_last_arm {
|
||||
let last = params.guards.len().saturating_sub(1);
|
||||
for (i, mut guard) in params.guards.into_iter().enumerate() {
|
||||
if let Expr::BinOp(BinOp {
|
||||
op:
|
||||
Token {
|
||||
kind: TokenKind::ContainsOp,
|
||||
..
|
||||
},
|
||||
lhs: _,
|
||||
rhs,
|
||||
..
|
||||
}) = &mut guard
|
||||
{
|
||||
// *lhs.ref_mut_t().unwrap() = Type::Obj;
|
||||
*rhs.ref_mut_t().unwrap() = Type::Obj;
|
||||
}
|
||||
if !self.contains_op_loaded {
|
||||
self.load_contains_op();
|
||||
self.emit_expr(guard);
|
||||
if i != 0 {
|
||||
self.emit_binop_instr(
|
||||
Token::from_str(TokenKind::AndOp, "and"),
|
||||
TypePair::Others,
|
||||
);
|
||||
}
|
||||
self.emit_load_name_instr(Identifier::private("#contains_operator"));
|
||||
self.rot2();
|
||||
self.emit_expr(t_spec);
|
||||
self.rot2();
|
||||
if self.py_version.minor >= Some(11) {
|
||||
self.emit_precall_and_call(2);
|
||||
} else {
|
||||
self.write_instr(Opcode310::CALL_FUNCTION);
|
||||
self.write_arg(2);
|
||||
}
|
||||
self.stack_dec();
|
||||
if i == last {
|
||||
pop_jump_point = Some(self.lasti());
|
||||
// HACK: match branches often jump very far (beyond the u8 range),
|
||||
// so the jump destination should be reserved as the u16 range.
|
||||
|
@ -2169,20 +2156,10 @@ impl PyCodeGenerator {
|
|||
// but the numbers are the same, only the way the jumping points are calculated is different.
|
||||
self.write_instr(Opcode310::POP_JUMP_IF_FALSE); // jump to the next case
|
||||
self.write_arg(0);
|
||||
self.stack_dec();
|
||||
// if matched, pop original
|
||||
self.emit_pop_top(); // self.stack_dec();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match param.raw.pat {
|
||||
ParamPattern::VarName(name) => {
|
||||
let ident = erg_parser::ast::Identifier::private_from_varname(name);
|
||||
let ident = Identifier::new(ident, None, param.vi);
|
||||
self.emit_store_instr(ident, AccessKind::Name);
|
||||
}
|
||||
ParamPattern::Discard(_) => {
|
||||
self.emit_pop_top();
|
||||
}
|
||||
_other => unreachable!(),
|
||||
}
|
||||
pop_jump_point
|
||||
}
|
||||
|
@ -3123,13 +3100,13 @@ impl PyCodeGenerator {
|
|||
/// forブロックなどで使う
|
||||
fn emit_frameless_block(&mut self, block: Block, params: Vec<Str>) {
|
||||
log!(info "entered {}", fn_name!());
|
||||
if block.is_empty() {
|
||||
return;
|
||||
}
|
||||
let line = block.ln_begin().unwrap_or(0);
|
||||
for param in params {
|
||||
self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name);
|
||||
}
|
||||
if block.is_empty() {
|
||||
return;
|
||||
}
|
||||
let init_stack_len = self.stack_len();
|
||||
for chunk in block.into_iter() {
|
||||
self.emit_chunk(chunk);
|
||||
|
@ -3253,7 +3230,7 @@ impl PyCodeGenerator {
|
|||
"?".into(),
|
||||
);
|
||||
let param = NonDefaultParamSignature::new(raw, vi, None);
|
||||
let params = Params::new(vec![self_param, param], None, vec![], None);
|
||||
let params = Params::new(vec![self_param, param], None, vec![], vec![], None);
|
||||
(param_name, params)
|
||||
} else {
|
||||
("_".into(), Params::single(self_param))
|
||||
|
|
|
@ -116,6 +116,14 @@ impl Context {
|
|||
poly(FROZENSET, vec![ty_tp(T.clone())]),
|
||||
)
|
||||
.quantify();
|
||||
let getattr_t = func(
|
||||
vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)],
|
||||
None,
|
||||
vec![kw_default(KW_DEFAULT, T.clone(), Obj)],
|
||||
T.clone(),
|
||||
)
|
||||
.quantify();
|
||||
let hasattr_t = func(vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], None, vec![], Bool);
|
||||
let t_hash = func1(mono(HASH), Int);
|
||||
let t_if = func(
|
||||
vec![
|
||||
|
@ -349,6 +357,20 @@ impl Context {
|
|||
Some(FUNC_FILTER),
|
||||
);
|
||||
self.register_builtin_py_impl(FUNC_FROZENSET, t_frozenset, Immutable, vis.clone(), None);
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_GETATTR,
|
||||
getattr_t,
|
||||
Immutable,
|
||||
vis.clone(),
|
||||
Some(FUNC_GETATTR),
|
||||
);
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_HASATTR,
|
||||
hasattr_t,
|
||||
Immutable,
|
||||
vis.clone(),
|
||||
Some(FUNC_HASATTR),
|
||||
);
|
||||
self.register_builtin_py_impl(FUNC_HASH, t_hash, Immutable, vis.clone(), Some(FUNC_HASH));
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_ISINSTANCE,
|
||||
|
@ -644,29 +666,6 @@ impl Context {
|
|||
}
|
||||
|
||||
pub(super) fn init_builtin_py_specific_funcs(&mut self) {
|
||||
let hasattr_t = func(vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], None, vec![], Bool);
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_HASATTR,
|
||||
hasattr_t,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
None,
|
||||
);
|
||||
let T = type_q("T");
|
||||
let getattr_t = func(
|
||||
vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)],
|
||||
None,
|
||||
vec![kw_default(KW_DEFAULT, T.clone(), Obj)],
|
||||
T,
|
||||
)
|
||||
.quantify();
|
||||
self.register_builtin_py_impl(
|
||||
FUNC_GETATTR,
|
||||
getattr_t,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
None,
|
||||
);
|
||||
let setattr_t = func(
|
||||
vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str), kw(KW_VALUE, Obj)],
|
||||
None,
|
||||
|
|
|
@ -241,8 +241,19 @@ impl ASTLowerer {
|
|||
Ok(args)
|
||||
}
|
||||
|
||||
fn fake_lower_call(&self, call: ast::Call) -> LowerResult<hir::Call> {
|
||||
fn fake_lower_call(&self, mut call: ast::Call) -> LowerResult<hir::Call> {
|
||||
let obj = self.fake_lower_expr(*call.obj)?;
|
||||
if call
|
||||
.attr_name
|
||||
.as_ref()
|
||||
.is_some_and(|attr| attr.inspect() == "__Tuple_getitem__")
|
||||
{
|
||||
call.attr_name
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.name
|
||||
.rename("__getitem__".into());
|
||||
}
|
||||
let attr_name = call.attr_name.map(hir::Identifier::bare);
|
||||
let args = self.fake_lower_args(call.args)?;
|
||||
Ok(hir::Call::new(obj, attr_name, args))
|
||||
|
@ -434,7 +445,7 @@ impl ASTLowerer {
|
|||
}
|
||||
|
||||
fn fake_lower_params(&self, params: ast::Params) -> LowerResult<hir::Params> {
|
||||
let (non_defaults_, var_params_, defaults_, parens) = params.deconstruct();
|
||||
let (non_defaults_, var_params_, defaults_, guards_, parens) = params.deconstruct();
|
||||
let mut non_defaults = vec![];
|
||||
for non_default_ in non_defaults_.into_iter() {
|
||||
let t_spec_as_expr = non_default_
|
||||
|
@ -480,7 +491,18 @@ impl ASTLowerer {
|
|||
let default = hir::DefaultParamSignature::new(sig, default_val);
|
||||
defaults.push(default);
|
||||
}
|
||||
Ok(hir::Params::new(non_defaults, var_params, defaults, parens))
|
||||
let mut guards = vec![];
|
||||
for guard in guards_.into_iter() {
|
||||
let guard = self.fake_lower_expr(guard)?;
|
||||
guards.push(guard);
|
||||
}
|
||||
Ok(hir::Params::new(
|
||||
non_defaults,
|
||||
var_params,
|
||||
defaults,
|
||||
guards,
|
||||
parens,
|
||||
))
|
||||
}
|
||||
|
||||
fn fake_lower_block(&self, block: ast::Block) -> LowerResult<hir::Block> {
|
||||
|
|
|
@ -1739,6 +1739,7 @@ pub struct Params {
|
|||
pub non_defaults: Vec<NonDefaultParamSignature>,
|
||||
pub var_params: Option<Box<NonDefaultParamSignature>>,
|
||||
pub defaults: Vec<DefaultParamSignature>,
|
||||
pub guards: Vec<Expr>,
|
||||
pub parens: Option<(Token, Token)>,
|
||||
}
|
||||
|
||||
|
@ -1811,22 +1812,24 @@ impl Params {
|
|||
non_defaults: Vec<NonDefaultParamSignature>,
|
||||
var_params: Option<Box<NonDefaultParamSignature>>,
|
||||
defaults: Vec<DefaultParamSignature>,
|
||||
guards: Vec<Expr>,
|
||||
parens: Option<(Token, Token)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
non_defaults,
|
||||
var_params,
|
||||
defaults,
|
||||
guards,
|
||||
parens,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self::new(vec![], None, vec![], None)
|
||||
Self::new(vec![], None, vec![], vec![], None)
|
||||
}
|
||||
|
||||
pub fn single(sig: NonDefaultParamSignature) -> Self {
|
||||
Self::new(vec![sig], None, vec![], None)
|
||||
Self::new(vec![sig], None, vec![], vec![], None)
|
||||
}
|
||||
|
||||
pub const fn ref_deconstruct(&self) -> RefRawParams {
|
||||
|
|
|
@ -1,13 +1,35 @@
|
|||
# e.g. `nightly.0`
|
||||
.Identifier = Class { .name = Str; .num = Nat }
|
||||
.Identifier|<: Show|.
|
||||
__str__ ref self = "\{self.name}.\{self.num}"
|
||||
.Identifier|.Identifier <: Eq|.
|
||||
__eq__ self, other: .Identifier =
|
||||
self.name == other.name and self.num == other.num
|
||||
.Identifier.
|
||||
from_str s: Str =
|
||||
match s.split("."):
|
||||
[name, num] ->
|
||||
num_ = nat(num)
|
||||
assert num_ in Nat
|
||||
.Identifier::__new__ { .name; .num = num_ }
|
||||
_ -> panic "invalid identifier string: \{s}"
|
||||
|
||||
.Version = Class { .major = Nat; .minor = Nat; .patch = Nat; .pre = .Identifier or NoneType }
|
||||
.Version.
|
||||
.SemVer = Class { .major = Nat; .minor = Nat; .patch = Nat; .pre = .Identifier or NoneType }
|
||||
.SemVer|<: Show|.
|
||||
__str__ ref self =
|
||||
if self.pre != None:
|
||||
do: "SemVer(\{self.major}.\{self.minor}.\{self.patch}-\{self.pre})"
|
||||
do: "SemVer(\{self.major}.\{self.minor}.\{self.patch})"
|
||||
.SemVer.
|
||||
new major, minor, patch, pre := None =
|
||||
.Version::__new__ { .major = major; .minor = minor; .patch = patch; .pre = pre }
|
||||
.SemVer::__new__ { .major; .minor; .patch; .pre }
|
||||
from_str s: Str =
|
||||
match s.split("."):
|
||||
[major, minor, patch] ->
|
||||
.SemVer.new(nat(major), nat(minor), nat(patch))
|
||||
[major, minor, patch, pre] ->
|
||||
.SemVer.new(nat(major), nat(minor), nat(patch), .Identifier.from_str(pre))
|
||||
_ -> panic "invalid semver string: \{s}"
|
||||
#[
|
||||
greater self, other: .Version =
|
||||
match [self.major > other.major, self.major >= other.major, self.minor > other.minor, self.minor >= other.minor, self.patch > other.patch]:
|
||||
|
@ -16,12 +38,14 @@
|
|||
[_, True, _, True, True] -> True
|
||||
_ -> False
|
||||
]#
|
||||
.Version|.Version <: Eq|.
|
||||
__eq__ self, other: .Version =
|
||||
.SemVer|<: Eq|.
|
||||
__eq__ self, other: .SemVer =
|
||||
self.major == other.major and self.minor == other.minor and self.patch == other.patch and self.pre == other.pre
|
||||
|
||||
if! __name__ == "__main__", do!:
|
||||
v = .Version.new(0, 0, 1)
|
||||
v = .SemVer.new(0, 0, 1)
|
||||
assert v.minor == 0
|
||||
assert v.pre == None
|
||||
assert v != .Version.new(0, 0, 2)
|
||||
assert v != .SemVer.new(0, 0, 2)
|
||||
v2 = .SemVer.from_str("0.0.1")
|
||||
assert v == v2
|
||||
|
|
|
@ -1344,6 +1344,7 @@ impl ASTLowerer {
|
|||
hir_non_defaults,
|
||||
hir_var_params,
|
||||
hir_defaults,
|
||||
vec![],
|
||||
params.parens,
|
||||
);
|
||||
if let Err(errs) = self
|
||||
|
@ -1353,6 +1354,18 @@ impl ASTLowerer {
|
|||
{
|
||||
self.errs.extend(errs);
|
||||
}
|
||||
for guard in params.guards.into_iter() {
|
||||
match self.fake_lower_expr(guard) {
|
||||
Ok(guard) => {
|
||||
if hir_params.guards.contains(&guard) {
|
||||
log!(err "duplicate guard: {guard}");
|
||||
continue;
|
||||
}
|
||||
hir_params.guards.push(guard)
|
||||
}
|
||||
Err(es) => self.errs.extend(es),
|
||||
}
|
||||
}
|
||||
if !errs.is_empty() {
|
||||
Err(errs)
|
||||
} else {
|
||||
|
|
|
@ -94,6 +94,11 @@ impl Literal {
|
|||
Self { token }
|
||||
}
|
||||
|
||||
pub fn str(s: impl Into<Str>, line: u32) -> Self {
|
||||
let token = Token::new_fake(TokenKind::StrLit, s, line, 0, 0);
|
||||
Self { token }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is(&self, kind: TokenKind) -> bool {
|
||||
self.token.is(kind)
|
||||
|
@ -3165,6 +3170,10 @@ impl VarName {
|
|||
self.0.content.starts_with('\'')
|
||||
}
|
||||
|
||||
pub fn is_generated(&self) -> bool {
|
||||
self.0.content.starts_with('%')
|
||||
}
|
||||
|
||||
pub const fn token(&self) -> &Token {
|
||||
&self.0
|
||||
}
|
||||
|
@ -3999,7 +4008,9 @@ impl TryFrom<&ParamPattern> for Expr {
|
|||
fn try_from(value: &ParamPattern) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
// ParamPattern::Discard(token) => Ok(Expr::Accessor(Accessor::local(token.clone()))),
|
||||
ParamPattern::VarName(name) => Ok(Expr::Accessor(Accessor::local(name.0.clone()))),
|
||||
ParamPattern::VarName(name) if name.inspect() != "_" => {
|
||||
Ok(Expr::Accessor(Accessor::local(name.0.clone())))
|
||||
}
|
||||
ParamPattern::Lit(lit) => Ok(Expr::Literal(lit.clone())),
|
||||
ParamPattern::Array(array) => Expr::try_from(array),
|
||||
ParamPattern::Tuple(tuple) => Expr::try_from(tuple),
|
||||
|
@ -4122,6 +4133,8 @@ pub struct Params {
|
|||
pub non_defaults: Vec<NonDefaultParamSignature>,
|
||||
pub var_params: Option<Box<NonDefaultParamSignature>>,
|
||||
pub defaults: Vec<DefaultParamSignature>,
|
||||
/// match conditions
|
||||
pub guards: Vec<Expr>,
|
||||
pub parens: Option<(Token, Token)>,
|
||||
}
|
||||
|
||||
|
@ -4134,6 +4147,15 @@ impl fmt::Display for Params {
|
|||
if !self.defaults.is_empty() {
|
||||
write!(f, ", {}", fmt_vec(&self.defaults))?;
|
||||
}
|
||||
if !self.guards.is_empty() {
|
||||
write!(f, " if ")?;
|
||||
}
|
||||
for (i, guard) in self.guards.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, " and ")?;
|
||||
}
|
||||
write!(f, "{guard}")?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
@ -4166,6 +4188,7 @@ type RawParams = (
|
|||
Vec<NonDefaultParamSignature>,
|
||||
Option<Box<NonDefaultParamSignature>>,
|
||||
Vec<DefaultParamSignature>,
|
||||
Vec<Expr>,
|
||||
Option<(Token, Token)>,
|
||||
);
|
||||
|
||||
|
@ -4180,6 +4203,7 @@ impl Params {
|
|||
non_defaults,
|
||||
var_params: var_params.map(Box::new),
|
||||
defaults,
|
||||
guards: Vec::new(),
|
||||
parens,
|
||||
}
|
||||
}
|
||||
|
@ -4193,6 +4217,7 @@ impl Params {
|
|||
self.non_defaults,
|
||||
self.var_params,
|
||||
self.defaults,
|
||||
self.guards,
|
||||
self.parens,
|
||||
)
|
||||
}
|
||||
|
@ -4206,6 +4231,14 @@ impl Params {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn add_guard(&mut self, guard: Expr) {
|
||||
self.guards.push(guard);
|
||||
}
|
||||
|
||||
pub fn extend_guards(&mut self, guards: Vec<Expr>) {
|
||||
self.guards.extend(guards);
|
||||
}
|
||||
}
|
||||
|
||||
/// 引数を取るならTypeでもSubr扱い
|
||||
|
|
|
@ -453,8 +453,13 @@ impl Desugarer {
|
|||
..default
|
||||
});
|
||||
}
|
||||
let mut guards = vec![];
|
||||
for guard in params.guards.into_iter() {
|
||||
guards.push(desugar(guard));
|
||||
}
|
||||
params.non_defaults = non_defaults;
|
||||
params.defaults = defaults;
|
||||
params.guards = guards;
|
||||
params
|
||||
}
|
||||
|
||||
|
@ -827,15 +832,17 @@ impl Desugarer {
|
|||
}
|
||||
|
||||
fn desugar_params_patterns(&mut self, params: &mut Params, pre_block: &mut Block) {
|
||||
let mut guards = vec![];
|
||||
for param in params.non_defaults.iter_mut() {
|
||||
self.desugar_nd_param(param, pre_block);
|
||||
guards.extend(self.desugar_nd_param(param, pre_block));
|
||||
}
|
||||
if let Some(var_params) = params.var_params.as_mut() {
|
||||
self.desugar_nd_param(var_params, pre_block);
|
||||
guards.extend(self.desugar_nd_param(var_params, pre_block));
|
||||
}
|
||||
for param in params.defaults.iter_mut() {
|
||||
self.desugar_nd_param(&mut param.sig, pre_block);
|
||||
guards.extend(self.desugar_nd_param(&mut param.sig, pre_block));
|
||||
}
|
||||
params.extend_guards(guards);
|
||||
}
|
||||
|
||||
fn desugar_nested_var_pattern(
|
||||
|
@ -1024,6 +1031,72 @@ impl Desugarer {
|
|||
Expr::from(NormalSet::new(l_brace, r_brace, args))
|
||||
}
|
||||
|
||||
fn len_guard(param_name: Str, n: usize, loc: &impl Locational) -> Expr {
|
||||
let name = VarName::new(Token::new_fake(
|
||||
TokenKind::Symbol,
|
||||
param_name.clone(),
|
||||
loc.ln_begin().unwrap_or(1),
|
||||
loc.col_begin().unwrap_or(0),
|
||||
loc.col_end().unwrap_or(0),
|
||||
));
|
||||
let eq = Token::from_str(TokenKind::DblEq, "==");
|
||||
let len = Literal::nat(n, loc.ln_begin().unwrap_or(0));
|
||||
Expr::from(
|
||||
Identifier::private("len".into())
|
||||
.call1(Expr::from(Identifier::private_from_varname(name))),
|
||||
)
|
||||
.bin_op(eq, len.into())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn hasattr_guard<'i>(
|
||||
param_name: Str,
|
||||
mut keys: impl Iterator<Item = &'i Identifier>,
|
||||
loc: &impl Locational,
|
||||
) -> Option<Expr> {
|
||||
let name = VarName::new(Token::new_fake(
|
||||
TokenKind::Symbol,
|
||||
param_name.clone(),
|
||||
loc.ln_begin().unwrap_or(1),
|
||||
loc.col_begin().unwrap_or(0),
|
||||
loc.col_end().unwrap_or(0),
|
||||
));
|
||||
let name = Expr::from(Identifier::private_from_varname(name));
|
||||
let Some(key) = keys.next() else {
|
||||
return None;
|
||||
};
|
||||
let attr_name = Expr::from(Literal::str(
|
||||
format!("\"{}\"", key.inspect().clone()),
|
||||
key.ln_begin().unwrap_or(0),
|
||||
));
|
||||
let hasattr = Identifier::private("hasattr".into()).call2(name.clone(), attr_name);
|
||||
let res = keys.fold(Expr::from(hasattr), |acc, key| {
|
||||
let attr_name = Expr::from(Literal::str(
|
||||
format!("\"{}\"", key.inspect().clone()),
|
||||
key.ln_begin().unwrap_or(0),
|
||||
));
|
||||
let hasattr = Identifier::private("hasattr".into()).call2(name.clone(), attr_name);
|
||||
acc.bin_op(Token::from_str(TokenKind::AndOp, "and"), hasattr.into())
|
||||
.into()
|
||||
});
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn type_guard(param_name: Str, class: &Expr, loc: &impl Locational) -> Option<Expr> {
|
||||
let name = VarName::new(Token::new_fake(
|
||||
TokenKind::Symbol,
|
||||
param_name.clone(),
|
||||
loc.ln_begin().unwrap_or(1),
|
||||
loc.col_begin().unwrap_or(0),
|
||||
loc.col_end().unwrap_or(0),
|
||||
));
|
||||
let in_op = class.clone().bin_op(
|
||||
Token::dummy(TokenKind::ContainsOp, "contains"),
|
||||
Identifier::private_from_varname(name).into(),
|
||||
);
|
||||
Some(in_op.into())
|
||||
}
|
||||
|
||||
/// ```erg
|
||||
/// f [x, y] =
|
||||
/// ...
|
||||
|
@ -1056,10 +1129,24 @@ impl Desugarer {
|
|||
/// ```erg
|
||||
/// f _: {1}, _: {2} = ...
|
||||
/// ```
|
||||
fn desugar_nd_param(&mut self, param: &mut NonDefaultParamSignature, pre_block: &mut Block) {
|
||||
fn desugar_nd_param(
|
||||
&mut self,
|
||||
param: &mut NonDefaultParamSignature,
|
||||
pre_block: &mut Block,
|
||||
) -> Vec<Expr> {
|
||||
let mut guards = vec![];
|
||||
let line = param.ln_begin().unwrap_or(1);
|
||||
match &mut param.pat {
|
||||
ParamPattern::VarName(_v) => {}
|
||||
ParamPattern::VarName(v) => {
|
||||
if let Some(t_spec) = param
|
||||
.t_spec
|
||||
.as_ref()
|
||||
.map(|t_spec| t_spec.t_spec_as_expr.as_ref())
|
||||
{
|
||||
guards.extend(Self::type_guard(v.inspect().clone(), t_spec, v));
|
||||
}
|
||||
guards
|
||||
}
|
||||
ParamPattern::Lit(l) => {
|
||||
let lit = l.clone();
|
||||
let name = VarName::new(Token::new_fake(
|
||||
|
@ -1069,6 +1156,10 @@ impl Desugarer {
|
|||
l.col_begin().unwrap_or(0),
|
||||
l.col_end().unwrap_or(0),
|
||||
));
|
||||
let eq = Token::from_str(TokenKind::DblEq, "==");
|
||||
let guard = Expr::from(Identifier::private_from_varname(name.clone()))
|
||||
.bin_op(eq, Expr::from(lit.clone()));
|
||||
guards.push(guard.into());
|
||||
param.pat = ParamPattern::VarName(name);
|
||||
let l_brace = Token {
|
||||
content: "{".into(),
|
||||
|
@ -1085,18 +1176,21 @@ impl Desugarer {
|
|||
let t_spec_as_expr = Expr::from(NormalSet::new(l_brace, r_brace, args));
|
||||
let t_spec = TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr);
|
||||
param.t_spec = Some(t_spec);
|
||||
guards
|
||||
}
|
||||
ParamPattern::Tuple(tup) => {
|
||||
let (buf_name, buf_param) = self.gen_buf_nd_param(tup.loc());
|
||||
let mut ty_specs = vec![];
|
||||
let mut ty_exprs = vec![];
|
||||
guards.push(Self::len_guard(buf_name.clone(), tup.elems.len(), tup));
|
||||
for (n, elem) in tup.elems.non_defaults.iter_mut().enumerate() {
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
elem,
|
||||
&buf_name,
|
||||
BufIndex::Tuple(n),
|
||||
);
|
||||
guards.extend(gs);
|
||||
let infer = Token::new_fake(TokenKind::Try, "?", line, 0, 0);
|
||||
let ty_expr = elem
|
||||
.t_spec
|
||||
|
@ -1127,17 +1221,37 @@ impl Desugarer {
|
|||
param.t_spec = Some(TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr));
|
||||
}
|
||||
param.pat = buf_param;
|
||||
guards
|
||||
}
|
||||
ParamPattern::Array(arr) => {
|
||||
let expr = Expr::try_from(&*arr);
|
||||
fn const_check(expr: &Expr) -> bool {
|
||||
match &expr {
|
||||
Expr::Accessor(Accessor::Ident(ident)) => ident.is_const(),
|
||||
Expr::Array(Array::Normal(arr)) => arr
|
||||
.elems
|
||||
.pos_args()
|
||||
.iter()
|
||||
.all(|pos| const_check(&pos.expr)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
let expr = Expr::try_from(&*arr).and_then(|expr| {
|
||||
if const_check(&expr) {
|
||||
Ok(expr)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
});
|
||||
let (buf_name, buf_param) = self.gen_buf_nd_param(arr.loc());
|
||||
guards.push(Self::len_guard(buf_name.clone(), arr.elems.len(), arr));
|
||||
for (n, elem) in arr.elems.non_defaults.iter_mut().enumerate() {
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
elem,
|
||||
&buf_name,
|
||||
BufIndex::Array(n),
|
||||
);
|
||||
guards.extend(gs);
|
||||
}
|
||||
if param.t_spec.is_none() {
|
||||
let len = arr.elems.non_defaults.len();
|
||||
|
@ -1172,16 +1286,19 @@ impl Desugarer {
|
|||
));
|
||||
}
|
||||
param.pat = buf_param;
|
||||
guards
|
||||
}
|
||||
ParamPattern::Record(rec) => {
|
||||
let (buf_name, buf_param) = self.gen_buf_nd_param(rec.loc());
|
||||
guards.extend(Self::hasattr_guard(buf_name.clone(), rec.elems.keys(), rec));
|
||||
for ParamRecordAttr { lhs, rhs } in rec.elems.iter_mut() {
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
rhs,
|
||||
&buf_name,
|
||||
BufIndex::Record(lhs),
|
||||
);
|
||||
guards.extend(gs);
|
||||
}
|
||||
if param.t_spec.is_none() {
|
||||
let mut attrs = RecordAttrs::new(vec![]);
|
||||
|
@ -1226,6 +1343,7 @@ impl Desugarer {
|
|||
param.t_spec = Some(TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr));
|
||||
}
|
||||
param.pat = buf_param;
|
||||
guards
|
||||
}
|
||||
/*ParamPattern::DataPack(pack) => {
|
||||
let (buf_name, buf_sig) = self.gen_buf_name_and_sig(
|
||||
|
@ -1243,7 +1361,7 @@ impl Desugarer {
|
|||
);
|
||||
}
|
||||
}*/
|
||||
_ => {}
|
||||
_ => guards,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1371,8 @@ impl Desugarer {
|
|||
sig: &mut NonDefaultParamSignature,
|
||||
buf_name: &str,
|
||||
buf_index: BufIndex,
|
||||
) {
|
||||
) -> Vec<Expr> {
|
||||
let mut guards = vec![];
|
||||
let obj = Expr::local(
|
||||
buf_name,
|
||||
sig.ln_begin().unwrap_or(1),
|
||||
|
@ -1282,7 +1401,7 @@ impl Desugarer {
|
|||
let id = DefId(get_hash(&(&acc, buf_name)));
|
||||
let block = Block::new(vec![Expr::Accessor(acc)]);
|
||||
let op = Token::from_str(TokenKind::Assign, "=");
|
||||
let body = DefBody::new(op, block, id);
|
||||
let mut body = DefBody::new(op, block, id);
|
||||
let line = sig.ln_begin().unwrap_or(1);
|
||||
match &mut sig.pat {
|
||||
ParamPattern::Tuple(tup) => {
|
||||
|
@ -1295,15 +1414,17 @@ impl Desugarer {
|
|||
)),
|
||||
body,
|
||||
)));
|
||||
guards.push(Self::len_guard(buf_name.clone(), tup.elems.len(), tup));
|
||||
let mut ty_exprs = vec![];
|
||||
let mut tys = vec![];
|
||||
for (n, elem) in tup.elems.non_defaults.iter_mut().enumerate() {
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
elem,
|
||||
&buf_name,
|
||||
BufIndex::Tuple(n),
|
||||
);
|
||||
guards.extend(gs);
|
||||
let infer = Token::new_fake(TokenKind::Try, "?", line, 0, 0);
|
||||
let ty_expr = elem
|
||||
.t_spec
|
||||
|
@ -1333,6 +1454,7 @@ impl Desugarer {
|
|||
sig.t_spec = Some(TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr));
|
||||
}
|
||||
sig.pat = buf_sig;
|
||||
guards
|
||||
}
|
||||
ParamPattern::Array(arr) => {
|
||||
let (buf_name, buf_sig) = self.gen_buf_nd_param(arr.loc());
|
||||
|
@ -1343,13 +1465,15 @@ impl Desugarer {
|
|||
)),
|
||||
body,
|
||||
)));
|
||||
guards.push(Self::len_guard(buf_name.clone(), arr.elems.len(), arr));
|
||||
for (n, elem) in arr.elems.non_defaults.iter_mut().enumerate() {
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
elem,
|
||||
&buf_name,
|
||||
BufIndex::Array(n),
|
||||
);
|
||||
guards.extend(gs);
|
||||
}
|
||||
if sig.t_spec.is_none() {
|
||||
let len = arr.elems.non_defaults.len();
|
||||
|
@ -1374,6 +1498,7 @@ impl Desugarer {
|
|||
));
|
||||
}
|
||||
sig.pat = buf_sig;
|
||||
guards
|
||||
}
|
||||
ParamPattern::Record(rec) => {
|
||||
let (buf_name, buf_sig) = self.gen_buf_nd_param(rec.loc());
|
||||
|
@ -1384,6 +1509,7 @@ impl Desugarer {
|
|||
)),
|
||||
body,
|
||||
)));
|
||||
guards.extend(Self::hasattr_guard(buf_name.clone(), rec.elems.keys(), rec));
|
||||
let mut attrs = RecordAttrs::new(vec![]);
|
||||
let mut tys = vec![];
|
||||
for ParamRecordAttr { lhs, rhs } in rec.elems.iter_mut() {
|
||||
|
@ -1391,12 +1517,13 @@ impl Desugarer {
|
|||
vis: VisModifierSpec::Public(Token::DUMMY),
|
||||
..lhs.clone()
|
||||
};
|
||||
self.desugar_nested_param_pattern(
|
||||
let gs = self.desugar_nested_param_pattern(
|
||||
pre_block,
|
||||
rhs,
|
||||
&buf_name,
|
||||
BufIndex::Record(&lhs),
|
||||
);
|
||||
guards.extend(gs);
|
||||
let infer = Token::new_fake(TokenKind::Try, "?", line, 0, 0);
|
||||
let expr = rhs
|
||||
.t_spec
|
||||
|
@ -1432,6 +1559,7 @@ impl Desugarer {
|
|||
sig.t_spec = Some(TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr));
|
||||
}
|
||||
sig.pat = buf_sig;
|
||||
guards
|
||||
}
|
||||
/*
|
||||
VarPattern::DataPack(pack) => {
|
||||
|
@ -1450,12 +1578,25 @@ impl Desugarer {
|
|||
}
|
||||
*/
|
||||
ParamPattern::VarName(name) => {
|
||||
if let Some(t_spec) = sig
|
||||
.t_spec
|
||||
.as_ref()
|
||||
.map(|t_spec| t_spec.t_spec_as_expr.as_ref())
|
||||
{
|
||||
guards.extend(Self::type_guard(name.inspect().clone(), t_spec, name));
|
||||
}
|
||||
let ident = Identifier::new(VisModifierSpec::Private, name.clone());
|
||||
let v = VarSignature::new(VarPattern::Ident(ident), sig.t_spec.clone());
|
||||
let def = Def::new(Signature::Var(v), body);
|
||||
pre_block.push(Expr::Def(def));
|
||||
guards
|
||||
}
|
||||
ParamPattern::Lit(l) => {
|
||||
let lhs = body.block.remove(0);
|
||||
let rhs = Expr::Literal(l.clone());
|
||||
let eq = Token::new_fake(TokenKind::DblEq, "==", l.ln_begin().unwrap_or(0), 0, 0);
|
||||
let guard = Expr::BinOp(lhs.bin_op(eq, rhs));
|
||||
guards.push(guard);
|
||||
let lit = l.clone();
|
||||
sig.pat = ParamPattern::VarName(VarName::new(Token::new_fake(
|
||||
TokenKind::Symbol,
|
||||
|
@ -1467,8 +1608,9 @@ impl Desugarer {
|
|||
let t_spec = TypeSpec::enum_t_spec(vec![lit.clone()]);
|
||||
let t_spec_as_expr = Self::dummy_set_expr(lit);
|
||||
sig.t_spec = Some(TypeSpecWithOp::new(COLON, t_spec, t_spec_as_expr));
|
||||
guards
|
||||
}
|
||||
_ => {}
|
||||
_ => guards,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -572,3 +572,8 @@ fn exec_err_loc() -> Result<(), ()> {
|
|||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_semver() -> Result<(), ()> {
|
||||
expect_success("crates/erg_compiler/lib/std/semver.er", 0)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue