Implement record pattern for parameters

This commit is contained in:
Shunsuke Shibayama 2022-11-09 18:14:02 +09:00
parent 388010cc27
commit a4c6009ec6
9 changed files with 170 additions and 66 deletions

View file

@ -29,6 +29,8 @@ use erg_parser::ast::DefKind;
use CommonOpcode::*;
use erg_parser::ast::{NonDefaultParamSignature, ParamPattern, VarName};
use erg_parser::token::DOT;
use erg_parser::token::EQUAL;
use erg_parser::token::{Token, TokenKind};
use crate::compile::{AccessKind, Name, StoreLoadKind};
@ -53,7 +55,6 @@ fn fake_method_to_func(class: &str, name: &str) -> Option<&'static str> {
}
}
/// This method obviously does not scale, so in the future all Python APIs will be replaced by declarations in d.er, and renaming will be done in `HIRDesugarer`.
fn escape_name(ident: Identifier) -> Str {
let vis = ident.vis();
if let Some(py_name) = ident.vi.py_name {
@ -801,7 +802,13 @@ impl PyCodeGenerator {
}
self.emit_load_name_instr(ident);
}
Accessor::Attr(a) => {
Accessor::Attr(mut a) => {
// Python's namedtuple, a representation of Record, does not allow attribute names such as `::x`.
// Since Erg does not allow the coexistence of private and public variables with the same name, there is no problem in this trick.
let is_record = a.obj.ref_t().is_record();
if is_record {
a.ident.dot = Some(DOT);
}
self.emit_expr(*a.obj);
self.emit_load_attr_instr(a.ident);
}
@ -2245,10 +2252,7 @@ impl PyCodeGenerator {
log!(info "entered {}", fn_name!());
let line = block.ln_begin().unwrap_or(0);
for param in params {
self.emit_store_instr(
Identifier::public_with_line(Token::dummy(), param, line),
Name,
);
self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name);
}
let init_stack_len = self.stack_len();
for expr in block.into_iter() {
@ -2264,10 +2268,7 @@ impl PyCodeGenerator {
log!(info "entered {}", fn_name!());
let line = block.ln_begin().unwrap_or(0);
for param in params {
self.emit_store_instr(
Identifier::public_with_line(Token::dummy(), param, line),
Name,
);
self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name);
}
let init_stack_len = self.stack_len();
for expr in block.into_iter() {
@ -2349,7 +2350,7 @@ impl PyCodeGenerator {
log!(info "entered {}", fn_name!());
let line = sig.ln_begin().unwrap();
let class_name = sig.ident().inspect();
let ident = Identifier::public_with_line(Token::dummy(), Str::ever("__init__"), line);
let ident = Identifier::public_with_line(DOT, Str::ever("__init__"), line);
let param_name = fresh_varname();
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
@ -2369,14 +2370,14 @@ impl PyCodeGenerator {
let obj =
Expr::Accessor(Accessor::private_with_line(Str::from(&param_name), line));
let expr = obj.attr_expr(Identifier::bare(
Some(Token::dummy()),
Some(DOT),
VarName::from_str(field.symbol.clone()),
));
let obj = Expr::Accessor(Accessor::private_with_line(Str::ever("self"), line));
let dot = if field.vis.is_private() {
None
} else {
Some(Token::dummy())
Some(DOT)
};
let attr = obj.attr(Identifier::bare(
dot,
@ -2391,7 +2392,7 @@ impl PyCodeGenerator {
other => todo!("{other}"),
}
let block = Block::new(attrs);
let body = DefBody::new(Token::dummy(), block, DefId(0));
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_name), subr_sig, body);
}
@ -2404,7 +2405,7 @@ impl PyCodeGenerator {
log!(info "entered {}", fn_name!());
let class_ident = sig.ident();
let line = sig.ln_begin().unwrap();
let ident = Identifier::public_with_line(Token::dummy(), Str::ever("new"), line);
let ident = Identifier::public_with_line(DOT, Str::ever("new"), line);
let param_name = fresh_varname();
let param = VarName::from_str_and_line(Str::from(param_name.clone()), line);
let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
@ -2420,7 +2421,7 @@ impl PyCodeGenerator {
let class_new = class.attr_expr(new_ident);
let call = class_new.call_expr(Args::new(vec![arg], None, vec![], None));
let block = Block::new(vec![call]);
let body = DefBody::new(Token::dummy(), block, DefId(0));
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_ident.inspect()), sig, body);
}

View file

@ -47,6 +47,26 @@ impl Context {
fv.generalize();
TyParam::FreeVar(fv)
}
TyParam::Array(tps) => TyParam::Array(
tps.into_iter()
.map(|tp| self.generalize_tp(tp, variance))
.collect(),
),
TyParam::Tuple(tps) => TyParam::Tuple(
tps.into_iter()
.map(|tp| self.generalize_tp(tp, variance))
.collect(),
),
TyParam::Dict(tps) => TyParam::Dict(
tps.into_iter()
.map(|(k, v)| {
(
self.generalize_tp(k, variance),
self.generalize_tp(v, variance),
)
})
.collect(),
),
TyParam::FreeVar(_) => free,
other if other.has_no_unbound_var() => other,
other => todo!("{other}"),
@ -980,6 +1000,27 @@ impl Context {
self.sub_unify(l, &r, loc, None)?;
Ok(())
}
(TyParam::Array(ls), TyParam::Array(rs)) | (TyParam::Tuple(ls), TyParam::Tuple(rs)) => {
for (l, r) in ls.iter().zip(rs.iter()) {
self.sub_unify_tp(l, r, _variance, loc, allow_divergence)?;
}
Ok(())
}
(TyParam::Dict(ls), TyParam::Dict(rs)) => {
for (lk, lv) in ls.iter() {
if let Some(rv) = rs.get(lk) {
self.sub_unify_tp(lv, rv, _variance, loc, allow_divergence)?;
} else {
// TODO:
return Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(),
fn_name!(),
line!(),
)));
}
}
Ok(())
}
(l, r) => panic!("type-parameter unification failed:\nl:{l}\nr: {r}"),
}
}

