diff --git a/compiler/erg_compiler/eval.rs b/compiler/erg_compiler/eval.rs index 04113d61..6e5f20f0 100644 --- a/compiler/erg_compiler/eval.rs +++ b/compiler/erg_compiler/eval.rs @@ -264,7 +264,14 @@ impl Evaluator { Some(ValueObj::Array(RcArray::from(elems))) } - fn eval_const_record(&self, record: &NormalRecord, ctx: &Context) -> Option { + fn eval_const_record(&self, record: &Record, ctx: &Context) -> Option { + match record { + Record::Normal(rec) => self.eval_const_normal_record(rec, ctx), + Record::Shortened(_rec) => unreachable!(), // should be desugared + } + } + + fn eval_const_normal_record(&self, record: &NormalRecord, ctx: &Context) -> Option { let mut attrs = vec![]; for attr in record.attrs.iter() { if let Some(elem) = self.eval_const_block(&attr.body.block, ctx) { diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 09206008..1afb6ff0 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -217,7 +217,15 @@ impl ASTLowerer { Ok(hir::NormalTuple::new(hir::Args::from(new_tuple))) } - fn lower_record(&mut self, record: ast::NormalRecord) -> LowerResult { + fn lower_record(&mut self, record: ast::Record) -> LowerResult { + log!(info "entered {}({record})", fn_name!()); + match record { + ast::Record::Normal(rec) => self.lower_normal_record(rec), + ast::Record::Shortened(_rec) => unreachable!(), // should be desugared + } + } + + fn lower_normal_record(&mut self, record: ast::NormalRecord) -> LowerResult { log!(info "entered {}({record})", fn_name!()); let mut hir_record = hir::Record::new(record.l_brace, record.r_brace, hir::RecordAttrs::new()); diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index aa11ea9e..2cae6145 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -6,8 +6,6 @@ use erg_common::error::Location; use erg_common::set::Set as HashSet; use erg_common::traits::{Locational, NestedDisplay, Stream}; use erg_common::vis::{Field, Visibility}; -// use erg_common::ty::SubrKind; -// use erg_common::value::{Field, ValueObj, Visibility}; use erg_common::Str; use erg_common::{ fmt_option, fmt_vec, impl_display_for_enum, impl_display_for_single_struct, @@ -675,6 +673,10 @@ impl From> for RecordAttrs { } impl RecordAttrs { + pub const fn new(attrs: Vec) -> Self { + Self(attrs) + } + pub fn iter(&self) -> impl Iterator { self.0.iter() } @@ -714,12 +716,45 @@ impl NormalRecord { /// e.g. {x; y; z} (syntax sugar of {x = x; y = y; z = z}) #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct SimpleRecord { +pub struct ShortenedRecord { pub l_brace: Token, pub r_brace: Token, - idents: Vec, + pub idents: Vec, } +impl NestedDisplay for ShortenedRecord { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{{")?; + for ident in self.idents.iter() { + write!(f, "{}; ", ident)?; + } + write!(f, "}}") + } +} + +impl_display_from_nested!(ShortenedRecord); +impl_locational!(ShortenedRecord, l_brace, r_brace); + +impl ShortenedRecord { + pub const fn new(l_brace: Token, r_brace: Token, idents: Vec) -> Self { + Self { + l_brace, + r_brace, + idents, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Record { + Normal(NormalRecord), + Shortened(ShortenedRecord), +} + +impl_nested_display_for_enum!(Record; Normal, Shortened); +impl_display_for_enum!(Record; Normal, Shortened); +impl_locational_for_enum!(Record; Normal, Shortened); + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct NormalSet { l_brace: Token, @@ -2610,7 +2645,8 @@ pub struct Def { impl NestedDisplay for Def { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { - writeln!(f, "{} {}", self.sig, self.body.op.content)?; + self.sig.fmt_nest(f, level)?; + writeln!(f, " {}", self.body.op.content)?; self.body.block.fmt_nest(f, level + 1) } } @@ -2671,7 +2707,7 @@ pub enum Expr { Tuple(Tuple), Dict(Dict), Set(Set), - Record(NormalRecord), + Record(Record), BinOp(BinOp), UnaryOp(UnaryOp), Call(Call), diff --git a/compiler/erg_parser/desugar.rs b/compiler/erg_parser/desugar.rs index a67a24a4..b1e18046 100644 --- a/compiler/erg_parser/desugar.rs +++ b/compiler/erg_parser/desugar.rs @@ -11,9 +11,11 @@ use erg_common::Str; use erg_common::{enum_unwrap, get_hash, set}; use crate::ast::{ - Accessor, Args, Block, Call, Def, DefBody, DefId, Expr, Identifier, Lambda, LambdaSignature, - Literal, Local, Module, ParamPattern, ParamSignature, Params, PosArg, Signature, SubrSignature, - TypeBoundSpecs, TypeSpec, VarName, VarPattern, VarSignature, + Accessor, Args, Array, BinOp, Block, Call, Def, DefBody, DefId, Expr, Identifier, KwArg, + Lambda, LambdaSignature, Literal, Local, MethodDefs, Module, NormalArray, NormalRecord, + NormalTuple, ParamPattern, ParamSignature, Params, PosArg, Record, RecordAttrs, + ShortenedRecord, Signature, SubrSignature, Tuple, TypeAscription, TypeBoundSpecs, TypeSpec, + UnaryOp, VarName, VarPattern, VarSignature, }; use crate::token::{Token, TokenKind}; @@ -46,7 +48,9 @@ impl Desugarer { pub fn desugar(&mut self, module: Module) -> Module { let module = self.desugar_multiple_pattern_def(module); - self.desugar_pattern(module) + let module = self.desugar_pattern(module); + let module = self.desugar_shortened_record(module); + module } fn desugar_ubar_lambda(&self, _module: Module) -> Module { @@ -306,6 +310,149 @@ impl Desugarer { } } + /// `{x; y}` -> `{x = x; y = y}` + fn desugar_shortened_record(&self, mut module: Module) -> Module { + let mut new = Module::with_capacity(module.len()); + while let Some(chunk) = module.lpop() { + new.push(self.rec_desugar_shortened_record(chunk)); + } + new + } + + fn rec_desugar_shortened_record(&self, expr: Expr) -> Expr { + match expr { + Expr::Record(Record::Shortened(rec)) => { + let rec = self.desugar_shortened_record_inner(rec); + Expr::Record(Record::Normal(rec)) + } + Expr::Array(array) => match array { + Array::Normal(arr) => { + let (elems, _, _) = arr.elems.deconstruct(); + let elems = elems + .into_iter() + .map(|elem| PosArg::new(self.rec_desugar_shortened_record(elem.expr))) + .collect(); + let elems = Args::new(elems, vec![], None); + let arr = NormalArray::new(arr.l_sqbr, arr.r_sqbr, elems); + Expr::Array(Array::Normal(arr)) + } + _ => todo!(), + }, + Expr::Tuple(tuple) => match tuple { + Tuple::Normal(tup) => { + let (elems, _, paren) = tup.elems.deconstruct(); + let elems = elems + .into_iter() + .map(|elem| PosArg::new(self.rec_desugar_shortened_record(elem.expr))) + .collect(); + let new_tup = Args::new(elems, vec![], paren); + let tup = NormalTuple::new(new_tup); + Expr::Tuple(Tuple::Normal(tup)) + } + }, + Expr::Set(set) => { + todo!("{set}") + } + Expr::Dict(dict) => { + todo!("{dict}") + } + Expr::BinOp(binop) => { + let mut args = vec![]; + for arg in binop.args.into_iter() { + args.push(self.rec_desugar_shortened_record(*arg)); + } + let lhs = args.remove(0); + let rhs = args.remove(0); + Expr::BinOp(BinOp::new(binop.op, lhs, rhs)) + } + Expr::UnaryOp(unaryop) => { + let mut args = vec![]; + for arg in unaryop.args.into_iter() { + args.push(self.rec_desugar_shortened_record(*arg)); + } + let expr = args.remove(0); + Expr::UnaryOp(UnaryOp::new(unaryop.op, expr)) + } + Expr::Call(call) => { + let obj = self.rec_desugar_shortened_record(*call.obj); + let (pos_args, kw_args, paren) = call.args.deconstruct(); + let pos_args = pos_args + .into_iter() + .map(|arg| PosArg::new(self.rec_desugar_shortened_record(arg.expr))) + .collect(); + let kw_args = kw_args + .into_iter() + .map(|arg| { + let expr = self.rec_desugar_shortened_record(arg.expr); + KwArg::new(arg.keyword, arg.t_spec, expr) // TODO: t_spec + }) + .collect(); + let args = Args::new(pos_args, kw_args, paren); + Expr::Call(Call::new(obj, call.method_name, args)) + } + Expr::Def(def) => { + let mut chunks = vec![]; + for chunk in def.body.block.into_iter() { + chunks.push(self.rec_desugar_shortened_record(chunk)); + } + let body = DefBody::new(def.body.op, Block::new(chunks), def.body.id); + Expr::Def(Def::new(def.sig, body)) + } + Expr::Lambda(lambda) => { + let mut chunks = vec![]; + for chunk in lambda.body.into_iter() { + chunks.push(self.rec_desugar_shortened_record(chunk)); + } + let body = Block::new(chunks); + Expr::Lambda(Lambda::new(lambda.sig, lambda.op, body, lambda.id)) + } + Expr::TypeAsc(tasc) => { + let expr = self.rec_desugar_shortened_record(*tasc.expr); + Expr::TypeAsc(TypeAscription::new(expr, tasc.t_spec)) + } + Expr::MethodDefs(method_defs) => { + let mut new_defs = vec![]; + for def in method_defs.defs.into_iter() { + let mut chunks = vec![]; + for chunk in def.body.block.into_iter() { + chunks.push(self.rec_desugar_shortened_record(chunk)); + } + let body = DefBody::new(def.body.op, Block::new(chunks), def.body.id); + new_defs.push(Def::new(def.sig, body)); + } + let new_defs = RecordAttrs::from(new_defs); + Expr::MethodDefs(MethodDefs::new( + method_defs.class, + method_defs.vis, + new_defs, + )) + } + // TODO: Accessorにも一応レコードを入れられる + other => other, + } + } + + fn desugar_shortened_record_inner(&self, rec: ShortenedRecord) -> NormalRecord { + let mut attrs = vec![]; + for attr in rec.idents.into_iter() { + let var = VarSignature::new(VarPattern::Ident(attr.clone()), None); + let sig = Signature::Var(var); + let body = DefBody::new( + Token::from_str(TokenKind::Equal, "="), + Block::new(vec![Expr::local( + attr.inspect(), + attr.ln_begin().unwrap(), + attr.col_begin().unwrap(), + )]), + DefId(get_hash(&(&sig, attr.inspect()))), + ); + let def = Def::new(sig, body); + attrs.push(def); + } + let attrs = RecordAttrs::new(attrs); + NormalRecord::new(rec.l_brace, rec.r_brace, attrs) + } + /// `F(I | I > 0)` -> `F(I: {I: Int | I > 0})` fn desugar_refinement_pattern(&self, _mod: Module) -> Module { todo!() diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index c255a598..083197b9 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -64,7 +64,7 @@ pub enum ArrayInner { pub enum BraceContainer { Set(Set), Dict(Dict), - Record(NormalRecord), + Record(Record), } /// Perform recursive descent parsing. @@ -1410,12 +1410,20 @@ impl Parser { match first { Expr::Def(def) => { let record = self - .try_reduce_record(l_brace, def) + .try_reduce_normal_record(l_brace, def) .map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(BraceContainer::Record(record)) + Ok(BraceContainer::Record(Record::Normal(record))) + } + // Dict + Expr::TypeAsc(_) => todo!(), + Expr::Accessor(acc) if self.cur_is(Semi) => { + let record = self + .try_reduce_shortened_record(l_brace, acc) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(BraceContainer::Record(Record::Shortened(record))) } - Expr::TypeAsc(_) => todo!(), // invalid syntax other => { let set = self .try_reduce_set(l_brace, other) @@ -1426,7 +1434,11 @@ impl Parser { } } - fn try_reduce_record(&mut self, l_brace: Token, first: Def) -> ParseResult { + fn try_reduce_normal_record( + &mut self, + l_brace: Token, + first: Def, + ) -> ParseResult { debug_call_info!(self); let mut attrs = vec![first]; loop { @@ -1459,6 +1471,54 @@ impl Parser { } } + fn try_reduce_shortened_record( + &mut self, + l_brace: Token, + first: Accessor, + ) -> ParseResult { + debug_call_info!(self); + let first = match first { + Accessor::Local(local) => Identifier::new(None, VarName::new(local.symbol)), + Accessor::Public(public) => { + Identifier::new(Some(public.dot), VarName::new(public.symbol)) + } + other => todo!("{other}"), // syntax error + }; + let mut idents = vec![first]; + loop { + match self.peek() { + Some(t) if t.category_is(TC::Separator) => { + self.skip(); + if self.cur_is(Dedent) { + self.skip(); + if self.cur_is(RBrace) { + let r_brace = self.lpop(); + self.level -= 1; + return Ok(ShortenedRecord::new(l_brace, r_brace, idents)); + } else { + todo!() + } + } + let acc = self.try_reduce_acc().map_err(|_| self.stack_dec())?; + let acc = match acc { + Accessor::Local(local) => Identifier::new(None, VarName::new(local.symbol)), + Accessor::Public(public) => { + Identifier::new(Some(public.dot), VarName::new(public.symbol)) + } + other => todo!("{other}"), // syntax error + }; + idents.push(acc); + } + Some(term) if term.is(RBrace) => { + let r_brace = self.lpop(); + self.level -= 1; + return Ok(ShortenedRecord::new(l_brace, r_brace, idents)); + } + _ => todo!(), + } + } + } + fn _try_reduce_dict() -> ParseResult { todo!() } @@ -1594,10 +1654,7 @@ impl Parser { todo!() } - fn convert_record_to_record_pat( - &mut self, - _record: NormalRecord, - ) -> ParseResult { + fn convert_record_to_record_pat(&mut self, _record: Record) -> ParseResult { debug_call_info!(self); todo!() } @@ -1809,7 +1866,7 @@ impl Parser { fn convert_record_to_param_record_pat( &mut self, - _record: NormalRecord, + _record: Record, ) -> ParseResult { debug_call_info!(self); todo!() @@ -1920,10 +1977,7 @@ impl Parser { todo!() } - fn convert_record_to_param_pat( - &mut self, - _record: NormalRecord, - ) -> ParseResult { + fn convert_record_to_param_pat(&mut self, _record: Record) -> ParseResult { debug_call_info!(self); todo!() } diff --git a/examples/class.er b/examples/class.er index 709c5886..babd2815 100644 --- a/examples/class.er +++ b/examples/class.er @@ -1,17 +1,17 @@ @Inheritable Point2D = Class {x = Int; y = Int} Point2D. - new x, y = Self::__new__ {x = x; y = y} + new x, y = Self::__new__ {x; y} norm ref self = self::x**2 + self::y**2 Point3D = Inherit Point2D, Additional := {z = Int} Point3D. @Override - new x, y, z = Self::__new__ {x = x; y = y; z = z} + new x, y, z = Self::__new__ {x; y; z} @Override norm ref self = self::x**2 + self::y**2 + self::z**2 UnpackPoint2D = Class {x = Int; y = Int}, Impl := Unpack p = UnpackPoint2D::{x = 1; y = 2} -UnpackPoint2D::{x = x; y = x} = p +UnpackPoint2D::{x; y} = p