View file

@ -17,7 +17,7 @@ use erg_common::{
use erg_parser::ast::{
fmt_lines, DefId, DefKind, NonDefaultParamSignature, OperationKind, TypeSpec, VarName,
};
use erg_parser::token::{Token, TokenKind};
use erg_parser::token::{Token, TokenKind, DOT};
use crate::ty::constructors::{array_t, dict_t, set_t, tuple_t};
use crate::ty::typaram::TyParam;
@ -506,7 +506,7 @@ impl Accessor {
}
pub fn public_with_line(name: Str, line: usize) -> Self {
Self::Ident(Identifier::public_with_line(Token::dummy(), name, line))
Self::Ident(Identifier::public_with_line(DOT, name, line))
}
pub const fn private(name: Token, vi: VarInfo) -> Self {
@ -514,12 +514,7 @@ impl Accessor {
}
pub fn public(name: Token, vi: VarInfo) -> Self {
Self::Ident(Identifier::new(
Some(Token::dummy()),
VarName::new(name),
None,
vi,
))
Self::Ident(Identifier::new(Some(DOT), VarName::new(name), None, vi))
}
pub fn attr(obj: Expr, ident: Identifier) -> Self {

View file

@ -8,7 +8,7 @@ use erg_common::Str;
use erg_common::{enum_unwrap, log};
use erg_parser::ast::{DefId, OperationKind};
use erg_parser::token::{Token, TokenKind};
use erg_parser::token::{Token, TokenKind, DOT, EQUAL};
use crate::ty::free::fresh_varname;
use crate::ty::typaram::TyParam;
@ -314,7 +314,7 @@ impl<'a> Linker<'a> {
Identifier::private_with_line(Str::from(fresh_varname()), expr.ln_begin().unwrap());
let mod_def = Expr::Def(Def::new(
Signature::Var(VarSignature::new(tmp.clone())),
DefBody::new(Token::dummy(), block, DefId(0)),
DefBody::new(EQUAL, block, DefId(0)),
));
let module = Expr::Accessor(Accessor::Ident(tmp));
let __dict__ = Identifier::public("__dict__");
@ -382,7 +382,7 @@ impl<'a> Linker<'a> {
for attr in comps {
*expr = mem::replace(expr, Expr::Code(Block::empty())).attr_expr(
Identifier::public_with_line(
Token::dummy(),
DOT,
Str::rc(attr.as_os_str().to_str().unwrap()),
line,
),

View file

@ -1600,6 +1600,15 @@ impl Type {
}
}
pub fn is_record(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_record(),
Self::Record(_) => true,
Self::Refinement(refine) => refine.t.is_record(),
_ => false,
}
}
pub fn is_module(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_module(),

View file

@ -14,12 +14,12 @@ use crate::ast::{
Accessor, Args, Array, ArrayComprehension, ArrayTypeSpec, ArrayWithLength, BinOp, Block, Call,
ClassAttr, ClassAttrs, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Expr, Identifier,
KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, Module, NonDefaultParamSignature,
NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple, ParamPattern, Params, PosArg,
Record, RecordAttrs, Set as astSet, SetWithLength, ShortenedRecord, Signature, SubrSignature,
Tuple, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, VarRecordAttr,
VarSignature,
NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple, ParamPattern, ParamRecordAttr,
Params, PosArg, Record, RecordAttrs, Set as astSet, SetWithLength, ShortenedRecord, Signature,
SubrSignature, Tuple, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern,
VarRecordAttr, VarSignature,
};
use crate::token::{Token, TokenKind};
use crate::token::{Token, TokenKind, COLON, DOT};
#[derive(Debug, Clone, PartialEq, Eq)]
enum BufIndex<'i> {
@ -635,10 +635,7 @@ impl Desugarer {
l.ln_begin().unwrap(),
l.col_begin().unwrap(),
));
param.t_spec = Some(TypeSpecWithOp::new(
Token::dummy(),
TypeSpec::enum_t_spec(vec![lit]),
));
param.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::enum_t_spec(vec![lit])));
}
ParamPattern::Tuple(tup) => {
let (buf_name, buf_param) = self.gen_buf_nd_param(line);
@ -661,7 +658,7 @@ impl Desugarer {
);
}
if param.t_spec.is_none() {
param.t_spec = Some(TypeSpecWithOp::new(Token::dummy(), TypeSpec::Tuple(tys)));
param.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::Tuple(tys)));
}
param.pat = buf_param;
}
@ -681,27 +678,42 @@ impl Desugarer {
let len = Literal::new(Token::new(TokenKind::NatLit, len.to_string(), line, 0));
let infer = Token::new(TokenKind::Try, "?", line, 0);
let t_spec = ArrayTypeSpec::new(TypeSpec::Infer(infer), ConstExpr::Lit(len));
param.t_spec =
Some(TypeSpecWithOp::new(Token::dummy(), TypeSpec::Array(t_spec)));
param.t_spec = Some(TypeSpecWithOp::new(
Token::dummy(TokenKind::Colon, ":"),
TypeSpec::Array(t_spec),
));
}
param.pat = buf_param;
}
/*
VarPattern::Record(rec) => {
let (buf_name, buf_sig) =
self.gen_buf_name_and_sig(v.ln_begin().unwrap(), v.t_spec);
let buf_def = Def::new(buf_sig, body);
new.push(Expr::Def(buf_def));
for VarRecordAttr { lhs, rhs } in rec.attrs.iter() {
self.desugar_nested_var_pattern(
&mut new,
ParamPattern::Record(rec) => {
let (buf_name, buf_param) = self.gen_buf_nd_param(line);
for ParamRecordAttr { lhs, rhs } in rec.elems.iter_mut() {
insertion_idx = self.desugar_nested_param_pattern(
body,
rhs,
&buf_name,
BufIndex::Record(lhs),
insertion_idx,
);
}
if param.t_spec.is_none() {
let mut tys = vec![];
for ParamRecordAttr { lhs, rhs } in rec.elems.iter() {
let infer = Token::new(TokenKind::Try, "?", line, 0);
tys.push((
lhs.clone(),
rhs.t_spec
.as_ref()
.map(|ts| ts.t_spec.clone())
.unwrap_or(TypeSpec::Infer(infer))
.clone(),
));
}
VarPattern::DataPack(pack) => {
param.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::Record(tys)));
}
param.pat = buf_param;
}
/*ParamPattern::DataPack(pack) => {
let (buf_name, buf_sig) = self.gen_buf_name_and_sig(
v.ln_begin().unwrap(),
Some(pack.class.clone()), // TODO: これだとvの型指定の意味がなくなる
@ -781,7 +793,7 @@ impl Desugarer {
);
}
if sig.t_spec.is_none() {
sig.t_spec = Some(TypeSpecWithOp::new(Token::dummy(), TypeSpec::Tuple(tys)));
sig.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::Tuple(tys)));
}
sig.pat = buf_sig;
insertion_idx
@ -813,24 +825,50 @@ impl Desugarer {
let len = Literal::new(Token::new(TokenKind::NatLit, len.to_string(), line, 0));
let infer = Token::new(TokenKind::Try, "?", line, 0);
let t_spec = ArrayTypeSpec::new(TypeSpec::Infer(infer), ConstExpr::Lit(len));
sig.t_spec = Some(TypeSpecWithOp::new(Token::dummy(), TypeSpec::Array(t_spec)));
sig.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::Array(t_spec)));
}
sig.pat = buf_sig;
insertion_idx
}
/*VarPattern::Record(rec) => {
let (buf_name, buf_sig) = self.gen_buf_name_and_sig(sig.ln_begin().unwrap(), None);
let buf_def = Def::new(buf_sig, body);
new_module.push(Expr::Def(buf_def));
for VarRecordAttr { lhs, rhs } in rec.attrs.iter() {
self.desugar_nested_var_pattern(
new_module,
ParamPattern::Record(rec) => {
let (buf_name, buf_sig) = self.gen_buf_nd_param(line);
new_sub_body.block.insert(
insertion_idx,
Expr::Def(Def::new(
Signature::Var(VarSignature::new(
VarPattern::Ident(Identifier::private(Str::from(&buf_name))),
sig.t_spec.as_ref().map(|ts| ts.t_spec.clone()),
)),
body,
)),
);
insertion_idx += 1;
let mut tys = vec![];
for ParamRecordAttr { lhs, rhs } in rec.elems.iter_mut() {
insertion_idx = self.desugar_nested_param_pattern(
new_sub_body,
rhs,
&buf_name,
BufIndex::Record(lhs),
insertion_idx,
);
let infer = Token::new(TokenKind::Try, "?", line, 0);
tys.push((
lhs.clone(),
rhs.t_spec
.as_ref()
.map(|ts| ts.t_spec.clone())
.unwrap_or(TypeSpec::Infer(infer))
.clone(),
));
}
if sig.t_spec.is_none() {
sig.t_spec = Some(TypeSpecWithOp::new(COLON, TypeSpec::Record(tys)));
}
sig.pat = buf_sig;
insertion_idx
}
/*
VarPattern::DataPack(pack) => {
let (buf_name, buf_sig) =
self.gen_buf_name_and_sig(sig.ln_begin().unwrap(), Some(pack.class.clone()));
@ -844,7 +882,8 @@ impl Desugarer {
BufIndex::Record(lhs),
);
}
}*/
}
*/
ParamPattern::VarName(name) => {
let v = VarSignature::new(
VarPattern::Ident(Identifier::new(None, name.clone())),
@ -894,7 +933,7 @@ impl Desugarer {
let call = Call::new(
Self::rec_desugar_acc(*subscr.obj),
Some(Identifier::public_with_line(
Token::dummy(),
DOT,
Str::ever("__getitem__"),
line,
)),
@ -909,7 +948,7 @@ impl Desugarer {
let call = Call::new(
Self::rec_desugar_acc(*tattr.obj),
Some(Identifier::public_with_line(
Token::dummy(),
DOT,
Str::ever("__Tuple_getitem__"),
line,
)),

View file

@ -311,6 +311,10 @@ pub struct Token {
pub col_begin: usize,
}
pub const COLON: Token = Token::dummy(TokenKind::Colon, ":");
pub const DOT: Token = Token::dummy(TokenKind::Dot, ".");
pub const EQUAL: Token = Token::dummy(TokenKind::Equal, "=");
impl fmt::Debug for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Token")
@ -364,11 +368,17 @@ impl Locational for Token {
}
impl Token {
#[inline]
pub fn dummy() -> Self {
Token {
pub const DUMMY: Token = Token {
kind: TokenKind::Illegal,
content: "DUMMY".into(),
content: Str::ever("DUMMY"),
lineno: 1,
col_begin: 0,
};
pub const fn dummy(kind: TokenKind, content: &'static str) -> Self {
Self {
kind,
content: Str::ever(content),
lineno: 1,
col_begin: 0,
}

4
tests/pattern.er Normal file
View file

@ -0,0 +1,4 @@
f {x; y}, [_], (_, _) = x + y
x = f {x = 1; y = 2}, [3], (4, 5)
assert x == 3

View file

@ -74,6 +74,11 @@ fn exec_move_check() -> Result<(), ()> {
expect_failure("examples/move_check.er", 1)
}
#[test]
fn exec_pattern() -> Result<(), ()> {
expect_success("tests/pattern.er")
}
#[test]
fn exec_pyimport() -> Result<(), ()> {
if cfg!(unix) {