diff --git a/README.md b/README.md index 18160a87..b05a0372 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ l2[10] # IndexError: `l2` has 7 elements but was accessed the 11th element 2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # => hello, hello, -2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # TypeError: `.times!` is a method of `Nat` (0 or more Int), not `Int` {Meter; Sec; meter; yard; sec; ...} = import "unit" diff --git a/README_JA.md b/README_JA.md index 1d2c89ab..9a626107 100644 --- a/README_JA.md +++ b/README_JA.md @@ -46,10 +46,10 @@ l2[10] # IndexError: `l2`は7つの要素を持っていますが、11番目の要素のアクセスしようとしています 2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # => hello, hello, -2.times! do!: - print! "hello,", end: "" + print! "hello,", end := "" # TypeError: `.times!`は`Nat`(0以上のInt)のメソッドです、`Int`ではありません {Meter; Sec; meter; yard; sec; ...} = import "unit" diff --git a/README_zh-CN.md b/README_zh-CN.md index fb4a59f1..a28be190 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -45,10 +45,10 @@ l2[10] # 下标错误:`l2`只有7个元素,但却被访问了第11个元素 2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # => hello, hello, -2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # 类型错误:`.times!`是`Nat`(0或更大整数)的方法,不是`Int`的 {Meter; Sec; meter; yard; sec; ...} = import "unit" diff --git a/README_zh-TW.md b/README_zh-TW.md index c6256cfe..639055ab 100644 --- a/README_zh-TW.md +++ b/README_zh-TW.md @@ -45,10 +45,10 @@ l2[10] # 下標錯誤:`l2`只有7個元素,但卻被訪問了第11個元素 2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # => hello, hello, -2.times! do!: - print! "hello, ", end: "" + print! "hello, ", end := "" # 類型錯誤:`.times!`是`Nat`(0或更大整數)的方法,不是`Int`的 {Meter; Sec; meter; yard; sec; ...} = import "unit" diff --git a/compiler/erg_common/traits.rs b/compiler/erg_common/traits.rs index ef860104..7474e868 100644 --- a/compiler/erg_common/traits.rs +++ b/compiler/erg_common/traits.rs @@ -212,7 +212,7 @@ macro_rules! impl_stream_for_wrapper { } } - impl erg_common::traits::Stream<$Inner> for $Strc { + impl $crate::traits::Stream<$Inner> for $Strc { #[inline] fn payload(self) -> Vec<$Inner> { self.0 @@ -232,7 +232,7 @@ macro_rules! impl_stream_for_wrapper { #[macro_export] macro_rules! impl_stream { ($Strc: ident, $Inner: ident, $field: ident) => { - impl erg_common::traits::Stream<$Inner> for $Strc { + impl $crate::traits::Stream<$Inner> for $Strc { #[inline] fn payload(self) -> Vec<$Inner> { self.$field diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 39c1fc7f..9a33de76 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -701,6 +701,9 @@ impl CodeGenerator { self.emit_store_instr(sig.ident, Name); } + // NOTE: use `TypeVar`, `Generic` in `typing` module + // fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {} + fn emit_var_def(&mut self, sig: VarSignature, mut body: DefBody) { if body.is_type() { return self.emit_mono_type_def(sig, body); diff --git a/compiler/erg_compiler/context/hint.rs b/compiler/erg_compiler/context/hint.rs new file mode 100644 index 00000000..4a121cff --- /dev/null +++ b/compiler/erg_compiler/context/hint.rs @@ -0,0 +1,24 @@ +use erg_common::Str; + +use erg_type::Type; + +use crate::context::Context; + +impl Context { + pub(crate) fn get_type_mismatch_hint(&self, expected: &Type, found: &Type) -> Option { + let expected = if let Type::FreeVar(fv) = expected { + if fv.is_linked() { + fv.crack().clone() + } else { + let (_sub, sup) = fv.crack_bound_types().unwrap(); + sup + } + } else { + expected.clone() + }; + match (&expected.name()[..], &found.name()[..]) { + ("Eq", "Float") => Some(Str::ever("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")), + _ => None, + } + } +} diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index b4786279..e2f28a03 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -51,6 +51,7 @@ impl Context { ident.inspect(), &spec_t, body_t, + self.get_type_mismatch_hint(&spec_t, body_t), )); } Ok(()) @@ -120,6 +121,7 @@ impl Context { "match", &class("LambdaFunc"), t, + self.get_type_mismatch_hint(&class("LambdaFunc"), t), )); } } @@ -616,6 +618,7 @@ impl Context { &name[..], param_t, arg_t, + self.get_type_mismatch_hint(param_t, arg_t), ) })?; if let Some(name) = param.name() { @@ -656,6 +659,7 @@ impl Context { &name[..], param_t, arg_t, + self.get_type_mismatch_hint(param_t, arg_t), ) }) } @@ -698,6 +702,7 @@ impl Context { &name[..], pt.typ(), arg_t, + self.get_type_mismatch_hint(pt.typ(), arg_t), ) })?; } else { diff --git a/compiler/erg_compiler/context/mod.rs b/compiler/erg_compiler/context/mod.rs index 8e913f4d..b845787d 100644 --- a/compiler/erg_compiler/context/mod.rs +++ b/compiler/erg_compiler/context/mod.rs @@ -2,6 +2,7 @@ //! `Context` is used for type inference and type checking. pub mod cache; pub mod compare; +pub mod hint; pub mod initialize; pub mod inquire; pub mod instantiate; diff --git a/compiler/erg_compiler/context/register.rs b/compiler/erg_compiler/context/register.rs index 2ff67b7d..285e62dc 100644 --- a/compiler/erg_compiler/context/register.rs +++ b/compiler/erg_compiler/context/register.rs @@ -393,6 +393,7 @@ impl Context { "import::name", &Str, mod_name.ref_t(), + self.get_type_mismatch_hint(&Str, mod_name.ref_t()), )); } } diff --git a/compiler/erg_compiler/context/tyvar.rs b/compiler/erg_compiler/context/tyvar.rs index d6b0e23b..8a86b084 100644 --- a/compiler/erg_compiler/context/tyvar.rs +++ b/compiler/erg_compiler/context/tyvar.rs @@ -1017,6 +1017,7 @@ impl Context { param_name.unwrap_or(&Str::ever("_")), maybe_sup, maybe_sub, + self.get_type_mismatch_hint(maybe_sup, maybe_sub), )); } match (maybe_sub, maybe_sup) { diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index dd61fc78..b6081d61 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -513,6 +513,7 @@ impl TyCheckError { name: &str, expect: &Type, found: &Type, + hint: Option, ) -> Self { Self::new( ErrorCore::new( @@ -525,7 +526,7 @@ impl TyCheckError { "traditional_chinese" => format!("{name}的類型不匹配:\n預期:{GREEN}{expect}{RESET}\n但找到:{RED}{found}{RESET}"), "english" => format!("the type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), ), - None, + hint, ), caused_by, ) diff --git a/compiler/erg_compiler/eval.rs b/compiler/erg_compiler/eval.rs index bd83414e..04113d61 100644 --- a/compiler/erg_compiler/eval.rs +++ b/compiler/erg_compiler/eval.rs @@ -264,7 +264,7 @@ impl Evaluator { Some(ValueObj::Array(RcArray::from(elems))) } - fn eval_const_record(&self, record: &Record, ctx: &Context) -> Option { + fn eval_const_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/hir.rs b/compiler/erg_compiler/hir.rs index d29d80cf..c2253441 100644 --- a/compiler/erg_compiler/hir.rs +++ b/compiler/erg_compiler/hir.rs @@ -97,7 +97,7 @@ pub struct KwArg { impl NestedDisplay for KwArg { fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { - writeln!(f, "{}:", self.keyword)?; + writeln!(f, "{} := ", self.keyword)?; self.expr.fmt_nest(f, level + 1) } } diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 6e79a0b1..09206008 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -55,6 +55,7 @@ impl ASTLowerer { name, expect, found, + self.ctx.get_type_mismatch_hint(expect, found), ) }) } @@ -216,7 +217,7 @@ impl ASTLowerer { Ok(hir::NormalTuple::new(hir::Args::from(new_tuple))) } - fn lower_record(&mut self, record: ast::Record) -> LowerResult { + fn lower_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()); @@ -336,8 +337,6 @@ impl ASTLowerer { &hir_args.kw_args, &self.ctx.name, )?; - log!(err "{}", obj); - log!(err "{:?}", call.method_name); Ok(hir::Call::new(obj, call.method_name, hir_args, t)) } diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index df855381..aa11ea9e 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -95,12 +95,18 @@ impl PosArg { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct KwArg { pub keyword: Token, + pub t_spec: Option, pub expr: Expr, } impl NestedDisplay for KwArg { fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { - writeln!(f, "{}: ", self.keyword.content)?; + writeln!( + f, + "{}{} := ", + self.keyword.content, + fmt_option!(pre ": ", self.t_spec) + )?; self.expr.fmt_nest(f, level + 1) } } @@ -109,8 +115,12 @@ impl_display_from_nested!(KwArg); impl_locational!(KwArg, keyword, expr); impl KwArg { - pub const fn new(keyword: Token, expr: Expr) -> Self { - Self { keyword, expr } + pub const fn new(keyword: Token, t_spec: Option, expr: Expr) -> Self { + Self { + keyword, + t_spec, + expr, + } } } @@ -169,6 +179,10 @@ impl Args { self.pos_args.is_empty() && self.kw_args.is_empty() } + pub fn len(&self) -> usize { + self.pos_args.len() + self.kw_args.len() + } + pub fn kw_is_empty(&self) -> bool { self.kw_args.is_empty() } @@ -296,12 +310,13 @@ impl Public { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Attribute { pub obj: Box, + pub vis: Token, pub name: Local, } impl NestedDisplay for Attribute { fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, _level: usize) -> std::fmt::Result { - write!(f, "({}).{}", self.obj, self.name) + write!(f, "({}){}{}", self.obj, self.vis.inspect(), self.name) } } @@ -309,9 +324,10 @@ impl_display_from_nested!(Attribute); impl_locational!(Attribute, obj, name); impl Attribute { - pub fn new(obj: Expr, name: Local) -> Self { + pub fn new(obj: Expr, vis: Token, name: Local) -> Self { Self { obj: Box::new(obj), + vis, name, } } @@ -388,8 +404,8 @@ impl Accessor { Self::Public(Public::new(dot, symbol)) } - pub fn attr(obj: Expr, name: Local) -> Self { - Self::Attr(Attribute::new(obj, name)) + pub fn attr(obj: Expr, vis: Token, name: Local) -> Self { + Self::Attr(Attribute::new(obj, vis, name)) } pub fn tuple_attr(obj: Expr, index: Literal) -> Self { @@ -646,6 +662,12 @@ impl NestedDisplay for RecordAttrs { } } +impl Locational for RecordAttrs { + fn loc(&self) -> Location { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } +} + impl From> for RecordAttrs { fn from(attrs: Vec) -> Self { Self(attrs) @@ -663,13 +685,13 @@ impl RecordAttrs { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Record { +pub struct NormalRecord { pub l_brace: Token, pub r_brace: Token, pub attrs: RecordAttrs, } -impl NestedDisplay for Record { +impl NestedDisplay for NormalRecord { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { writeln!(f, "{{")?; self.attrs.fmt_nest(f, level + 1)?; @@ -677,10 +699,10 @@ impl NestedDisplay for Record { } } -impl_display_from_nested!(Record); -impl_locational!(Record, l_brace, r_brace); +impl_display_from_nested!(NormalRecord); +impl_locational!(NormalRecord, l_brace, r_brace); -impl Record { +impl NormalRecord { pub fn new(l_brace: Token, r_brace: Token, attrs: RecordAttrs) -> Self { Self { l_brace, @@ -690,6 +712,14 @@ impl Record { } } +/// e.g. {x; y; z} (syntax sugar of {x = x; y = y; z = z}) +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SimpleRecord { + pub l_brace: Token, + pub r_brace: Token, + idents: Vec, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct NormalSet { l_brace: Token, @@ -1168,7 +1198,7 @@ pub struct ConstKwArg { impl NestedDisplay for ConstKwArg { fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, _level: usize) -> std::fmt::Result { - write!(f, "{}: {}", self.keyword.content, self.expr) + write!(f, "{} := {}", self.keyword.content, self.expr) } } @@ -1403,7 +1433,7 @@ impl SubrKindSpec { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct SubrTySpec { +pub struct SubrTypeSpec { pub kind: SubrKindSpec, pub lparen: Option, pub non_defaults: Vec, @@ -1412,11 +1442,11 @@ pub struct SubrTySpec { pub return_t: Box, } -impl fmt::Display for SubrTySpec { +impl fmt::Display for SubrTypeSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "({}, {}, |= {}) {} {}", + "({}, {}, := {}) {} {}", fmt_vec(&self.non_defaults), fmt_option!(pre "...", &self.var_args), fmt_vec(&self.defaults), @@ -1426,7 +1456,7 @@ impl fmt::Display for SubrTySpec { } } -impl Locational for SubrTySpec { +impl Locational for SubrTypeSpec { fn loc(&self) -> Location { if let Some(lparen) = &self.lparen { Location::concat(lparen, self.return_t.as_ref()) @@ -1437,7 +1467,7 @@ impl Locational for SubrTySpec { } } -impl SubrTySpec { +impl SubrTypeSpec { pub fn new( kind: SubrKindSpec, lparen: Option, @@ -1457,24 +1487,34 @@ impl SubrTySpec { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayTypeSpec { + pub ty: Box, + pub len: ConstExpr, +} + +impl fmt::Display for ArrayTypeSpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}; {}]", self.ty, self.len) + } +} + +impl_locational!(ArrayTypeSpec, ty, len); + /// * Array: `[Int; 3]`, `[Int, Ratio, Complex]`, etc. /// * Dict: `[Str: Str]`, etc. -/// * Option: `Int?`, etc. /// * And (Intersection type): Add and Sub and Mul (== Num), etc. /// * Not (Diff type): Pos == Nat not {0}, etc. /// * Or (Union type): Int or None (== Option Int), etc. /// * Enum: `{0, 1}` (== Binary), etc. /// * Range: 1..12, 0.0<..1.0, etc. /// * Record: {.into_s: Self.() -> Str }, etc. -/// * Func: Int -> Int, etc. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +/// * Subr: Int -> Int, Int => None, T.(X) -> Int, etc. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TypeSpec { PreDeclTy(PreDeclTypeSpec), /* Composite types */ - Array { - t: PreDeclTypeSpec, - len: ConstExpr, - }, + Array(ArrayTypeSpec), Tuple(Vec), // Dict(), // Option(), @@ -1488,7 +1528,7 @@ pub enum TypeSpec { rhs: ConstExpr, }, // Record(), - Subr(SubrTySpec), + Subr(SubrTypeSpec), } impl fmt::Display for TypeSpec { @@ -1498,7 +1538,7 @@ impl fmt::Display for TypeSpec { Self::And(lhs, rhs) => write!(f, "{lhs} and {rhs}"), Self::Not(lhs, rhs) => write!(f, "{lhs} not {rhs}"), Self::Or(lhs, rhs) => write!(f, "{lhs} or {rhs}"), - Self::Array { t, len } => write!(f, "[{t}; {len}]"), + Self::Array(arr) => write!(f, "{arr}"), Self::Tuple(tys) => write!(f, "({})", fmt_vec(tys)), Self::Enum(elems) => write!(f, "{{{elems}}}"), Self::Interval { op, lhs, rhs } => write!(f, "{lhs}{}{rhs}", op.inspect()), @@ -1514,7 +1554,7 @@ impl Locational for TypeSpec { Self::And(lhs, rhs) | Self::Not(lhs, rhs) | Self::Or(lhs, rhs) => { Location::concat(lhs.as_ref(), rhs.as_ref()) } - Self::Array { t, len } => Location::concat(t, len), + Self::Array(arr) => arr.loc(), // TODO: ユニット Self::Tuple(tys) => Location::concat(tys.first().unwrap(), tys.last().unwrap()), Self::Enum(set) => set.loc(), @@ -1548,7 +1588,7 @@ impl TypeSpec { defaults: Vec, return_t: TypeSpec, ) -> Self { - Self::Subr(SubrTySpec::new( + Self::Subr(SubrTypeSpec::new( SubrKindSpec::Func, lparen, non_defaults, @@ -1565,7 +1605,7 @@ impl TypeSpec { defaults: Vec, return_t: TypeSpec, ) -> Self { - Self::Subr(SubrTySpec::new( + Self::Subr(SubrTypeSpec::new( SubrKindSpec::Proc, lparen, non_defaults, @@ -1623,6 +1663,14 @@ impl Decorator { pub const fn new(expr: Expr) -> Self { Self(expr) } + + pub fn expr(&self) -> &Expr { + &self.0 + } + + pub fn into_expr(self) -> Expr { + self.0 + } } /// symbol as a left value @@ -2063,6 +2111,20 @@ impl ParamArrayPattern { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamTuplePattern { + pub elems: Params, +} + +impl NestedDisplay for ParamTuplePattern { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "({})", self.elems) + } +} + +impl_display_from_nested!(ParamTuplePattern); +impl_locational!(ParamTuplePattern, elems); + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ParamRecordPattern { l_brace: Token, @@ -2100,11 +2162,15 @@ pub enum ParamPattern { VarArgsName(VarName), Lit(Literal), Array(ParamArrayPattern), + Tuple(ParamTuplePattern), Record(ParamRecordPattern), + Ref(VarName), + RefMut(VarName), + VarArgs(VarName), } -impl_display_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Record); -impl_locational_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Record); +impl_display_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Tuple, Record, Ref, RefMut, VarArgs); +impl_locational_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Tuple, Record, Ref, RefMut, VarArgs); impl ParamPattern { pub const fn inspect(&self) -> Option<&Str> { @@ -2148,7 +2214,7 @@ impl NestedDisplay for ParamSignature { if let Some(default_val) = &self.opt_default_val { write!( f, - "{}{} |= {}", + "{}{} := {}", self.pat, fmt_option!(pre ": ", self.t_spec), default_val @@ -2163,7 +2229,9 @@ impl_display_from_nested!(ParamSignature); impl Locational for ParamSignature { fn loc(&self) -> Location { - if let Some(t_spec) = &self.t_spec { + if let Some(default) = &self.opt_default_val { + Location::concat(&self.pat, default) + } else if let Some(t_spec) = &self.t_spec { Location::concat(&self.pat, t_spec) } else { self.pat.loc() @@ -2220,6 +2288,8 @@ impl Locational for Params { Location::concat(l, r) } else if !self.non_defaults.is_empty() { Location::concat(&self.non_defaults[0], self.non_defaults.last().unwrap()) + } else if let Some(var_args) = &self.var_args { + Location::concat(var_args.as_ref(), self.defaults.last().unwrap()) } else if !self.defaults.is_empty() { Location::concat(&self.defaults[0], self.defaults.last().unwrap()) } else { @@ -2340,9 +2410,9 @@ impl SubrSignature { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct LambdaSignature { + pub bounds: TypeBoundSpecs, pub params: Params, pub return_t_spec: Option, - pub bounds: TypeBoundSpecs, } impl fmt::Display for LambdaSignature { @@ -2492,7 +2562,30 @@ impl Signature { } } -pub type Decl = Signature; +/// type_ascription ::= expr ':' type +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TypeAscription { + pub expr: Box, + pub t_spec: TypeSpec, +} + +impl NestedDisplay for TypeAscription { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + writeln!(f, "{}: {}", self.expr, self.t_spec) + } +} + +impl_display_from_nested!(TypeAscription); +impl_locational!(TypeAscription, expr, t_spec); + +impl TypeAscription { + pub fn new(expr: Expr, t_spec: TypeSpec) -> Self { + Self { + expr: Box::new(expr), + t_spec, + } + } +} #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct DefBody { @@ -2539,6 +2632,36 @@ impl Def { } } +/// e.g. +/// ```erg +/// T = Class ... +/// T. +/// x = 1 +/// f(a) = ... +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MethodDefs { + pub class: TypeSpec, + pub vis: Token, // `.` or `::` + pub defs: RecordAttrs, // TODO: allow declaration +} + +impl NestedDisplay for MethodDefs { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + writeln!(f, "{}{}", self.class, self.vis)?; + self.defs.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(MethodDefs); +impl_locational!(MethodDefs, class, defs); + +impl MethodDefs { + pub const fn new(class: TypeSpec, vis: Token, defs: RecordAttrs) -> Self { + Self { class, vis, defs } + } +} + /// Expression(式) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Expr { @@ -2548,18 +2671,19 @@ pub enum Expr { Tuple(Tuple), Dict(Dict), Set(Set), - Record(Record), + Record(NormalRecord), BinOp(BinOp), UnaryOp(UnaryOp), Call(Call), Lambda(Lambda), - Decl(Decl), + TypeAsc(TypeAscription), Def(Def), + MethodDefs(MethodDefs), } -impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, TypeAsc, Def, MethodDefs); impl_display_from_nested!(Expr); -impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, TypeAsc, Def, MethodDefs); impl Expr { pub fn is_match_call(&self) -> bool { diff --git a/compiler/erg_parser/desugar.rs b/compiler/erg_parser/desugar.rs index 4c58ac71..a67a24a4 100644 --- a/compiler/erg_parser/desugar.rs +++ b/compiler/erg_parser/desugar.rs @@ -244,7 +244,12 @@ impl Desugarer { Accessor::subscr(obj, Expr::Lit(Literal::nat(n, sig.ln_begin().unwrap()))) } BufIndex::Record(attr) => { - Accessor::attr(obj, Local::dummy_with_line(attr, sig.ln_begin().unwrap())) + // TODO: visibility + Accessor::attr( + obj, + Token::from_str(TokenKind::Dot, "."), + Local::dummy_with_line(attr, sig.ln_begin().unwrap()), + ) } }; let id = DefId(get_hash(&(&acc, buf_name))); diff --git a/compiler/erg_parser/lex.rs b/compiler/erg_parser/lex.rs index b456f2ce..74fb08af 100644 --- a/compiler/erg_parser/lex.rs +++ b/compiler/erg_parser/lex.rs @@ -664,7 +664,11 @@ impl Iterator for Lexer /*<'a>*/ { _ => self.accept(Closed, ".."), } } - Some(c) if c.is_ascii_digit() => Some(self.lex_ratio(".".into())), + // prev_token is Symbol => TupleAttribute + // else: RatioLit (e.g. .0) + Some(c) if c.is_ascii_digit() && !self.prev_token.is(Symbol) => { + Some(self.lex_ratio(".".into())) + } _ => self.accept(Dot, "."), }, Some(',') => self.accept(Comma, ","), @@ -673,6 +677,10 @@ impl Iterator for Lexer /*<'a>*/ { self.consume(); self.accept(DblColon, "::") } + Some('=') => { + self.consume(); + self.accept(Walrus, ":=") + } Some('>') => { self.consume(); self.accept(SupertypeOf, ":>") @@ -694,10 +702,6 @@ impl Iterator for Lexer /*<'a>*/ { self.consume(); self.accept(BitOr, "||") } - Some('=') => { - self.consume(); - self.accept(OrEqual, "|=") - } _ => self.accept(VBar, "|"), }, Some('^') => { diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index a50ab878..c255a598 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -8,6 +8,7 @@ use std::mem; use erg_common::config::ErgConfig; use erg_common::config::Input; use erg_common::error::Location; +use erg_common::option_enum_unwrap; use erg_common::set::Set as HashSet; use erg_common::traits::Runnable; use erg_common::traits::{Locational, Stream}; @@ -50,13 +51,6 @@ enum PosOrKwArg { Kw(KwArg), } -pub enum Side { - LhsAssign, - LhsLambda, - Do, - Rhs, -} - pub enum ArrayInner { Normal(Args), WithLength(PosArg, Expr), @@ -70,7 +64,7 @@ pub enum ArrayInner { pub enum BraceContainer { Set(Set), Dict(Dict), - Record(Record), + Record(NormalRecord), } /// Perform recursive descent parsing. @@ -132,141 +126,8 @@ impl Parser { self.nth(idx).map(|t| t.is(kind)).unwrap_or(false) } - fn nth_category(&self, idx: usize) -> Option { - self.nth(idx).map(|t| t.category()) - } - - /// `+1`: true - /// `+ 1`: false - /// `F()`: true - /// `F ()`: false - fn cur_is_in_contact_with_next(&self) -> bool { - let cur_loc = self.peek().unwrap().ln_end().unwrap(); - let next_loc = self.nth(1).unwrap().ln_end().unwrap(); - cur_loc + 1 == next_loc - } - - /// returns if the current position is a left-hand side value. - /// - /// ``` - /// f(x: Int) = { y = x+1; z = [v: Int, w: Int] -> w + x } - /// LhsAssign | Rhs | LhsLambda | Rhs - /// ``` - /// `(Rhs) ; (LhsAssign) =` - /// `(Rhs) ; (LhsLambda) ->` - /// `(Rhs) , (LhsLambda) ->` - /// `(Rhs) (LhsLambda) -> (Rhs);` - fn cur_side(&self) -> Side { - match self.peek() { - Some(t) => { - let name = &t.inspect()[..]; - if name == "do" || name == "do!" { - return Side::Do; - } - } - _ => {} - } - // 以降に=, ->などがないならすべて右辺値 - let opt_equal_pos = self.tokens.iter().skip(1).position(|t| t.is(Equal)); - let opt_arrow_pos = self - .tokens - .iter() - .skip(1) - .position(|t| t.category_is(TC::LambdaOp)); - let opt_sep_pos = self - .tokens - .iter() - .skip(1) - .position(|t| t.category_is(TC::Separator)); - match (opt_equal_pos, opt_arrow_pos, opt_sep_pos) { - (Some(equal), Some(arrow), Some(sep)) => { - let min = [equal, arrow, sep].into_iter().min().unwrap(); - if min == sep { - Side::Rhs - } else if min == equal { - Side::LhsAssign - } else { - // (cur) -> ... = ... ; - if equal < sep { - Side::LhsAssign - } - // (cur) -> ... ; ... = - else if self.arrow_distance(0, 0) == 1 { - Side::LhsLambda - } else { - Side::Rhs - } - } - } - // (cur) = ... -> ... - // (cur) -> ... = ... - (Some(_eq), Some(_arrow), None) => Side::LhsAssign, - // (cur) = ... ; - // (cur) ; ... = - (Some(equal), None, Some(sep)) => { - if equal < sep { - Side::LhsAssign - } else { - Side::Rhs - } - } - (None, Some(arrow), Some(sep)) => { - // (cur) -> ... ; - if arrow < sep { - if self.arrow_distance(0, 0) == 1 { - Side::LhsLambda - } else { - Side::Rhs - } - } - // (cur) ; ... -> - else { - Side::Rhs - } - } - (Some(_eq), None, None) => Side::LhsAssign, - (None, Some(_arrow), None) => { - if self.arrow_distance(0, 0) == 1 { - Side::LhsLambda - } else { - Side::Rhs - } - } - (None, None, Some(_)) | (None, None, None) => Side::Rhs, - } - } - - /// `->`: 0 - /// `i ->`: 1 - /// `i: Int ->`: 1 - /// `a: Array(Int) ->`: 1 - /// `(i, j) ->`: 1 - /// `F () ->`: 2 - /// `F() ->`: 1 - /// `if True, () ->`: 3 - fn arrow_distance(&self, cur: usize, enc_nest_level: usize) -> usize { - match self.nth_category(cur).unwrap() { - TC::LambdaOp => 0, - TC::LEnclosure => { - if self.nth_category(cur + 1).unwrap() == TC::REnclosure { - 1 + self.arrow_distance(cur + 2, enc_nest_level) - } else { - self.arrow_distance(cur + 1, enc_nest_level + 1) - } - } - TC::REnclosure => self.arrow_distance(cur + 1, enc_nest_level - 1), - _ => match self.nth_category(cur + 1).unwrap() { - TC::SpecialBinOp => self.arrow_distance(cur + 1, enc_nest_level), - TC::LEnclosure if self.cur_is_in_contact_with_next() => { - self.arrow_distance(cur + 2, enc_nest_level + 1) - } - _ if enc_nest_level == 0 => 1 + self.arrow_distance(cur + 1, enc_nest_level), - _ => self.arrow_distance(cur + 1, enc_nest_level), - }, - } - } - /// 解析を諦めて次の解析できる要素に移行する + /// give up parsing and move to the next element that can be parsed fn next_expr(&mut self) { while let Some(t) = self.peek() { match t.category() { @@ -411,7 +272,7 @@ impl Parser { Some(t) if t.is(Indent) || t.is(Dedent) => { switch_unreachable!() } - Some(_) => match self.try_reduce_expr(true) { + Some(_) => match self.try_reduce_chunk(true) { Ok(expr) => { chunks.push(expr); } @@ -429,7 +290,7 @@ impl Parser { let mut block = Block::with_capacity(2); // single line block if !self.cur_is(Newline) { - let chunk = self.try_reduce_expr(true).map_err(|_| self.stack_dec())?; + let chunk = self.try_reduce_chunk(true).map_err(|_| self.stack_dec())?; block.push(chunk); self.level -= 1; return Ok(block); @@ -451,7 +312,7 @@ impl Parser { } else if t.is(EOF) { break; } - match self.try_reduce_expr(true) { + match self.try_reduce_chunk(true) { Ok(expr) => { block.push(expr); if self.cur_is(Dedent) { @@ -511,156 +372,16 @@ impl Parser { let mut decs = set![]; while let Some(deco) = self.opt_reduce_decorator().map_err(|_| self.stack_dec())? { decs.insert(deco); + if self.cur_is(Newline) { + self.skip(); + } else { + todo!() + } } self.level -= 1; Ok(decs) } - #[inline] - fn try_reduce_decl(&mut self) -> ParseResult { - debug_call_info!(self); - if self.peek().unwrap().category_is(TC::LEnclosure) - || self.nth(1).map(|n| n.is(Comma)).unwrap_or(false) - { - let var = self.try_reduce_var_sig().map_err(|_| self.stack_dec())?; - self.level -= 1; - return Ok(Signature::Var(var)); - } - let decorators = self.opt_reduce_decorators().map_err(|_| self.stack_dec())?; - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - // TODO: parse bounds |...| - let bounds = TypeBoundSpecs::empty(); - if self.cur_is(VBar) { - todo!("type bounds are not supported yet"); - } - if let Some(params) = self - .opt_reduce_params() - .transpose() - .map_err(|_| self.stack_dec())? - { - let t_spec = if self.cur_is(Colon) { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } else { - None - }; - self.level -= 1; - Ok(Signature::Subr(SubrSignature::new( - decorators, ident, params, t_spec, bounds, - ))) - } else { - if !bounds.is_empty() { - let err = ParseError::syntax_error( - 0, - self.peek().unwrap().loc(), - switch_lang!( - "japanese" => "変数宣言で型制約は使えません", - "simplified_chinese" => "变量声明中不允许类型约束", - "traditional_chinese" => "變量聲明中不允許類型約束", - "english" => "Cannot use type bounds in a declaration of a variable", - ), - None, - ); - self.next_expr(); - self.level -= 1; - self.errs.push(err); - return Err(()); - } - let t_spec = if ident.is_const() { - if self.cur_is(SubtypeOf) { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } else { - None - } - } else if self.cur_is(Colon) { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } else { - None - }; - self.level -= 1; - Ok(Signature::Var(VarSignature::new( - VarPattern::Ident(ident), - t_spec, - ))) - } - } - - #[inline] - fn try_reduce_var_sig(&mut self) -> ParseResult { - debug_call_info!(self); - let pat = self - .try_reduce_var_pattern() - .map_err(|_| self.stack_dec())?; - let t_spec = if self.cur_is(Colon) { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } else { - None - }; - if self.cur_is(VBar) { - todo!() - } - self.level -= 1; - Ok(VarSignature::new(pat, t_spec)) - } - - /// default_param ::= non_default_param `|=` const_expr - fn try_reduce_param_sig(&mut self) -> ParseResult { - debug_call_info!(self); - let lhs = self - .try_reduce_non_default_param_sig() - .map_err(|_| self.stack_dec())?; - if self.cur_is(OrEqual) { - self.skip(); - let val = self.try_reduce_const_expr().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(ParamSignature::new(lhs.pat, lhs.t_spec, Some(val))) - } else { - self.level -= 1; - Ok(ParamSignature::new(lhs.pat, lhs.t_spec, None)) - } - } - - #[inline] - fn try_reduce_non_default_param_sig(&mut self) -> ParseResult { - debug_call_info!(self); - let pat = self - .try_reduce_param_pattern() - .map_err(|_| self.stack_dec())?; - let t_spec = if self.cur_is(Colon) { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } else { - None - }; - self.level -= 1; - Ok(ParamSignature::new(pat, t_spec, None)) - } - - #[inline] - fn try_reduce_lambda_sig(&mut self) -> ParseResult { - debug_call_info!(self); - let bounds = match self.peek() { - Some(t) if t.is(VBar) => { - self.skip(); - self.try_reduce_bounds().map_err(|_| self.stack_dec())? - } - _ => TypeBoundSpecs::empty(), - }; - let params = self.try_reduce_params().map_err(|_| self.stack_dec())?; - let return_t = match self.peek() { - Some(t) if t.is(Colon) => { - self.skip(); - Some(self.try_reduce_type_spec().map_err(|_| self.stack_dec())?) - } - _ => None, - }; - self.level -= 1; - Ok(LambdaSignature::new(params, return_t, bounds)) - } - fn try_reduce_acc(&mut self) -> ParseResult { debug_call_info!(self); let mut acc = match self.peek() { @@ -687,17 +408,44 @@ impl Parser { loop { match self.peek() { Some(t) if t.is(Dot) => { - self.skip(); + let vis = self.lpop(); let token = self.lpop(); match token.kind { Symbol => { let attr = Local::new(token); - acc = Accessor::attr(Expr::Accessor(acc), attr); + acc = Accessor::attr(Expr::Accessor(acc), vis, attr); } NatLit => { let attr = Literal::from(token); acc = Accessor::tuple_attr(Expr::Accessor(acc), attr); } + Newline => { + self.restore(token); + self.restore(vis); + break; + } + _ => { + self.restore(token); + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } + } + } + Some(t) if t.is(DblColon) => { + let vis = self.lpop(); + let token = self.lpop(); + match token.kind { + Symbol => { + let attr = Local::new(token); + acc = Accessor::attr(Expr::Accessor(acc), vis, attr); + } + Newline => { + self.restore(token); + self.restore(vis); + break; + } _ => { self.restore(token); self.level -= 1; @@ -738,563 +486,6 @@ impl Parser { Ok(acc) } - fn opt_reduce_params(&mut self) -> Option> { - match self.peek() { - Some(t) - if t.category_is(TC::Literal) - || t.is(Symbol) - || t.category_is(TC::UnaryOp) - || t.is(Dot) - || t.category_is(TC::Caret) - || t.is(LParen) - || t.is(LSqBr) - || t.is(LBrace) => - { - Some(self.try_reduce_params()) - } - _ => None, - } - } - - fn try_reduce_params(&mut self) -> ParseResult { - debug_call_info!(self); - let lp = if self.cur_is(TokenKind::LParen) { - Some(self.lpop()) - } else { - None - }; - let mut non_default_params = vec![]; - let mut default_params = vec![]; - let mut default_appeared = false; - match self.peek() { - Some(t) if t.is(RParen) => { - let parens = (lp.unwrap(), self.lpop()); - self.level -= 1; - return Ok(Params::new( - non_default_params, - None, - default_params, - Some(parens), - )); - } - Some(t) if t.is_block_op() => { - if lp.is_none() { - self.level -= 1; - return Ok(Params::new(non_default_params, None, default_params, None)); - } - // TODO: RParen not found - else { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - Some(_) => { - let param = self.try_reduce_param_sig().map_err(|_| self.stack_dec())?; - if param.has_default() { - default_appeared = true; - default_params.push(param); - } else { - non_default_params.push(param); - } - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - loop { - match self.peek() { - Some(t) if t.is(Comma) => { - self.skip(); - if self.cur_is(Spread) { - todo!("user-defined subroutine with a variable length parameter is not supported yet") - } - let param = self.try_reduce_param_sig().map_err(|_| self.stack_dec())?; - match (param.has_default(), default_appeared) { - (true, true) => { - default_params.push(param); - } - (true, false) => { - default_appeared = true; - default_params.push(param); - } - (false, true) => { - self.level -= 1; - let err = ParseError::syntax_error( - 0, - param.loc(), - // TODO: switch_lang! - "non-default argument follows default argument", - None, - ); - self.errs.push(err); - return Err(()); - } - (false, false) => { - non_default_params.push(param); - } - } - } - Some(t) if t.category_is(TC::BinOp) => { - let err = ParseError::syntax_error( - line!() as usize, - t.loc(), - switch_lang!( - "japanese" => "仮引数の中で中置演算子は使えません", - "simplified_chinese" => "参数中不能使用二元运算符", - "traditional_chinese" => "參數中不能使用二元運算符", - "english" => "Binary operators cannot be used in parameters", - ), - None, - ); - self.next_expr(); - self.level -= 1; - self.errs.push(err); - return Err(()); - } - Some(t) if t.is(TokenKind::RParen) => { - let rp = self.lpop(); - if let Some(lp) = lp { - self.level -= 1; - return Ok(Params::new( - non_default_params, - None, - default_params, - Some((lp, rp)), - )); - } else { - self.level -= 1; - // LParen not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - _ if lp.is_none() => { - self.level -= 1; - return Ok(Params::new(non_default_params, None, default_params, None)); - } - _ => { - self.level -= 1; - // TODO: RParen not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - } - - fn try_reduce_var_pattern(&mut self) -> ParseResult { - debug_call_info!(self); - match self.peek() { - Some(t) if t.is(Symbol) => { - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - if self.peek().map(|t| t.is(Comma)).unwrap_or(false) { - let pat = VarPattern::Ident(ident); - let mut idents = vec![VarSignature::new(pat, None)]; - loop { - match self.peek() { - Some(t) if t.is(Comma) => { - self.skip(); - let pat = self - .try_reduce_var_pattern() - .map_err(|_| self.stack_dec())?; - // ()なしのTupleの場合はflatten - match pat { - VarPattern::Tuple(tuple) if tuple.paren.is_none() => { - idents.extend(tuple.elems.elems); - } - other => { - let var_sig = VarSignature::new(other, None); - idents.push(var_sig); - } - } - } - _ => { - break; - } - } - } - let tuple_pat = VarTuplePattern::new(None, Vars::new(idents)); - self.level -= 1; - return Ok(VarPattern::Tuple(tuple_pat)); - } else { - self.level -= 1; - Ok(VarPattern::Ident(ident)) - } - } - Some(t) if t.is(UBar) => { - self.level -= 1; - Ok(VarPattern::Discard(self.lpop())) - } - Some(t) if t.is(LSqBr) => { - let l_sqbr = self.lpop(); - let elems = self - .try_reduce_var_pattern() - .map_err(|_| self.stack_dec())?; - let elems = match elems { - VarPattern::Tuple(tuple) if tuple.paren.is_none() => { - Vars::new(tuple.elems.elems) - } - other => Vars::new(vec![VarSignature::new(other, None)]), - }; - if self.cur_is(RSqBr) { - let r_sqbr = self.lpop(); - self.level -= 1; - Ok(VarPattern::Array(VarArrayPattern::new( - l_sqbr, elems, r_sqbr, - ))) - } else { - self.level -= 1; - // TODO: error report: RSqBr not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - Err(()) - } - } - Some(t) if t.is(LParen) => { - let lparen = self.lpop(); - let mut pat = self - .try_reduce_var_pattern() - .map_err(|_| self.stack_dec())?; - if self.cur_is(RParen) { - let rparen = self.lpop(); - match &mut pat { - VarPattern::Tuple(tuple) => { - tuple.paren = Some((lparen, rparen)); - } - _ => {} - } - self.level -= 1; - Ok(pat) - } else { - self.level -= 1; - // TODO: error report: RParen not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - Err(()) - } - } - Some(t) if t.category_is(TC::BinOp) || t.category_is(TC::UnaryOp) => { - log!(info "error caused by: {}", fn_name!()); - let err = ParseError::syntax_error( - line!() as usize, - t.loc(), - switch_lang!( - "japanese" => "左辺値の中で演算子は使えません", - "simplified_chinese" => "运算符不能用于左值", - "traditional_chinese" => "運算符不能用於左值", - "english" => "Operators cannot be used in left-values", - ), - None, - ); - self.next_expr(); - self.level -= 1; - self.errs.push(err); - return Err(()); - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - - fn try_reduce_param_pattern(&mut self) -> ParseResult { - debug_call_info!(self); - match self.peek() { - Some(t) if t.is(Symbol) => { - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - if let Some(dot) = &ident.dot { - let loc = dot.loc(); - self.level -= 1; - self.errs.push(ParseError::syntax_error( - line!() as usize, - loc, - switch_lang!( - "japanese" => "仮引数に`.`は使えません", - "simplified_chinese" => "参数不能使用点(`.`)", - "traditional_chinese" => "參數不能使用點(`.`)", - "english" => "`.` is not allowed in parameters", - ), - None, - )); - return Err(()); - } - self.level -= 1; - Ok(ParamPattern::VarName(ident.name)) - } - Some(t) if t.is(UBar) => { - self.level -= 1; - Ok(ParamPattern::Discard(self.lpop())) - } - Some(t) if t.category_is(TC::Literal) => { - let lit = self.try_reduce_lit().map_err(|_| self.stack_dec())?; - self.level -= 1; - // TODO: range pattern - Ok(ParamPattern::Lit(lit)) - } - Some(t) if t.is(Spread) => { - self.skip(); - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - if let Some(dot) = &ident.dot { - let loc = dot.loc(); - self.level -= 1; - self.errs.push(ParseError::syntax_error( - line!() as usize, - loc, - switch_lang!( - "japanese" => "仮引数に`.`は使えません", - "simplified_chinese" => "参数不能使用点(`.`)", - "traditional_chinese" => "參數不能使用點(`.`)", - "english" => "`.` is not allowed in parameters", - ), - None, - )); - return Err(()); - } - self.level -= 1; - Ok(ParamPattern::VarArgsName(ident.name)) - } - Some(t) if t.is(LSqBr) => { - let l_sqbr = self.lpop(); - let elems = self.try_reduce_params().map_err(|_| self.stack_dec())?; - if self.cur_is(RSqBr) { - let r_sqbr = self.lpop(); - self.level -= 1; - Ok(ParamPattern::Array(ParamArrayPattern::new( - l_sqbr, elems, r_sqbr, - ))) - } else { - self.level -= 1; - // TODO: error report: RSqBr not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - Some(t) if t.is(LParen) => { - self.skip(); - let pat = self - .try_reduce_param_pattern() - .map_err(|_| self.stack_dec())?; - if self.cur_is(RParen) { - self.skip(); - self.level -= 1; - Ok(pat) - } else { - self.level -= 1; - // TODO: error report: RParen not found - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - - // TODO: set type - fn try_reduce_type_spec(&mut self) -> ParseResult { - debug_call_info!(self); - let mut typ = match self.peek() { - Some(t) if t.is(Symbol) => { - let simple = self - .try_reduce_simple_type_spec() - .map_err(|_| self.stack_dec())?; - // not finished - TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(simple)) - } - Some(t) if t.category_is(TC::Literal) => { - let lit = self.try_reduce_lit().map_err(|_| self.stack_dec())?; - let lhs = ConstExpr::Lit(lit); - let maybe_op = self.lpop(); - let op = if maybe_op.is(Closed) - || maybe_op.is(LeftOpen) - || maybe_op.is(RightOpen) - || maybe_op.is(Open) - { - maybe_op - } else { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - }; - // TODO: maybe Name - let rhs = ConstExpr::Lit(self.try_reduce_lit().map_err(|_| self.stack_dec())?); - TypeSpec::interval(op, lhs, rhs) - } - Some(t) if t.is(LParen) => { - let func_type_spec = self.try_reduce_func_type().map_err(|_| self.stack_dec())?; - func_type_spec - } - Some(t) if t.is(LSqBr) => { - self.skip(); - let mut tys = vec![self.try_reduce_type_spec().map_err(|_| self.stack_dec())?]; - loop { - match self.peek() { - Some(t) if t.is(Comma) => { - self.skip(); - let t = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - tys.push(t); - } - Some(t) if t.is(RSqBr) => { - self.skip(); - break; - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - TypeSpec::Tuple(tys) - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - }; - loop { - match self.peek() { - Some(t) if t.is(AndOp) => { - let rhs = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - typ = TypeSpec::and(typ, rhs); - } - Some(t) if t.is(OrOp) => { - let rhs = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - typ = TypeSpec::or(typ, rhs); - } - Some(t) if t.category_is(TC::LambdaOp) => { - let is_func = t.is(FuncArrow); - self.skip(); - let rhs = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - typ = if is_func { - TypeSpec::func(None, vec![ParamTySpec::anonymous(typ)], None, vec![], rhs) - } else { - TypeSpec::proc(None, vec![ParamTySpec::anonymous(typ)], None, vec![], rhs) - }; - } - _ => { - break; - } - } - } - self.level -= 1; - Ok(typ) - } - - fn try_reduce_func_type_param(&mut self) -> ParseResult { - debug_call_info!(self); - if self.cur_is(Symbol) && self.nth_is(1, Colon) { - // TODO: handle `.` - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - self.skip(); - let typ = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(ParamTySpec::new(Some(ident.name.into_token()), typ)) - } else { - let ty_spec = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(ParamTySpec::anonymous(ty_spec)) - } - } - - // TODO: default parameters - fn try_reduce_func_type(&mut self) -> ParseResult { - debug_call_info!(self); - let lparen = Some(self.lpop()); - let mut non_defaults = vec![self - .try_reduce_func_type_param() - .map_err(|_| self.stack_dec())?]; - loop { - match self.peek() { - Some(t) if t.is(Comma) => { - self.skip(); - non_defaults.push( - self.try_reduce_func_type_param() - .map_err(|_| self.stack_dec())?, - ); - } - Some(t) if t.is(RParen) => { - self.skip(); - break; - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - match self.peek() { - Some(t) if t.category_is(TC::LambdaOp) => { - let is_func = t.is(FuncArrow); - self.skip(); - let rhs = self.try_reduce_type_spec().map_err(|_| self.stack_dec())?; - self.level -= 1; - if is_func { - Ok(TypeSpec::func(lparen, non_defaults, None, vec![], rhs)) - } else { - Ok(TypeSpec::proc(lparen, non_defaults, None, vec![], rhs)) - } - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - - #[inline] - fn try_reduce_simple_type_spec(&mut self) -> ParseResult { - debug_call_info!(self); - match self.peek() { - Some(t) if t.is(Symbol) => { - // TODO: handle dot (`.`) - let ident = self.try_reduce_ident().map_err(|_| self.stack_dec())?; - if let Some(res) = self.opt_reduce_args() { - let args = self.validate_const_args(res?)?; - self.level -= 1; - Ok(SimpleTypeSpec::new(ident.name, args)) - } else { - self.level -= 1; - Ok(SimpleTypeSpec::new(ident.name, ConstArgs::empty())) - } - } - _ => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - - fn try_reduce_bounds(&mut self) -> ParseResult { - todo!() - } - fn validate_const_expr(&mut self, expr: Expr) -> ParseResult { match expr { Expr::Lit(l) => Ok(ConstExpr::Lit(l)), @@ -1320,39 +511,6 @@ impl Parser { } } - fn validate_const_pos_arg(&mut self, arg: PosArg) -> ParseResult { - let expr = self.validate_const_expr(arg.expr)?; - Ok(ConstPosArg::new(expr)) - } - - fn validate_const_kw_arg(&mut self, arg: KwArg) -> ParseResult { - let expr = self.validate_const_expr(arg.expr)?; - Ok(ConstKwArg::new(arg.keyword, expr)) - } - - // exprが定数式か確認する - fn validate_const_args(&mut self, args: Args) -> ParseResult { - let (pos, kw, paren) = args.deconstruct(); - let mut const_args = ConstArgs::new(vec![], vec![], paren); - for arg in pos.into_iter() { - match self.validate_const_pos_arg(arg) { - Ok(arg) => { - const_args.push_pos(arg); - } - Err(e) => return Err(e), - } - } - for arg in kw.into_iter() { - match self.validate_const_kw_arg(arg) { - Ok(arg) => { - const_args.push_kw(arg); - } - Err(e) => return Err(e), - } - } - Ok(const_args) - } - /// For parsing elements of arrays and tuples fn try_reduce_elems(&mut self) -> ParseResult { debug_call_info!(self); @@ -1436,15 +594,15 @@ impl Parser { if t.category_is(TC::Literal) || t.is(Symbol) || t.category_is(TC::UnaryOp) - || t.is(Dot) - || t.category_is(TC::Caret) || t.is(LParen) || t.is(LSqBr) - || t.is(LBrace) - || t.is(Colon) => + || t.is(LBrace) => { Some(self.try_reduce_args()) } + Some(t) if (t.is(Dot) || t.is(DblColon)) && !self.nth_is(1, Newline) => { + Some(self.try_reduce_args()) + } _ => None, } } @@ -1452,9 +610,9 @@ impl Parser { /// 引数はインデントで区切ることができる(ただしコンマに戻すことはできない) /// /// ``` - /// x = if true, 1, 2 + /// x = if True, 1, 2 /// # is equal to - /// x = if true: + /// x = if True: /// 1 /// 2 /// ``` @@ -1574,28 +732,33 @@ impl Parser { self.level -= 1; return Ok(PosOrKwArg::Pos(PosArg::new(Expr::Lambda(lambda)))); } - if self.nth_is(1, Colon) { + if self.nth_is(1, Walrus) { let acc = self.try_reduce_acc().map_err(|_| self.stack_dec())?; - debug_power_assert!(self.cur_is(Colon)); - if self.nth_is(1, Newline) { - self.level -= 1; - // colon style call - Ok(PosOrKwArg::Pos(PosArg::new(Expr::Accessor(acc)))) + // TODO: type specification + debug_power_assert!(self.cur_is(Walrus)); + self.skip(); + let kw = if let Accessor::Local(n) = acc { + n.symbol } else { - self.skip(); - let kw = if let Accessor::Local(n) = acc { - n.symbol - } else { - self.next_expr(); - self.level -= 1; - let err = ParseError::simple_syntax_error(0, acc.loc()); - self.errs.push(err); - return Err(()); - }; - let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + self.next_expr(); self.level -= 1; - Ok(PosOrKwArg::Kw(KwArg::new(kw, expr))) - } + let err = ParseError::simple_syntax_error(0, acc.loc()); + self.errs.push(err); + return Err(()); + }; + let t_spec = if self.cur_is(Colon) { + self.skip(); + let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + let t_spec = self + .convert_rhs_to_type_spec(expr) + .map_err(|_| self.stack_dec())?; + Some(t_spec) + } else { + None + }; + let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(PosOrKwArg::Kw(KwArg::new(kw, t_spec, expr))) } else { let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; self.level -= 1; @@ -1615,9 +778,9 @@ impl Parser { debug_call_info!(self); match self.peek() { Some(t) if t.is(Symbol) => { - if self.nth_is(1, Colon) { + if self.nth_is(1, Walrus) { let acc = self.try_reduce_acc().map_err(|_| self.stack_dec())?; - debug_power_assert!(self.cur_is(Colon)); + debug_power_assert!(self.cur_is(Walrus)); self.skip(); let keyword = if let Accessor::Local(n) = acc { n.symbol @@ -1628,9 +791,19 @@ impl Parser { .push(ParseError::simple_syntax_error(0, acc.loc())); return Err(()); }; + let t_spec = if self.cur_is(Colon) { + self.skip(); + let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + let t_spec = self + .convert_rhs_to_type_spec(expr) + .map_err(|_| self.stack_dec())?; + Some(t_spec) + } else { + None + }; let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(KwArg::new(keyword, expr)) + Ok(KwArg::new(keyword, t_spec, expr)) } else { let loc = t.loc(); self.level -= 1; @@ -1648,50 +821,37 @@ impl Parser { } } - fn try_reduce_const_expr(&mut self) -> ParseResult { + fn try_reduce_method_defs(&mut self, class: Expr, vis: Token) -> ParseResult { debug_call_info!(self); - let expr = self.try_reduce_expr(true).map_err(|_| self.stack_dec())?; - self.level -= 1; - self.validate_const_expr(expr) - } - - #[inline] - fn try_reduce_def(&mut self) -> ParseResult { - debug_call_info!(self); - let sig = self.try_reduce_decl().map_err(|_| self.stack_dec())?; - match self.peek() { - Some(t) if t.is(Equal) => { - let op = self.lpop(); - self.counter.inc(); - let block = self.try_reduce_block().map_err(|_| self.stack_dec())?; - let body = DefBody::new(op, block, self.counter); - self.level -= 1; - Ok(Def::new(sig, body)) - } - _other => { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); + if self.cur_is(Indent) { + self.skip(); + } else { + todo!() + } + let first = self.try_reduce_chunk(false).map_err(|_| self.stack_dec())?; + let first = option_enum_unwrap!(first, Expr::Def).unwrap_or_else(|| todo!()); + let mut defs = vec![first]; + loop { + match self.peek() { + Some(t) if t.category_is(TC::Separator) => { + self.skip(); + if self.cur_is(Dedent) { + self.skip(); + break; + } + let def = self.try_reduce_chunk(false).map_err(|_| self.stack_dec())?; + let def = option_enum_unwrap!(def, Expr::Def).unwrap_or_else(|| todo!()); + defs.push(def); + } + _ => todo!(), } } - } - - #[inline] - fn try_reduce_lambda(&mut self) -> ParseResult { - debug_call_info!(self); - let sig = self.try_reduce_lambda_sig().map_err(|_| self.stack_dec())?; - let op = self.lpop(); - if op.category() != TC::LambdaOp { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - let body = self.try_reduce_block().map_err(|_| self.stack_dec())?; - self.counter.inc(); + let defs = RecordAttrs::from(defs); + let class = self + .convert_rhs_to_type_spec(class) + .map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(Lambda::new(sig, op, body, self.counter)) + Ok(MethodDefs::new(class, vis, defs)) } fn try_reduce_do_block(&mut self) -> ParseResult { @@ -1717,147 +877,343 @@ impl Parser { } } + fn try_reduce_chunk(&mut self, winding: bool) -> ParseResult { + debug_call_info!(self); + let mut stack = Vec::::new(); + stack.push(ExprOrOp::Expr( + self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, + )); + loop { + match self.peek() { + Some(op) if op.category_is(TC::DefOp) => { + let op = self.lpop(); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let sig = self.convert_rhs_to_sig(lhs).map_err(|_| self.stack_dec())?; + self.counter.inc(); + let block = self.try_reduce_block().map_err(|_| self.stack_dec())?; + let body = DefBody::new(op, block, self.counter); + stack.push(ExprOrOp::Expr(Expr::Def(Def::new(sig, body)))); + } + Some(op) if op.category_is(TC::LambdaOp) => { + let op = self.lpop(); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let sig = self + .convert_rhs_to_lambda_sig(lhs) + .map_err(|_| self.stack_dec())?; + self.counter.inc(); + let block = self.try_reduce_block().map_err(|_| self.stack_dec())?; + stack.push(ExprOrOp::Expr(Expr::Lambda(Lambda::new( + sig, + op, + block, + self.counter, + )))); + } + Some(op) if op.is(Colon) => { + let _op = self.lpop(); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let t_spec = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + let t_spec = self + .convert_rhs_to_type_spec(t_spec) + .map_err(|_| self.stack_dec())?; + let expr = Expr::TypeAsc(TypeAscription::new(lhs, t_spec)); + stack.push(ExprOrOp::Expr(expr)); + } + Some(op) if op.category_is(TC::BinOp) => { + let op_prec = op.kind.precedence(); + if stack.len() >= 2 { + while let Some(ExprOrOp::Op(prev_op)) = stack.get(stack.len() - 2) { + if prev_op.category_is(TC::BinOp) + && prev_op.kind.precedence() >= op_prec + { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let prev_op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(prev_op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } else { + break; + } + if stack.len() <= 1 { + break; + } + } + } + stack.push(ExprOrOp::Op(self.lpop())); + stack.push(ExprOrOp::Expr( + self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, + )); + } + Some(t) if t.is(DblColon) => { + let vis = self.lpop(); + match self.lpop() { + symbol if symbol.is(Symbol) => { + let obj = if let Some(ExprOrOp::Expr(expr)) = stack.pop() { + expr + } else { + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + }; + if let Some(args) = self + .opt_reduce_args() + .transpose() + .map_err(|_| self.stack_dec())? + { + let call = Call::new(obj, Some(symbol), args); + stack.push(ExprOrOp::Expr(Expr::Call(call))); + } else { + let acc = Accessor::attr(obj, vis, Local::new(symbol)); + stack.push(ExprOrOp::Expr(Expr::Accessor(acc))); + } + } + line_break if line_break.is(Newline) => { + let maybe_class = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let defs = self + .try_reduce_method_defs(maybe_class, vis) + .map_err(|_| self.stack_dec())?; + return Ok(Expr::MethodDefs(defs)); + } + other => { + self.restore(other); + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } + } + } + Some(t) if t.is(Dot) => { + let vis = self.lpop(); + match self.lpop() { + symbol if symbol.is(Symbol) => { + let obj = if let Some(ExprOrOp::Expr(expr)) = stack.pop() { + expr + } else { + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + }; + if let Some(args) = self + .opt_reduce_args() + .transpose() + .map_err(|_| self.stack_dec())? + { + let call = Call::new(obj, Some(symbol), args); + stack.push(ExprOrOp::Expr(Expr::Call(call))); + } else { + let acc = Accessor::attr(obj, vis, Local::new(symbol)); + stack.push(ExprOrOp::Expr(Expr::Accessor(acc))); + } + } + line_break if line_break.is(Newline) => { + let maybe_class = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let defs = self + .try_reduce_method_defs(maybe_class, vis) + .map_err(|_| self.stack_dec())?; + return Ok(Expr::MethodDefs(defs)); + } + other => { + self.restore(other); + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } + } + } + Some(t) if t.is(Comma) && winding => { + let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let tup = self + .try_reduce_tuple(first_elem) + .map_err(|_| self.stack_dec())?; + stack.push(ExprOrOp::Expr(Expr::Tuple(tup))); + } + _ => { + if stack.len() <= 1 { + break; + } + // else if stack.len() == 2 { switch_unreachable!() } + else { + while stack.len() >= 3 { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } + } + } + } + } + match stack.pop() { + Some(ExprOrOp::Expr(expr)) if stack.is_empty() => { + self.level -= 1; + Ok(expr) + } + Some(ExprOrOp::Expr(expr)) => { + let extra = stack.pop().unwrap(); + let loc = match extra { + ExprOrOp::Expr(expr) => expr.loc(), + ExprOrOp::Op(op) => op.loc(), + }; + self.warns + .push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); + self.level -= 1; + Ok(expr) + } + Some(ExprOrOp::Op(op)) => { + self.level -= 1; + self.errs + .push(ParseError::compiler_bug(0, op.loc(), fn_name!(), line!())); + Err(()) + } + _ => switch_unreachable!(), + } + } + /// winding: true => parse paren-less tuple fn try_reduce_expr(&mut self, winding: bool) -> ParseResult { debug_call_info!(self); let mut stack = Vec::::new(); - match self.cur_side() { - Side::LhsAssign => { - let def = self.try_reduce_def().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(Expr::Def(def)) - } - Side::LhsLambda => { - let lambda = self.try_reduce_lambda().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(Expr::Lambda(lambda)) - } - Side::Do => { - let lambda = self.try_reduce_do_block().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(Expr::Lambda(lambda)) - } - Side::Rhs => { - stack.push(ExprOrOp::Expr( - self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, - )); - loop { - match self.peek() { - Some(op) if op.category_is(TC::BinOp) => { - let op_prec = op.kind.precedence(); - if stack.len() >= 2 { - while let Some(ExprOrOp::Op(prev_op)) = stack.get(stack.len() - 2) { - if prev_op.category_is(TC::BinOp) - && prev_op.kind.precedence() >= op_prec - { - let rhs = - enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); - let prev_op = - enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); - let lhs = - enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); - let bin = BinOp::new(prev_op, lhs, rhs); - stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); - } else { - break; - } - if stack.len() <= 1 { - break; - } - } + stack.push(ExprOrOp::Expr( + self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, + )); + loop { + match self.peek() { + Some(op) if op.category_is(TC::LambdaOp) => { + let op = self.lpop(); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let sig = self + .convert_rhs_to_lambda_sig(lhs) + .map_err(|_| self.stack_dec())?; + self.counter.inc(); + let block = self.try_reduce_block().map_err(|_| self.stack_dec())?; + stack.push(ExprOrOp::Expr(Expr::Lambda(Lambda::new( + sig, + op, + block, + self.counter, + )))); + } + Some(op) if op.is(Colon) => { + let _op = self.lpop(); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let t_spec = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + let t_spec = self + .convert_rhs_to_type_spec(t_spec) + .map_err(|_| self.stack_dec())?; + let expr = Expr::TypeAsc(TypeAscription::new(lhs, t_spec)); + stack.push(ExprOrOp::Expr(expr)); + } + Some(op) if op.category_is(TC::BinOp) => { + let op_prec = op.kind.precedence(); + if stack.len() >= 2 { + while let Some(ExprOrOp::Op(prev_op)) = stack.get(stack.len() - 2) { + if prev_op.category_is(TC::BinOp) + && prev_op.kind.precedence() >= op_prec + { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let prev_op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(prev_op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } else { + break; } - stack.push(ExprOrOp::Op(self.lpop())); - stack.push(ExprOrOp::Expr( - self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, - )); - } - Some(t) if t.category_is(TC::DefOp) => { - switch_unreachable!() - } - Some(t) if t.is(Dot) => { - self.skip(); - match self.lpop() { - symbol if symbol.is(Symbol) => { - let obj = if let Some(ExprOrOp::Expr(expr)) = stack.pop() { - expr - } else { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - }; - if let Some(args) = self - .opt_reduce_args() - .transpose() - .map_err(|_| self.stack_dec())? - { - let call = Call::new(obj, Some(symbol), args); - stack.push(ExprOrOp::Expr(Expr::Call(call))); - } else { - let acc = Accessor::attr(obj, Local::new(symbol)); - stack.push(ExprOrOp::Expr(Expr::Accessor(acc))); - } - } - other => { - self.restore(other); - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); - } - } - } - Some(t) if t.is(Comma) && winding => { - let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); - let tup = self - .try_reduce_tuple(first_elem) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - return Ok(Expr::Tuple(tup)); - } - _ => { if stack.len() <= 1 { break; } - // else if stack.len() == 2 { switch_unreachable!() } - else { - while stack.len() >= 3 { - let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); - let op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); - let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); - let bin = BinOp::new(op, lhs, rhs); - stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); - } + } + } + stack.push(ExprOrOp::Op(self.lpop())); + stack.push(ExprOrOp::Expr( + self.try_reduce_bin_lhs().map_err(|_| self.stack_dec())?, + )); + } + Some(t) if t.is(Dot) => { + let vis = self.lpop(); + match self.lpop() { + symbol if symbol.is(Symbol) => { + let obj = if let Some(ExprOrOp::Expr(expr)) = stack.pop() { + expr + } else { + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + }; + if let Some(args) = self + .opt_reduce_args() + .transpose() + .map_err(|_| self.stack_dec())? + { + let call = Call::new(obj, Some(symbol), args); + stack.push(ExprOrOp::Expr(Expr::Call(call))); + } else { + let acc = Accessor::attr(obj, vis, Local::new(symbol)); + stack.push(ExprOrOp::Expr(Expr::Accessor(acc))); } } + other => { + self.restore(other); + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } } } - match stack.pop() { - Some(ExprOrOp::Expr(expr)) if stack.is_empty() => { - self.level -= 1; - Ok(expr) + Some(t) if t.is(Comma) && winding => { + let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let tup = self + .try_reduce_tuple(first_elem) + .map_err(|_| self.stack_dec())?; + stack.push(ExprOrOp::Expr(Expr::Tuple(tup))); + } + _ => { + if stack.len() <= 1 { + break; } - Some(ExprOrOp::Expr(expr)) => { - let extra = stack.pop().unwrap(); - let loc = match extra { - ExprOrOp::Expr(expr) => expr.loc(), - ExprOrOp::Op(op) => op.loc(), - }; - self.warns - .push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); - self.level -= 1; - Ok(expr) + // else if stack.len() == 2 { switch_unreachable!() } + else { + while stack.len() >= 3 { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } } - Some(ExprOrOp::Op(op)) => { - self.level -= 1; - self.errs - .push(ParseError::compiler_bug(0, op.loc(), fn_name!(), line!())); - Err(()) - } - _ => switch_unreachable!(), } } } + match stack.pop() { + Some(ExprOrOp::Expr(expr)) if stack.is_empty() => { + self.level -= 1; + Ok(expr) + } + Some(ExprOrOp::Expr(expr)) => { + let extra = stack.pop().unwrap(); + let loc = match extra { + ExprOrOp::Expr(expr) => expr.loc(), + ExprOrOp::Op(op) => op.loc(), + }; + self.warns + .push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); + self.level -= 1; + Ok(expr) + } + Some(ExprOrOp::Op(op)) => { + self.level -= 1; + self.errs + .push(ParseError::compiler_bug(0, op.loc(), fn_name!(), line!())); + Err(()) + } + _ => switch_unreachable!(), + } } /// "LHS" is the smallest unit that can be the left-hand side of an BinOp. @@ -1871,23 +1227,43 @@ impl Parser { self.level -= 1; Ok(Expr::Lit(lit)) } + Some(t) if t.is(AtSign) => { + let decos = self.opt_reduce_decorators()?; + let expr = self.try_reduce_chunk(false)?; + let mut def = option_enum_unwrap!(expr, Expr::Def).unwrap_or_else(|| todo!()); + match def.sig { + Signature::Subr(mut subr) => { + subr.decorators = decos; + let expr = Expr::Def(Def::new(Signature::Subr(subr), def.body)); + Ok(expr) + } + Signature::Var(var) => { + let mut last = def.body.block.pop().unwrap(); + for deco in decos.into_iter() { + last = Expr::Call(Call::new( + deco.into_expr(), + None, + Args::new(vec![PosArg::new(last)], vec![], None), + )); + } + def.body.block.push(last); + let expr = Expr::Def(Def::new(Signature::Var(var), def.body)); + Ok(expr) + } + } + } Some(t) if t.is(Symbol) || t.is(Dot) => { - let acc = self + let call_or_acc = self .try_reduce_call_or_acc() .map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(acc) + Ok(call_or_acc) } Some(t) if t.category_is(TC::UnaryOp) => { let unaryop = self.try_reduce_unary().map_err(|_| self.stack_dec())?; self.level -= 1; Ok(Expr::UnaryOp(unaryop)) } - Some(t) if t.category_is(TC::Caret) => { - let lambda = self.try_reduce_lambda().map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(Expr::Lambda(lambda)) - } Some(t) if t.is(LParen) => { let lparen = self.lpop(); if self.cur_is(RParen) { @@ -2030,8 +1406,7 @@ impl Parser { todo!() } } - // TODO: K: V - let first = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; + let first = self.try_reduce_chunk(false).map_err(|_| self.stack_dec())?; match first { Expr::Def(def) => { let record = self @@ -2040,7 +1415,7 @@ impl Parser { self.level -= 1; Ok(BraceContainer::Record(record)) } - Expr::Decl(_) => todo!(), // invalid syntax + Expr::TypeAsc(_) => todo!(), // invalid syntax other => { let set = self .try_reduce_set(l_brace, other) @@ -2051,7 +1426,7 @@ impl Parser { } } - fn try_reduce_record(&mut self, l_brace: Token, first: Def) -> ParseResult { + fn try_reduce_record(&mut self, l_brace: Token, first: Def) -> ParseResult { debug_call_info!(self); let mut attrs = vec![first]; loop { @@ -2064,19 +1439,20 @@ impl Parser { let r_brace = self.lpop(); self.level -= 1; let attrs = RecordAttrs::from(attrs); - return Ok(Record::new(l_brace, r_brace, attrs)); + return Ok(NormalRecord::new(l_brace, r_brace, attrs)); } else { todo!() } } - let def = self.try_reduce_def().map_err(|_| self.stack_dec())?; + let def = self.try_reduce_chunk(false).map_err(|_| self.stack_dec())?; + let def = option_enum_unwrap!(def, Expr::Def).unwrap_or_else(|| todo!()); attrs.push(def); } Some(term) if term.is(RBrace) => { let r_brace = self.lpop(); self.level -= 1; let attrs = RecordAttrs::from(attrs); - return Ok(Record::new(l_brace, r_brace, attrs)); + return Ok(NormalRecord::new(l_brace, r_brace, attrs)); } _ => todo!(), } @@ -2116,17 +1492,6 @@ impl Parser { PosOrKwArg::Kw(_arg) => todo!(), } } - Some(t) if t.is(Newline) => { - while self.cur_is(Newline) { - self.skip(); - } - match self.try_reduce_arg().map_err(|_| self.stack_dec())? { - PosOrKwArg::Pos(arg) => { - args.push_pos(arg); - } - PosOrKwArg::Kw(_arg) => todo!(), - } - } _ => { break; } @@ -2137,30 +1502,6 @@ impl Parser { Ok(tup) } - #[inline] - fn try_reduce_ident(&mut self) -> ParseResult { - debug_call_info!(self); - self.level -= 1; - match self.peek() { - Some(t) if t.is(Dot) => { - let dot = self.lpop(); - // TODO: - assert!(self.cur_category_is(TC::Symbol)); - let ident = Identifier::new(Some(dot), VarName::new(self.lpop())); - Ok(ident) - } - Some(t) if t.is(Symbol) => { - let ident = Identifier::new(None, VarName::new(self.lpop())); - Ok(ident) - } - _ => { - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - Err(()) - } - } - } - #[inline] fn try_reduce_lit(&mut self) -> ParseResult { debug_call_info!(self); @@ -2174,4 +1515,490 @@ impl Parser { } } } + + /// Call: F(x) -> SubrSignature: F(x) + fn convert_rhs_to_sig(&mut self, rhs: Expr) -> ParseResult { + debug_call_info!(self); + match rhs { + Expr::Accessor(accessor) => { + let var = self + .convert_accessor_to_var_sig(accessor) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(Signature::Var(var)) + } + Expr::Call(call) => { + let subr = self + .convert_call_to_subr_sig(call) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(Signature::Subr(subr)) + } + Expr::Array(array) => { + let array_pat = self + .convert_array_to_array_pat(array) + .map_err(|_| self.stack_dec())?; + let var = VarSignature::new(VarPattern::Array(array_pat), None); + self.level -= 1; + Ok(Signature::Var(var)) + } + Expr::Record(record) => { + let record_pat = self + .convert_record_to_record_pat(record) + .map_err(|_| self.stack_dec())?; + let var = VarSignature::new(VarPattern::Record(record_pat), None); + self.level -= 1; + Ok(Signature::Var(var)) + } + Expr::Tuple(tuple) => { + let tuple_pat = self + .convert_tuple_to_tuple_pat(tuple) + .map_err(|_| self.stack_dec())?; + let var = VarSignature::new(VarPattern::Tuple(tuple_pat), None); + self.level -= 1; + Ok(Signature::Var(var)) + } + Expr::TypeAsc(tasc) => { + let sig = self + .convert_type_asc_to_sig(tasc) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(sig) + } + other => todo!("{other}"), // Error + } + } + + fn convert_accessor_to_var_sig(&mut self, _accessor: Accessor) -> ParseResult { + debug_call_info!(self); + match _accessor { + Accessor::Local(local) => { + let pat = VarPattern::Ident(Identifier::new(None, VarName::new(local.symbol))); + self.level -= 1; + Ok(VarSignature::new(pat, None)) + } + Accessor::Public(public) => { + let pat = VarPattern::Ident(Identifier::new( + Some(public.dot), + VarName::new(public.symbol), + )); + self.level -= 1; + Ok(VarSignature::new(pat, None)) + } + other => todo!("{other}"), + } + } + + fn convert_array_to_array_pat(&mut self, _array: Array) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_record_to_record_pat( + &mut self, + _record: NormalRecord, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_tuple_to_tuple_pat(&mut self, tuple: Tuple) -> ParseResult { + debug_call_info!(self); + let mut vars = Vars::empty(); + match tuple { + Tuple::Normal(tup) => { + let (pos_args, _kw_args, paren) = tup.elems.deconstruct(); + for arg in pos_args { + let sig = self + .convert_rhs_to_sig(arg.expr) + .map_err(|_| self.stack_dec())?; + match sig { + Signature::Var(var) => { + vars.push(var); + } + other => todo!("{other}"), + } + } + let tuple = VarTuplePattern::new(paren, vars); + self.level -= 1; + Ok(tuple) + } + } + } + + fn convert_type_asc_to_sig(&mut self, tasc: TypeAscription) -> ParseResult { + debug_call_info!(self); + let sig = self + .convert_rhs_to_sig(*tasc.expr) + .map_err(|_| self.stack_dec())?; + let sig = match sig { + Signature::Var(var) => { + let var = VarSignature::new(var.pat, Some(tasc.t_spec)); + Signature::Var(var) + } + Signature::Subr(subr) => { + let subr = SubrSignature::new( + subr.decorators, + subr.ident, + subr.params, + Some(tasc.t_spec), + subr.bounds, + ); + Signature::Subr(subr) + } + }; + self.level -= 1; + Ok(sig) + } + + fn convert_call_to_subr_sig(&mut self, call: Call) -> ParseResult { + debug_call_info!(self); + let ident = match *call.obj { + Expr::Accessor(acc) => self + .convert_accessor_to_ident(acc) + .map_err(|_| self.stack_dec())?, + other => todo!("{other}"), // Error + }; + let params = self + .convert_args_to_params(call.args) + .map_err(|_| self.stack_dec())?; + let sig = SubrSignature::new(set! {}, ident, params, None, TypeBoundSpecs::empty()); + self.level -= 1; + Ok(sig) + } + + fn convert_accessor_to_ident(&mut self, _accessor: Accessor) -> ParseResult { + debug_call_info!(self); + let ident = match _accessor { + 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}"), // Error + }; + self.level -= 1; + Ok(ident) + } + + fn convert_args_to_params(&mut self, args: Args) -> ParseResult { + debug_call_info!(self); + let (pos_args, kw_args, parens) = args.deconstruct(); + let mut params = Params::new(vec![], None, vec![], parens); + for arg in pos_args.into_iter() { + let nd_param = self + .convert_pos_arg_to_non_default_param(arg) + .map_err(|_| self.stack_dec())?; + params.non_defaults.push(nd_param); + } + // TODO: varargs + for arg in kw_args.into_iter() { + let d_param = self + .convert_kw_arg_to_default_param(arg) + .map_err(|_| self.stack_dec())?; + params.defaults.push(d_param); + } + self.level -= 1; + Ok(params) + } + + fn convert_pos_arg_to_non_default_param(&mut self, arg: PosArg) -> ParseResult { + debug_call_info!(self); + let param = self + .convert_rhs_to_param(arg.expr) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(param) + } + + fn convert_rhs_to_param(&mut self, expr: Expr) -> ParseResult { + debug_call_info!(self); + match expr { + Expr::Accessor(Accessor::Local(local)) => { + let name = VarName::new(local.symbol); + let pat = ParamPattern::VarName(name); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + Expr::Lit(lit) => { + let pat = ParamPattern::Lit(lit); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + Expr::Array(array) => { + let array_pat = self + .convert_array_to_param_array_pat(array) + .map_err(|_| self.stack_dec())?; + let pat = ParamPattern::Array(array_pat); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + Expr::Record(record) => { + let record_pat = self + .convert_record_to_param_record_pat(record) + .map_err(|_| self.stack_dec())?; + let pat = ParamPattern::Record(record_pat); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + Expr::Tuple(tuple) => { + let tuple_pat = self + .convert_tuple_to_param_tuple_pat(tuple) + .map_err(|_| self.stack_dec())?; + let pat = ParamPattern::Tuple(tuple_pat); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + Expr::TypeAsc(tasc) => { + let param = self + .convert_type_asc_to_param_pattern(tasc) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(param) + } + Expr::Call(mut call) => match *call.obj { + Expr::Accessor(Accessor::Local(local)) => match &local.inspect()[..] { + "ref" => { + assert_eq!(call.args.len(), 1); + let var = call.args.remove_pos(0).expr; + let var = option_enum_unwrap!(var, Expr::Accessor:(Accessor::Local:(_))) + .unwrap_or_else(|| todo!()); + let pat = ParamPattern::Ref(VarName::new(var.symbol)); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + "ref!" => { + assert_eq!(call.args.len(), 1); + let var = call.args.remove_pos(0).expr; + let var = option_enum_unwrap!(var, Expr::Accessor:(Accessor::Local:(_))) + .unwrap_or_else(|| todo!()); + let pat = ParamPattern::RefMut(VarName::new(var.symbol)); + let param = ParamSignature::new(pat, None, None); + self.level -= 1; + Ok(param) + } + other => todo!("{other}"), + }, + other => todo!("{other}"), + }, + other => todo!("{other}"), // Error + } + } + + fn convert_kw_arg_to_default_param(&mut self, arg: KwArg) -> ParseResult { + debug_call_info!(self); + let pat = ParamPattern::VarName(VarName::new(arg.keyword)); + let expr = self.validate_const_expr(arg.expr)?; + let param = ParamSignature::new(pat, arg.t_spec, Some(expr)); + self.level -= 1; + Ok(param) + } + + fn convert_array_to_param_array_pat( + &mut self, + _array: Array, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_record_to_param_record_pat( + &mut self, + _record: NormalRecord, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_tuple_to_param_tuple_pat( + &mut self, + _tuple: Tuple, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_type_asc_to_param_pattern( + &mut self, + tasc: TypeAscription, + ) -> ParseResult { + debug_call_info!(self); + let param = self + .convert_rhs_to_param(*tasc.expr) + .map_err(|_| self.stack_dec())?; + let param = ParamSignature::new(param.pat, Some(tasc.t_spec), None); + self.level -= 1; + Ok(param) + } + + fn convert_rhs_to_lambda_sig(&mut self, rhs: Expr) -> ParseResult { + debug_call_info!(self); + match rhs { + Expr::Accessor(accessor) => { + let param = self + .convert_accessor_to_param_sig(accessor) + .map_err(|_| self.stack_dec())?; + let params = Params::new(vec![param], None, vec![], None); + self.level -= 1; + Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty())) + } + Expr::Tuple(tuple) => { + let params = self + .convert_tuple_to_params(tuple) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty())) + } + Expr::Array(array) => { + let arr = self + .convert_array_to_param_pat(array) + .map_err(|_| self.stack_dec())?; + let param = ParamSignature::new(ParamPattern::Array(arr), None, None); + let params = Params::new(vec![param], None, vec![], None); + self.level -= 1; + Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty())) + } + Expr::Record(record) => { + let rec = self + .convert_record_to_param_pat(record) + .map_err(|_| self.stack_dec())?; + let param = ParamSignature::new(ParamPattern::Record(rec), None, None); + let params = Params::new(vec![param], None, vec![], None); + self.level -= 1; + Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty())) + } + Expr::TypeAsc(tasc) => { + let sig = self + .convert_type_asc_to_lambda_sig(tasc) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(sig) + } + other => todo!("{other}"), // Error + } + } + + fn convert_accessor_to_param_sig( + &mut self, + _accessor: Accessor, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_tuple_to_params(&mut self, tuple: Tuple) -> ParseResult { + debug_call_info!(self); + match tuple { + Tuple::Normal(tup) => { + let (pos_args, kw_args, paren) = tup.elems.deconstruct(); + let mut params = Params::new(vec![], None, vec![], paren); + for arg in pos_args { + let param = self + .convert_pos_arg_to_non_default_param(arg) + .map_err(|_| self.stack_dec())?; + params.non_defaults.push(param); + } + for arg in kw_args { + let param = self + .convert_kw_arg_to_default_param(arg) + .map_err(|_| self.stack_dec())?; + params.defaults.push(param); + } + self.level -= 1; + Ok(params) + } + } + } + + fn convert_array_to_param_pat(&mut self, _array: Array) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_record_to_param_pat( + &mut self, + _record: NormalRecord, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_type_asc_to_lambda_sig( + &mut self, + _tasc: TypeAscription, + ) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_rhs_to_type_spec(&mut self, rhs: Expr) -> ParseResult { + debug_call_info!(self); + match rhs { + Expr::Accessor(acc) => { + let predecl = self + .convert_accessor_to_predecl_type_spec(acc) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(TypeSpec::PreDeclTy(predecl)) + } + Expr::Call(call) => { + let predecl = self + .convert_call_to_predecl_type_spec(call) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(TypeSpec::PreDeclTy(predecl)) + } + Expr::Lambda(lambda) => { + let lambda = self + .convert_lambda_to_subr_type_spec(lambda) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(TypeSpec::Subr(lambda)) + } + Expr::Array(array) => { + let array = self + .convert_array_to_array_type_spec(array) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(TypeSpec::Array(array)) + } + other => todo!("{other}"), + } + } + + fn convert_accessor_to_predecl_type_spec( + &mut self, + accessor: Accessor, + ) -> ParseResult { + debug_call_info!(self); + let t_spec = match accessor { + Accessor::Local(local) => PreDeclTypeSpec::Simple(SimpleTypeSpec::new( + VarName::new(local.symbol), + ConstArgs::empty(), + )), + other => todo!("{other}"), + }; + self.level -= 1; + Ok(t_spec) + } + + fn convert_call_to_predecl_type_spec(&mut self, _call: Call) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_lambda_to_subr_type_spec(&mut self, _lambda: Lambda) -> ParseResult { + debug_call_info!(self); + todo!() + } + + fn convert_array_to_array_type_spec(&mut self, _array: Array) -> ParseResult { + debug_call_info!(self); + todo!() + } } diff --git a/compiler/erg_parser/token.rs b/compiler/erg_parser/token.rs index 27431d56..4dabc27b 100644 --- a/compiler/erg_parser/token.rs +++ b/compiler/erg_parser/token.rs @@ -105,8 +105,8 @@ pub enum TokenKind { CrossOp, /// = Equal, - /// |= - OrEqual, + /// := + Walrus, /// -> FuncArrow, /// => @@ -175,7 +175,7 @@ pub enum TokenCategory { LEnclosure, /// ) } } Dedent REnclosure, - /// , : :: :> <: . |> |= + /// , : :: :> <: . |> := SpecialBinOp, /// = DefOp, @@ -212,7 +212,7 @@ impl TokenKind { | InfLit => TokenCategory::Literal, PrePlus | PreMinus | PreBitNot | Mutate => TokenCategory::UnaryOp, Try => TokenCategory::PostfixOp, - Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | OrEqual => { + Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | Walrus => { TokenCategory::SpecialBinOp } Equal => TokenCategory::DefOp, @@ -250,7 +250,7 @@ impl TokenKind { FuncArrow | ProcArrow => 60, // -> => Colon | SupertypeOf | SubtypeOf => 50, // : :> <: Comma => 40, // , - Equal | OrEqual => 20, // = |= + Equal | Walrus => 20, // = := Newline | Semi => 10, // \n ; LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent _ => return None, diff --git a/compiler/erg_type/lib.rs b/compiler/erg_type/lib.rs index 6f7eaee4..a803395a 100644 --- a/compiler/erg_type/lib.rs +++ b/compiler/erg_type/lib.rs @@ -704,7 +704,7 @@ impl LimitedDisplay for SubrType { var_params.typ().limited_fmt(f, limit - 1)?; } for pt in self.default_params.iter() { - write!(f, ", {} |= ", pt.name().unwrap())?; + write!(f, ", {} := ", pt.name().unwrap())?; pt.typ().limited_fmt(f, limit - 1)?; } write!(f, ") {} ", self.kind.arrow())?; @@ -1075,7 +1075,7 @@ impl fmt::Display for ArgsOwnership { write!(f, ", ...{o:?}")?; } for (name, o) in self.defaults.iter() { - write!(f, ", {name} |= {o:?}")?; + write!(f, ", {name} := {o:?}")?; } write!(f, ")")?; Ok(()) diff --git a/doc/EN/syntax/00_basic.md b/doc/EN/syntax/00_basic.md index e8558d45..bfd645d5 100644 --- a/doc/EN/syntax/00_basic.md +++ b/doc/EN/syntax/00_basic.md @@ -3,7 +3,7 @@ [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/00_basic.md%26commit_hash%3D21e8145e83fb54ed77e7631deeee8a7e39b028a3) ](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/00_basic.md&commit_hash=21e8145e83fb54ed77e7631deeee8a7e39b028a3) -> __Info__: This document is incomplete. It has not been proofread (style, correct links, mistranslation, etc.). Also, Erg's syntax may be change destructively during version 0.*, and the documentation may not have been updated accordingly. Please be aware of this beforehand. +> __Warning__: This document is incomplete. It has not been proofread (style, correct links, mistranslation, etc.). Also, Erg's syntax may be change destructively during version 0.*, and the documentation may not have been updated accordingly. Please be aware of this beforehand. > If you find any errors in this document, please report then to [here form](https://forms.gle/HtLYRfYzWCAaeTGb6) or [GitHub repo](https://github.com/mtshiba/TheErgBook/issues/new). We would appreciate your suggestions. > > [The Erg book original version (Japanese)](http://mtshiba.me/TheErgBook/) diff --git a/doc/EN/syntax/03_declaration.md b/doc/EN/syntax/03_declaration.md index c2bd464e..1c2ef5a8 100644 --- a/doc/EN/syntax/03_declaration.md +++ b/doc/EN/syntax/03_declaration.md @@ -41,7 +41,7 @@ T = Trait { .f = (x: Int, y: Int): Int } -C = Class(U, Impl: T) +C = Class(U, Impl := T) C.f(a: Int, b: Int): Int = ... # TypeError: `.f` must be type of `(x: Int, y: Int) -> Int`, not `(a: Int, b: Int) -> Int` ``` diff --git a/doc/EN/syntax/04_function.md b/doc/EN/syntax/04_function.md index d9493eac..8a3bcdc9 100644 --- a/doc/EN/syntax/04_function.md +++ b/doc/EN/syntax/04_function.md @@ -85,10 +85,10 @@ f x: Default parameters are used when some parameters are mostly fixed and you want to be able to omit them. -Default parameters are specified by `|=`(or-assign operator). If `base` is not specified, assign `math.E` to `base`. +Default parameters are specified by `:=`(walrus operator). If `base` is not specified, assign `math.E` to `base`. ```erg -math_log x: Ratio, base |= math.E = ... +math_log x: Ratio, base := math.E = ... assert math_log(100, 10) == 2 assert math_log(100) == math_log(100, math.E) @@ -97,7 +97,7 @@ assert math_log(100) == math_log(100, math.E) Note that there is a distinction between specifying no argument and assigning `None`. ```erg -p! x |= 0 = print! +p! x := 0 = print! p!(2) # 2 p!() # 0 p!(None) # None @@ -106,20 +106,20 @@ p!(None) # None Can also be used with type specification and patterns. ```erg -math_log x, base: Ratio |= math.E = ... -f [x, y] |= [1, 2] = ... +math_log x, base: Ratio := math.E = ... +f [x, y] := [1, 2] = ... ``` However, within the default arguments, it is not possible to call the procedures (described later) or assign mutable objects. ```erg -f x |= p! 1 = ... # NG +f x := p! 1 = ... # NG ``` Also, the argument just defined cannot be used as the value passed to the default argument. ```erg -f x |= 1, y |= x = ... # NG +f x := 1, y := x = ... # NG ``` ## Variable-length arguments diff --git a/doc/EN/syntax/26_pattern_matching.md b/doc/EN/syntax/26_pattern_matching.md index d3524ef3..d82c0b86 100644 --- a/doc/EN/syntax/26_pattern_matching.md +++ b/doc/EN/syntax/26_pattern_matching.md @@ -134,20 +134,20 @@ f {x: Int; y: Int} = ... ```erg Point = Inherit {x = Int; y = Int} -p = Point.{x = 1; y = 2} -Point.{x; y} = p +p = Point::{x = 1; y = 2} +Point::{x; y} = p -Nil T = Class Impl: Phantom T +Nil T = Class Impl := Phantom T Cons T = Inherit {head = T; rest = List T} List T = Enum Nil(T), Cons(T) List T. first self = match self: - Cons.{head; ...} -> x + Cons::{head; ...} -> x _ -> ... second self = match self: - Cons.{rest=Cons.{head; ...} ; ...} -> head + Cons::{rest=Cons::{head; ...} ; ...} -> head _ -> ... ``` diff --git a/doc/EN/syntax/29_decorator.md b/doc/EN/syntax/29_decorator.md index 81a295d1..1aa3fc52 100644 --- a/doc/EN/syntax/29_decorator.md +++ b/doc/EN/syntax/29_decorator.md @@ -20,7 +20,21 @@ X = ... X = deco(X) ``` -Since Erg does not allow reassignment, the above code will not pass, and a decorator is required. +Erg does not allow reassignment of variables, so the above code will not pass. +For a simple variable, `X = deco(...)` is the same, but for instant blocks and subroutines, you can't do that, so you need decorators. + +```erg +@deco +f x = ... + y = ... + x + y + +# Can also prevent code from going horizontal. +@LongNameDeco1 +@LongNameDeco2 +C = Class ... +``` + Here are some frequently used built-in decorators. ## Inheritable @@ -37,7 +51,7 @@ Use to override an attribute, which by default Erg will fail if you try to defin ## Impl -Indicates implementation of the argument trace. +Indicates implementation of traits. ```erg ClosedAdd = Trait { @@ -47,11 +61,11 @@ ClosedSub = Trait { . `_-_` = Self.(Self) -> Self } -C = Class({i = Int}, Impl: ClosedAdd and ClosedSub) +C = Class {i = Int} C. - @Impl Add. + @Impl ClosedAdd `_+_` self, other = C.new {i = self::i + other::i} - @Impl Sub + @Impl ClosedSub `_-_` self, other = C.new {i = self::i - other::} ``` @@ -70,9 +84,9 @@ Add R = Trait { @Attach AddForInt, AddForOdd ClosedAdd = Subsume Add(Self) -AddForInt = Patch(Int, Impl: ClosedAdd) +AddForInt = Patch(Int, Impl := ClosedAdd) AddForInt.AddO = Int -AddForOdd = Patch(Odd, Impl: ClosedAdd) +AddForOdd = Patch(Odd, Impl := ClosedAdd) AddForOdd.AddO = Even ``` @@ -86,7 +100,7 @@ assert Int.AddO == Int assert Odd.AddO == Even ``` -Internally, they are only connected together using the trait's `.attach` method. If there is a conflict, it can be removed using the trace's `.detach` method. +Internally, they are only connected together using the trait's `.attach` method. If there is a conflict, it can be removed using the trait's `.detach` method. ```erg @Attach X diff --git a/doc/EN/syntax/31_pipeline.md b/doc/EN/syntax/31_pipeline.md index ba3e805e..956ea22c 100644 --- a/doc/EN/syntax/31_pipeline.md +++ b/doc/EN/syntax/31_pipeline.md @@ -17,7 +17,7 @@ It looks like just an increase in `|>`, but since the bond strength is low, the ``` erg rand = -1.0..1.0 |>.sample!() log rand # 0.2597... -1+1*2 |>.times do log("a", end: "") # aaa +1+1*2 |>.times do log("a", end := "") # aaa # without `|>`, the following will be `evens = (1..100).iter().filter(i -> i % 2 == 0).collect(Array)` evens = 1..100 |>.iter |>.filter i -> i % 2 == 0 |>.collect Array # or diff --git a/doc/EN/syntax/grammar.txt b/doc/EN/syntax/grammar.txt index 36df1935..deaff920 100644 --- a/doc/EN/syntax/grammar.txt +++ b/doc/EN/syntax/grammar.txt @@ -1,9 +1,8 @@ # The Grammar of Erg (ver 0.1.0, provisional) -special_op ::= '=' | '->' | '=>' | '.' | ',' | ':' | '|>' | '&' +special_op ::= '=' | '->' | '=>' | '.' | ',' | ':' | '::' | '|>' | '&' separator ::= ';' | '\n' escape ::= '\' comment_marker ::= '#' -comment ::= '#' .* '\n' reserved_symbol ::= special_op | separator | comment_marker number ::= [0-9] first_last_dight ::= number @@ -28,7 +27,8 @@ parenthesis ::= '(' | ')' bracket ::= '{' | '}' square_bracket ::= '[' | ']' enclosure ::= parenthesis | bracket | square_bracket -infix_op ::= '+' | '-' | '*' | '/' | '//' | '**' | '%' | '&&' | '||' | '^^' | '<' | '<=' | '>' | '>=' +infix_op ::= '+' | '-' | '*' | '/' | '//' | '**' + | '%' | '&&' | '||' | '^^' | '<' | '<=' | '>' | '>=' | 'and' | 'or' | 'is' | 'as' | 'isnot' | 'in' | 'notin' | 'dot' | 'cross' prefix_op ::= '+' | '-' | '*' | '**' | '..' | '..<' | '~' | '&' | '!' postfix_op ::= '?' | '..' | '<..' @@ -68,10 +68,21 @@ decorator ::= call lambda_func ::= params_opt_t '->' body lambda_proc ::= params_opt_t '=>' body lambda ::= lambda_func | lambda_proc -array ::= '[' enc_args ']' +normal_array ::= '[' enc_args ']' array_comprehension ::= '[' expr | (generator)+ ']' -anonymous_type ::= '{' enc_args '}' +array ::= normal_array | array_comprehension +record ::= '{' '=' '}' + | '{' def (';' def)* ';'? '}' +set ::= '{' '}' + | '{' expr (',' expr)* ','? '}' +dict ::= '{' ':' '}' + | '{' symbol ':' expr (',' symbol ':' expr)* ','? '}' +tuple ::= '(' ')' + | '(' expr (',' expr)* ','? ')' indent ::= /* ... */ -expr ::= accessor | literal | prefix | infix | postfix | call | def | lambda +expr ::= accessor | literal + | prefix | infix | postfix + | array | record | set | dict | tuple + | call | def | lambda line ::= expr separator+ program ::= expr? | (line | comment)* diff --git a/doc/EN/syntax/type/02_basic.md b/doc/EN/syntax/type/02_basic.md index fa3814a5..0a150c0d 100644 --- a/doc/EN/syntax/type/02_basic.md +++ b/doc/EN/syntax/type/02_basic.md @@ -70,7 +70,7 @@ You can also use a subtype specification when defining a class to statically che ```erg # Class C is a subtype of Show -C = Class Object, Impl: Show +C = Class Object, Impl := Show C.show self = ... # Show's required attributes. ``` diff --git a/doc/EN/syntax/type/03_trait.md b/doc/EN/syntax/type/03_trait.md index b8c923fc..2ac2aa6f 100644 --- a/doc/EN/syntax/type/03_trait.md +++ b/doc/EN/syntax/type/03_trait.md @@ -27,7 +27,7 @@ Point2D <: Norm # TypeError: Point2D is not a subtype of Norm Point2D = Class {.x = Int; .y = Int} ``` -Traits, like structural types, can apply operations such as composition, substitution, and elimination (e.g. `T and U`). The resulting trace is called an instant trace. +Traits, like structural types, can apply operations such as composition, substitution, and elimination (e.g. `T and U`). The resulting trait is called an instant trait. ```erg T = Trait {.x = Int} @@ -49,7 +49,7 @@ assert points.iter().map(x -> x.norm()).collect(Array) == [5, 25]. ## Trait inclusion -The expansion operator `...` allows you to define a trace that contains a certain trace as a supertype. This is called the __subsumption__ of a trace. +The expansion operator `...` allows you to define a trait that contains a certain trait as a supertype. This is called the __subsumption__ of a trait. In the example below, `BinAddSub` subsumes `BinAdd` and `BinSub`. This corresponds to Inheritance in a class, but unlike Inheritance, multiple base types can be combined using `and`. Traits that are partially excluded by `not` are also allowed. @@ -87,7 +87,7 @@ assert add(C.new(1), C.new(2)) == C.new(3) ``` Nominal traits cannot be used simply by implementing a request method, but must be explicitly declared to have been implemented. -In the following example, `add` cannot be used with an argument of type `C` because there is no explicit declaration of implementation. It must be `C = Class {i = Int}, Impl: Add`. +In the following example, `add` cannot be used with an argument of type `C` because there is no explicit declaration of implementation. It must be `C = Class {i = Int}, Impl := Add`. ```erg Add = Trait { diff --git a/doc/EN/syntax/type/05_inheritance.md b/doc/EN/syntax/type/05_inheritance.md index 67e02759..7a21d9e3 100644 --- a/doc/EN/syntax/type/05_inheritance.md +++ b/doc/EN/syntax/type/05_inheritance.md @@ -3,7 +3,7 @@ [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/type/05_inheritance.md%26commit_hash%3D8586bf6f02bd04fd5c823b3a476238881ef037de)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/type/05_inheritance.md&commit_hash=8586bf6f02bd04fd5c823b3a476238881ef037de) Inheritance allows you to define a new class that adds functionality or specialization to an existing class. -Inheritance is similar to inclusion in a trace. The inherited class becomes a subtype of the original class. +Inheritance is similar to inclusion in a trait. The inherited class becomes a subtype of the original class. ```erg NewInt = Inherit Int @@ -126,7 +126,7 @@ Although it is not possible to replace traits at inheritance time, there are exa For example, `Int`, a subtype of `Real` (which implements `Add()`), appears to reimplement `Add()`. ```erg -Int = Class ... , Impl: Add() and ... +Int = Class ... , Impl := Add() and ... ``` But in fact `Add()` in `Real` stands for `Add(Real, Real)`, and in `Int` it is just overwritten by `Add(Int, Int)`. diff --git a/doc/EN/syntax/type/07_patch.md b/doc/EN/syntax/type/07_patch.md index cfdae640..422c6b3f 100644 --- a/doc/EN/syntax/type/07_patch.md +++ b/doc/EN/syntax/type/07_patch.md @@ -113,7 +113,7 @@ Reverse = Trait { .reverse = Self.() -> Self } -StrReverse = Patch Str, Impl: Reverse +StrReverse = Patch Str, Impl := Reverse StrReverse. reverse self = self.iter().rev().collect(Str) @@ -128,7 +128,7 @@ NumericStr = Inherit Str NumericStr. ... -NumStrRev = Patch NumericStr, Impl: Reverse +NumStrRev = Patch NumericStr, Impl := Reverse NumStrRev. ... # DuplicatePatchError: NumericStr is already associated with `Reverse` @@ -160,7 +160,7 @@ Reverse = Trait { .reverse = Self.() -> Self } -StrReverse = Patch(Str, Impl: Reverse) +StrReverse = Patch(Str, Impl := Reverse) StrReverse. reverse self = self.iter().rev().collect(Str) diff --git a/doc/EN/syntax/type/14_dependent.md b/doc/EN/syntax/type/14_dependent.md index af5d2cb6..41934e0c 100644 --- a/doc/EN/syntax/type/14_dependent.md +++ b/doc/EN/syntax/type/14_dependent.md @@ -34,7 +34,7 @@ f x = print! f::x, module::x # Phantom types have an attribute called Phantom that has the same value as the type argument -T X: Int = Class Impl: Phantom X +T X: Int = Class Impl := Phantom X T(X). x self = self::Phantom @@ -46,7 +46,7 @@ Transitions are specified with `~>`. ```erg # Note that `Id` is an immutable type and cannot be transitioned. -VM!(State: {"stopped", "running"}! |= _, Id: Nat |= _) = Class(... State). +VM!(State: {"stopped", "running"}! := _, Id: Nat := _) = Class(... State). VM!(). # Variables that do not change can be omitted by passing `_`. start! ref! self("stopped" ~> "running") = diff --git a/doc/EN/syntax/type/16_subtyping.md b/doc/EN/syntax/type/16_subtyping.md index 44ae7607..1e99cd71 100644 --- a/doc/EN/syntax/type/16_subtyping.md +++ b/doc/EN/syntax/type/16_subtyping.md @@ -53,7 +53,7 @@ assert not c in D ## Subtyping of subroutines Arguments and return values of subroutines take only a single class. -In other words, you cannot directly specify a structural type or a trace as the type of a function. +In other words, you cannot directly specify a structural type or a trait as the type of a function. It must be specified as "a single class that is a subtype of that type" using the partial type specification. ```erg @@ -66,7 +66,7 @@ f2 x, y: Add = x + y f3 x, y: A = x + y ``` -Type inference in subroutines also follows this rule. When a variable in a subroutine has an unspecified type, the compiler first checks to see if it is an instance of one of the classes, and if not, looks for a match in the scope of the trace. If it still cannot find one, a compile error occurs. This error can be resolved by using a structural type, but since inferring an anonymous type may have unintended consequences for the programmer, it is designed to be explicitly specified by the programmer with `Structural`. +Type inference in subroutines also follows this rule. When a variable in a subroutine has an unspecified type, the compiler first checks to see if it is an instance of one of the classes, and if not, looks for a match in the scope of the trait. If it still cannot find one, a compile error occurs. This error can be resolved by using a structural type, but since inferring an anonymous type may have unintended consequences for the programmer, it is designed to be explicitly specified by the programmer with `Structural`. ## Class upcasting diff --git a/doc/EN/syntax/type/advanced/GADTs.md b/doc/EN/syntax/type/advanced/GADTs.md index 52e7c5c0..90ba2d8b 100644 --- a/doc/EN/syntax/type/advanced/GADTs.md +++ b/doc/EN/syntax/type/advanced/GADTs.md @@ -5,8 +5,8 @@ Erg can create Generalized Algebraic Data Types (GADTs) by classifying Or (Union) types. ```erg -Nil T = Class(Impl: Phantom T) -Cons T = Class {head = T; rest = List T}, Impl: Unpack +Nil T = Class(Impl := Phantom T) +Cons T = Class {head = T; rest = List T}, Impl := Unpack List T: Type = Class(Nil T or Cons T) List. nil|T|() = Self(T).new Nil(T).new() @@ -32,8 +32,8 @@ For example, the `.head` method above will give a runtime error if the contents ```erg List: (Type, {"Empty", "Nonempty"}) -> Type -List T, "Empty" = Class(Impl: Phantom T) -List T, "Nonempty" = Class {head = T; rest = List(T, _)}, Impl: Unpack +List T, "Empty" = Class(Impl := Phantom T) +List T, "Nonempty" = Class {head = T; rest = List(T, _)}, Impl := Unpack List. nil|T|() = Self(T, "Empty").new Nil(T).new() cons head, rest | T = Self(T, "Nonempty").new {head; rest} @@ -50,8 +50,8 @@ Erg allows for further refinement, defining a list with length. ```erg List: (Type, Nat) -> Type -List T, 0 = Class(Impl: Phantom T) -List T, N = Class {head = T; rest = List(T, N-1)}, Impl: Unpack +List T, 0 = Class(Impl := Phantom T) +List T, N = Class {head = T; rest = List(T, N-1)}, Impl := Unpack List. nil|T|() = Self(T, 0).new Nil(T).new() cons head, rest | T, N = Self(T, N).new {head; rest} diff --git a/doc/EN/syntax/type/advanced/default_param.md b/doc/EN/syntax/type/advanced/default_param.md index cbcb595d..458a882b 100644 --- a/doc/EN/syntax/type/advanced/default_param.md +++ b/doc/EN/syntax/type/advanced/default_param.md @@ -5,25 +5,25 @@ First, let's look at an example of the use of default parameters. ```erg -f: (Int, Int, z |= Int) -> Int -f(x, y, z |= 0) = x + y + z +f: (Int, Int, z := Int) -> Int +f(x, y, z := 0) = x + y + z -g: (Int, Int, z |= Int, w |= Int) -> Int -g(x, y, z |= 0, w |= 1) = x + y + z + w +g: (Int, Int, z := Int, w := Int) -> Int +g(x, y, z := 0, w := 1) = x + y + z + w -fold: ((Int, Int) -> Int, [Int], acc |= Int) -> Int +fold: ((Int, Int) -> Int, [Int], acc := Int) -> Int fold(f, [], acc) = acc -fold(f, arr, acc |= 0) = fold(f, arr[1...]) , f(acc, arr[0])) +fold(f, arr, acc := 0) = fold(f, arr[1...]) , f(acc, arr[0])) assert fold(f, [1, 2, 3]) == 6 assert fold(g, [1, 2, 3]) == 8 ``` -The parameters after `|=` are default parameters. +The parameters after `:=` are default parameters. The subtyping rules are as follows. ```erg -((X, y |= Y) -> Z) <: (X -> Z) -((X, y |= Y, ...) -> Z) -> Z) <: ((X, ...) -> Z) +((X, y := Y) -> Z) <: (X -> Z) +((X, y := Y, ...) -> Z) -> Z) <: ((X, ...) -> Z) ``` The first means that a function with a default parameter is identical to a function without it. diff --git a/doc/EN/syntax/type/advanced/marker_trait.md b/doc/EN/syntax/type/advanced/marker_trait.md index 4ac1b225..0c2e61ac 100644 --- a/doc/EN/syntax/type/advanced/marker_trait.md +++ b/doc/EN/syntax/type/advanced/marker_trait.md @@ -6,7 +6,7 @@ A marker trait is a trait with no required attributes. That is, it can be Impl w It may seem meaningless without the required attribute, but it registers the information that it belongs to that trait, so that patch methods can be used and the compiler can give it special treatment. All marker traits are encompassed by the `Marker` trait. -The `Light` provided in the standard is a kind of `marker` trace. +The `Light` provided in the standard is a kind of marker trait. ```erg Light = Subsume Marker @@ -19,7 +19,7 @@ Person = Class {.name = Str; .age = Nat} and Light ```erg M = Subsume Marker -MarkedInt = Inherit Int, Impl: M +MarkedInt = Inherit Int, Impl := M i = MarkedInt.new(2) assert i + 1 == 2 @@ -29,5 +29,5 @@ assert i in M The marker class can also be removed with the `Excluding` argument. ```erg -NInt = Inherit MarkedInt, Impl: N, Excluding: M +NInt = Inherit MarkedInt, Impl := N, Excluding: M ``` diff --git a/doc/EN/syntax/type/advanced/mut_struct.md b/doc/EN/syntax/type/advanced/mut_struct.md index ff42ce7a..1db1c803 100644 --- a/doc/EN/syntax/type/advanced/mut_struct.md +++ b/doc/EN/syntax/type/advanced/mut_struct.md @@ -5,7 +5,7 @@ The ``T!`` type is described as a box type that can be replaced by any ``T`` type object. ```erg -Particle!State: {"base", "excited"}! = Class(... Impl: Phantom State) +Particle!State: {"base", "excited"}! = Class(... Impl := Phantom State) Particle! # This method moves the State from "base" to "excited". apply_electric_field!(ref! self("base" ~> "excited"), field: Vector) = ... @@ -33,7 +33,7 @@ Incidentally, the `[T; !N]` type is the sugar-coated syntax of the `ArrayWithLen Mutable structure types can of course be user-defined. Note, however, that there are some differences from invariant structure types in terms of the construction method. ```erg -Nil T = Class(Impl: Phantom T) +Nil T = Class(Impl := Phantom T) List T, !0 = Inherit Nil T List T, N: Nat! = Class {head = T; rest = List(T, !N-1)} List(T, !N). diff --git a/doc/EN/syntax/type/advanced/overloading.md b/doc/EN/syntax/type/advanced/overloading.md index 03d42863..89023a57 100644 --- a/doc/EN/syntax/type/advanced/overloading.md +++ b/doc/EN/syntax/type/advanced/overloading.md @@ -2,17 +2,17 @@ [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/type/advanced/overloading.md%26commit_hash%3D317b5973c354984891523d14a5e6e8f1cc3923ec)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/type/advanced/overloading.md&commit_hash=317b5973c354984891523d14a5e6e8f1cc3923ec) -Erg does not support __ad hoc polymorphism__. That is, multiple definitions of functions and Kinds (overloading) are not possible. However, you can reproduce the overloading behavior by using a combination of a trace class and a patch. +Erg does not support __ad hoc polymorphism__. That is, multiple definitions of functions and Kinds (overloading) are not possible. However, you can reproduce the overloading behavior by using a combination of a trait and a patch. You can use traits instead of trait classes, but then all types that implement `.add1` will be covered. ```erg Add1 = Trait { .add1: Self.() -> Self } -IntAdd1 = Patch Int, Impl: Add1 +IntAdd1 = Patch Int, Impl := Add1 IntAdd1. add1 self = self + 1 -RatioAdd1 = Patch Ratio, Impl: Add1 +RatioAdd1 = Patch Ratio, Impl := Add1 RatioAdd1. add1 self = self + 1.0 @@ -37,7 +37,7 @@ Also, overloading of types with different numbers of arguments can be reproduced ```erg C = Class {.x = Int; .y = Int} C. - new(x, y |= 0) = Self::__new__ {.x; .y} + new(x, y := 0) = Self::__new__ {.x; .y} assert C.new(0, 0) == C.new(0) ``` @@ -64,7 +64,7 @@ Second, it is incompatible with default arguments. When a function with default ```erg f x: Int = ... -f(x: Int, y |= 0) = ... +f(x: Int, y := 0) = ... f(1) # which is chosen? ``` diff --git a/doc/EN/syntax/type/advanced/phantom.md b/doc/EN/syntax/type/advanced/phantom.md index 4ed7dd26..69537e3f 100644 --- a/doc/EN/syntax/type/advanced/phantom.md +++ b/doc/EN/syntax/type/advanced/phantom.md @@ -34,7 +34,7 @@ This code passes compilation. But it's a little tricky to understand the intent, In such a case, a phantom type is just what you need. A phantom type is a generalized type of size 0. ```erg -Nil T = Class(Impl: Phantom T) +Nil T = Class(Impl := Phantom T) List T, 0 = Inherit Nil T List T, N: Nat = Class {head = T; rest = List(T, N-1)} diff --git a/doc/EN/syntax/type/advanced/projection.md b/doc/EN/syntax/type/advanced/projection.md index 62837d1b..b8ea5885 100644 --- a/doc/EN/syntax/type/advanced/projection.md +++ b/doc/EN/syntax/type/advanced/projection.md @@ -10,7 +10,7 @@ Add R = Trait { .AddO = Type } -AddForInt = Patch(Int, Impl: Add Int) +AddForInt = Patch(Int, Impl := Add Int) AddForInt. AddO = Int ``` diff --git a/doc/EN/syntax/type/advanced/special.md b/doc/EN/syntax/type/advanced/special.md index cf34727e..19a25fef 100644 --- a/doc/EN/syntax/type/advanced/special.md +++ b/doc/EN/syntax/type/advanced/special.md @@ -42,7 +42,7 @@ Add R = Trait { } ClosedAdd = Subsume Add(Self) -ClosedAddForInt = Patch(Int, Impl: ClosedAdd) +ClosedAddForInt = Patch(Int, Impl := ClosedAdd) ClosedAddForInt. AddO = Int diff --git a/doc/EN/tips.md b/doc/EN/tips.md index 67067e11..255124d2 100644 --- a/doc/EN/tips.md +++ b/doc/EN/tips.md @@ -47,15 +47,15 @@ If you implement `Singleton`, classes and instances are identical. Also, if you use `Enum`, the type of choice is automatically defined as a redirect attribute. ```erg -Ok = Class Impl: Singleton -Err = Class Impl: Singleton +Ok = Class Impl := Singleton +Err = Class Impl := Singleton ErrWithInfo = Inherit {info = Str} Status = Enum Ok, Err, ErrWithInfo stat: Status = Status.cons(ErrWithInfo) {info = "error caused by ..."} match! stat: Status.Ok -> ... Status.Err -> ... - Status.ErrWithInfo.{info;} -> ... + Status.ErrWithInfo::{info} -> ... ``` ```erg diff --git a/doc/JA/API/types.md b/doc/JA/API/types.md index 909e470c..60b7e9d0 100644 --- a/doc/JA/API/types.md +++ b/doc/JA/API/types.md @@ -114,15 +114,15 @@ ClassはInt, Bool, Strなど Complex以外の例として、Vector, Matrix, TensorはNum(Matrix, Tensorの*はそれぞれdot, productと同じ) -### Complex (= Inherit(Object, Impl: Num)) +### Complex (= Inherit(Object, Impl := Num)) * `imag: Ratio`: 虚部を返す * `real: Ratio`: 実部を返す * `conjugate self -> Complex`: 共役複素数を返す -### Float (= Inherit(FloatComplex, Impl: Num)) +### Float (= Inherit(FloatComplex, Impl := Num)) -### Ratio (= Inherit(Complex, Impl: Num)) +### Ratio (= Inherit(Complex, Impl := Num)) * `numerator: Int`: 分子を返す * `denominator: Int`: 分母を返す diff --git a/doc/JA/API/types/classes/Interval.md b/doc/JA/API/types/classes/Interval.md index c59e3b4f..20588c1a 100644 --- a/doc/JA/API/types/classes/Interval.md +++ b/doc/JA/API/types/classes/Interval.md @@ -1,4 +1,4 @@ -# Interval begin, end: WellOrder +# Interval begin, end := WellOrder 整列集合型(WellOrder)の部分型を表す型です。Interval型にはPreOpen(x<..y)などの派生型が存在します。 diff --git a/doc/JA/compiler/inference.md b/doc/JA/compiler/inference.md index 0fe2b232..74b79282 100644 --- a/doc/JA/compiler/inference.md +++ b/doc/JA/compiler/inference.md @@ -1,6 +1,6 @@ # 型推論アルゴリズム -> __Info__: この項は編集中であり、一部に間違いを含む可能性があります。 +> __Warning__: この項は編集中であり、一部に間違いを含む可能性があります。 以下で用いる表記方法を示します。 diff --git a/doc/JA/syntax/00_basic.md b/doc/JA/syntax/00_basic.md index 700f6390..09c298a6 100644 --- a/doc/JA/syntax/00_basic.md +++ b/doc/JA/syntax/00_basic.md @@ -1,6 +1,6 @@ # 基本事項 -> __Info__: 本ドキュメントは未完成です。校正(文体、正しいリンクが張られているか、など)がなされていません。また、Ergの文法はバージョン0.*の間に破壊的変更が加えられる可能性があり、それに伴うドキュメントの更新が追いついていない可能性があります。予めご了承ください。 +> __Warning__: 本ドキュメントは未完成です。校正(文体、正しいリンクが張られているか、など)がなされていません。また、Ergの文法はバージョン0.*の間に破壊的変更が加えられる可能性があり、それに伴うドキュメントの更新が追いついていない可能性があります。予めご了承ください。 > また、本ドキュメントの誤りを見つけた場合は、[こちらのフォーム](https://forms.gle/HtLYRfYzWCAaeTGb6)または[GitHubリポジトリ](https://github.com/mtshiba/TheErgBook/issues/new)から修正の提案をしていただけると幸いです。 本ドキュメントは、Ergの基本文法について解説するものです。[標準API](../API/index.md)や[Ergコントリビューター向けの内部資料](../dev_guide/index.md)は別のディレクトリに置かれています。 diff --git a/doc/JA/syntax/03_declaration.md b/doc/JA/syntax/03_declaration.md index 268f3fa1..a0077a81 100644 --- a/doc/JA/syntax/03_declaration.md +++ b/doc/JA/syntax/03_declaration.md @@ -39,7 +39,7 @@ T = Trait { .f = (x: Int, y: Int): Int } -C = Class(U, Impl: T) +C = Class(U, Impl := T) C.f(a: Int, b: Int): Int = ... # TypeError: `.f` must be type of `(x: Int, y: Int) -> Int`, not `(a: Int, b: Int) -> Int` ``` diff --git a/doc/JA/syntax/04_function.md b/doc/JA/syntax/04_function.md index 39780394..abad4e52 100644 --- a/doc/JA/syntax/04_function.md +++ b/doc/JA/syntax/04_function.md @@ -84,10 +84,10 @@ f x: ある引数が大抵の場合決まりきっており省略できるようにしたい場合、デフォルト引数を使うと良いでしょう。 -デフォルト引数は`|=`(or-assign operator)で指定します。`base`が指定されなかったら`math.E`を`base`に代入します。 +デフォルト引数は`:=`(or-assign operator)で指定します。`base`が指定されなかったら`math.E`を`base`に代入します。 ```erg -math_log x: Ratio, base |= math.E = ... +math_log x: Ratio, base := math.E = ... assert math_log(100, 10) == 2 assert math_log(100) == math_log(100, math.E) @@ -96,7 +96,7 @@ assert math_log(100) == math_log(100, math.E) 引数を指定しないことと`None`を代入することは区別されるので注意してください。 ```erg -p! x |= 0 = print! x +p! x := 0 = print! x p!(2) # 2 p!() # 0 p!(None) # None @@ -105,20 +105,20 @@ p!(None) # None 型指定、パターンと併用することもできます。 ```erg -math_log x, base: Ratio |= math.E = ... -f [x, y] |= [1, 2] = ... +math_log x, base: Ratio := math.E = ... +f [x, y] := [1, 2] = ... ``` しかしデフォルト引数内では、後述するプロシージャを呼び出したり、可変オブジェクトを代入したりすることができません。 ```erg -f x |= p! 1 = ... # NG +f x := p! 1 = ... # NG ``` また、定義したばかりの引数はデフォルト引数に渡す値として使えません。 ```erg -f x |= 1, y |= x = ... # NG +f x := 1, y := x = ... # NG ``` ## 可変長引数 diff --git a/doc/JA/syntax/19_visibility.md b/doc/JA/syntax/19_visibility.md index 1209eaee..e28163df 100644 --- a/doc/JA/syntax/19_visibility.md +++ b/doc/JA/syntax/19_visibility.md @@ -141,7 +141,7 @@ foo.public() # AttributeError: 'Foo' has no attribute 'public' ('public' is defi {Foo; ...} = import "foo" FooImpl = Patch Foo -FooImpl:: +FooImpl :=: private self = pass FooImpl. public self = self::private() diff --git a/doc/JA/syntax/26_pattern_matching.md b/doc/JA/syntax/26_pattern_matching.md index 46bd341b..c86cc43f 100644 --- a/doc/JA/syntax/26_pattern_matching.md +++ b/doc/JA/syntax/26_pattern_matching.md @@ -135,20 +135,20 @@ f {x: Int; y: Int} = ... ```erg Point = Inherit {x = Int; y = Int} -p = Point.{x = 1; y = 2} -Point.{x; y} = p +p = Point::{x = 1; y = 2} +Point::{x; y} = p -Nil T = Class Impl: Phantom T +Nil T = Class Impl := Phantom T Cons T = Inherit {head = T; rest = List T} List T = Enum Nil(T), Cons(T) List T. first self = match self: - Cons.{head; ...} -> x + Cons::{head; ...} -> x _ -> ... second self = match self: - Cons.{rest=Cons.{head; ...}; ...} -> head + Cons::{rest=Cons::{head; ...}; ...} -> head _ -> ... ``` diff --git a/doc/JA/syntax/29_decorator.md b/doc/JA/syntax/29_decorator.md index dbd9f33a..90fd1587 100644 --- a/doc/JA/syntax/29_decorator.md +++ b/doc/JA/syntax/29_decorator.md @@ -17,7 +17,21 @@ X = ... X = deco(X) ``` -Ergでは変数の再代入が出来ないので、上のようなコードは通らず、デコレータが必要なのです。 +Ergでは変数の再代入が出来ないので、上のようなコードは通りません。 +単なる変数の場合は`X = deco(...)`と同じなのですが、インスタントブロックやサブルーチンの場合はそうすることができないので、デコレータが必要になってきます。 + +```erg +@deco +f x = + y = ... + x + y + +# コードが横長になるのを防ぐこともできる +@LongNameDeco1 +@LongNameDeco2 +C = Class ... +``` + 以下に、頻出の組み込みデコレータを紹介します。 ## Inheritable @@ -44,7 +58,7 @@ Sub = Trait { .`_-_` = Self.(Self) -> Self } -C = Class({i = Int}, Impl: Add and Sub) +C = Class({i = Int}, Impl := Add and Sub) C. @Impl Add `_+_` self, other = C.new {i = self::i + other::i} @@ -66,9 +80,9 @@ Add R = Trait { @Attach AddForInt, AddForOdd ClosedAdd = Subsume Add(Self) -AddForInt = Patch(Int, Impl: ClosedAdd) +AddForInt = Patch(Int, Impl := ClosedAdd) AddForInt.AddO = Int -AddForOdd = Patch(Odd, Impl: ClosedAdd) +AddForOdd = Patch(Odd, Impl := ClosedAdd) AddForOdd.AddO = Even ``` diff --git a/doc/JA/syntax/31_pipeline.md b/doc/JA/syntax/31_pipeline.md index 9956b61b..f4b708cd 100644 --- a/doc/JA/syntax/31_pipeline.md +++ b/doc/JA/syntax/31_pipeline.md @@ -15,7 +15,7 @@ assert f(g(x, y)) == ((x, y) |> g |> f) rand = -1.0..1.0 |>.sample!() log rand # 0.2597... -1+1*2 |>.times do log("a", end: "") # aaa +1+1*2 |>.times do log("a", end := "") # aaa evens = 1..100 |>.iter |>.filter i -> i % 2 == 0 |>.collect Array # パイプライン演算子を使わずに実装する場合、 diff --git a/doc/JA/syntax/grammar.txt b/doc/JA/syntax/grammar.txt index 36df1935..d9dbbb2a 100644 --- a/doc/JA/syntax/grammar.txt +++ b/doc/JA/syntax/grammar.txt @@ -1,9 +1,8 @@ # The Grammar of Erg (ver 0.1.0, provisional) -special_op ::= '=' | '->' | '=>' | '.' | ',' | ':' | '|>' | '&' +special_op ::= '=' | '->' | '=>' | '.' | ',' | ':' | '::' | '|>' | '&' separator ::= ';' | '\n' escape ::= '\' comment_marker ::= '#' -comment ::= '#' .* '\n' reserved_symbol ::= special_op | separator | comment_marker number ::= [0-9] first_last_dight ::= number @@ -28,7 +27,8 @@ parenthesis ::= '(' | ')' bracket ::= '{' | '}' square_bracket ::= '[' | ']' enclosure ::= parenthesis | bracket | square_bracket -infix_op ::= '+' | '-' | '*' | '/' | '//' | '**' | '%' | '&&' | '||' | '^^' | '<' | '<=' | '>' | '>=' +infix_op ::= '+' | '-' | '*' | '/' | '//' | '**' + | '%' | '&&' | '||' | '^^' | '<' | '<=' | '>' | '>=' | 'and' | 'or' | 'is' | 'as' | 'isnot' | 'in' | 'notin' | 'dot' | 'cross' prefix_op ::= '+' | '-' | '*' | '**' | '..' | '..<' | '~' | '&' | '!' postfix_op ::= '?' | '..' | '<..' @@ -68,10 +68,21 @@ decorator ::= call lambda_func ::= params_opt_t '->' body lambda_proc ::= params_opt_t '=>' body lambda ::= lambda_func | lambda_proc -array ::= '[' enc_args ']' +normal_array ::= '[' enc_args ']' array_comprehension ::= '[' expr | (generator)+ ']' -anonymous_type ::= '{' enc_args '}' +array ::= normal_array | array_comprehension +record ::= '{' '=' '}' + | '{' def (';' def)* ';'? '}' +set ::= '{' '}' + | '{' expr (',' expr)* ','? '}' +dict ::= '{' ':' '}' + | '{' expr ':' expr (',' expr ':' expr)* ','? '}' +tuple ::= '(' ')' + | '(' expr (',' expr)* ','? ')' indent ::= /* ... */ -expr ::= accessor | literal | prefix | infix | postfix | call | def | lambda +expr ::= accessor | literal + | prefix | infix | postfix + | array | record | set | dict | tuple + | call | def | lambda line ::= expr separator+ program ::= expr? | (line | comment)* diff --git a/doc/JA/syntax/quick_tour.md b/doc/JA/syntax/quick_tour.md index bdffec57..66ddbdd3 100644 --- a/doc/JA/syntax/quick_tour.md +++ b/doc/JA/syntax/quick_tour.md @@ -215,8 +215,8 @@ age = match person: ```erg Point = Inherit {x = Int; y = Int} -p = Point.{x = 1; y = 2} -Point.{x; y} = p +p = Point::{x = 1; y = 2} +Point::{x; y} = p ``` ## 内包表記 diff --git a/doc/JA/syntax/type/03_trait.md b/doc/JA/syntax/type/03_trait.md index 1f401698..5b234c07 100644 --- a/doc/JA/syntax/type/03_trait.md +++ b/doc/JA/syntax/type/03_trait.md @@ -85,7 +85,7 @@ assert add(C.new(1), C.new(2)) == C.new(3) ``` 記名的トレイトは単に要求メソッドを実装しただけでは使えず、実装したことを明示的に宣言する必要があります。 -以下の例では明示的な実装の宣言がないため、`add`が`C`型の引数で使えません。`C = Class {i = Int}, Impl: Add`としなくてはならないのです。 +以下の例では明示的な実装の宣言がないため、`add`が`C`型の引数で使えません。`C = Class {i = Int}, Impl := Add`としなくてはならないのです。 ```erg Add = Trait { diff --git a/doc/JA/syntax/type/05_inheritance.md b/doc/JA/syntax/type/05_inheritance.md index 09f433aa..82c2c6b8 100644 --- a/doc/JA/syntax/type/05_inheritance.md +++ b/doc/JA/syntax/type/05_inheritance.md @@ -125,7 +125,7 @@ Inherited!. 例えば`Real`(`Add()`を実装する)のサブタイプである`Int`では`Add()`を再実装しているようにみえます。 ```erg -Int = Class ..., Impl: Add() and ... +Int = Class ..., Impl := Add() and ... ``` しかし実際は`Real`の`Add()`は`Add(Real, Real)`の略で、`Int`では`Add(Int, Int)`で上書きしているだけです。 diff --git a/doc/JA/syntax/type/07_patch.md b/doc/JA/syntax/type/07_patch.md index 327fbb9b..f79c7f53 100644 --- a/doc/JA/syntax/type/07_patch.md +++ b/doc/JA/syntax/type/07_patch.md @@ -110,7 +110,7 @@ Reverse = Trait { .reverse = Self.() -> Self } -StrReverse = Patch Str, Impl: Reverse +StrReverse = Patch Str, Impl := Reverse StrReverse. reverse self = self.iter().rev().collect(Str) @@ -125,7 +125,7 @@ NumericStr = Inherit Str NumericStr. ... -NumStrRev = Patch NumericStr, Impl: Reverse +NumStrRev = Patch NumericStr, Impl := Reverse NumStrRev. ... # DuplicatePatchError: NumericStr is already associated with `Reverse` @@ -157,7 +157,7 @@ Reverse = Trait { .reverse = Self.() -> Self } -StrReverse = Patch(Str, Impl: Reverse) +StrReverse = Patch(Str, Impl := Reverse) StrReverse. reverse self = self.iter().rev().collect(Str) diff --git a/doc/JA/syntax/type/09_attributive.md b/doc/JA/syntax/type/09_attributive.md index 8bcfc101..96dce87e 100644 --- a/doc/JA/syntax/type/09_attributive.md +++ b/doc/JA/syntax/type/09_attributive.md @@ -5,4 +5,4 @@ ## レコード型の合成 -合成されたレコード型は平坦化できます。例えば、`{...{.name = Str; .age = Nat}; ...{.name = Str; .id = Nat}}`は`{.name = Str; .age = Nat; .id = Nat}`となります。 +合成されたレコード型は平坦化できます。例えば、`{..::{.name = Str; .age = Nat}; ..::{.name = Str; .id = Nat}}`は`{.name = Str; .age = Nat; .id = Nat}`となります。 diff --git a/doc/JA/syntax/type/14_dependent.md b/doc/JA/syntax/type/14_dependent.md index f844cad8..754ae782 100644 --- a/doc/JA/syntax/type/14_dependent.md +++ b/doc/JA/syntax/type/14_dependent.md @@ -32,7 +32,7 @@ f x = print! f::x, module::x # Phantom型は型引数と同じ値になるPhantomという属性を持っている -T X: Int = Class Impl: Phantom X +T X: Int = Class Impl := Phantom X T(X). x self = self::Phantom @@ -44,7 +44,7 @@ T(1).x() # 1 ```erg # `Id`は不変型なので遷移させることはできないことに注意 -VM!(State: {"stopped", "running"}! |= _, Id: Nat |= _) = Class(..., Impl: Phantom! State) +VM!(State: {"stopped", "running"}! := _, Id: Nat := _) = Class(..., Impl := Phantom! State) VM!(). # 変わらない変数は`_`を渡せば省略可能, デフォルト引数にしておけば書く必要すらない start! ref! self("stopped" ~> "running") = diff --git a/doc/JA/syntax/type/advanced/GADTs.md b/doc/JA/syntax/type/advanced/GADTs.md index 40789d4b..2b8a8a7d 100644 --- a/doc/JA/syntax/type/advanced/GADTs.md +++ b/doc/JA/syntax/type/advanced/GADTs.md @@ -3,8 +3,8 @@ ErgはOr型をクラス化することで一般化代数的データ型(GADTs)を作成出来ます。 ```erg -Nil T = Class(Impl: Phantom T) -Cons T = Class {head = T; rest = List T}, Impl: Unpack +Nil T = Class(Impl := Phantom T) +Cons T = Class {head = T; rest = List T}, Impl := Unpack List T: Type = Class(Nil T or Cons T) List. nil|T|() = Self(T).new Nil(T).new() @@ -30,8 +30,8 @@ _: List Int = cons 1, i ```erg List: (Type, {"Empty", "Nonempty"}) -> Type -List T, "Empty" = Class(Impl: Phantom T) -List T, "Nonempty" = Class {head = T; rest = List(T, _)}, Impl: Unpack +List T, "Empty" = Class(Impl := Phantom T) +List T, "Nonempty" = Class {head = T; rest = List(T, _)}, Impl := Unpack List. nil|T|() = Self(T, "Empty").new Nil(T).new() cons head, rest | T = Self(T, "Nonempty").new {head; rest} @@ -48,8 +48,8 @@ Ergではさらに精密化して、長さを持つリストを定義できま ```erg List: (Type, Nat) -> Type -List T, 0 = Class(Impl: Phantom T) -List T, N = Class {head = T; rest = List(T, N-1)}, Impl: Unpack +List T, 0 = Class(Impl := Phantom T) +List T, N = Class {head = T; rest = List(T, N-1)}, Impl := Unpack List. nil|T|() = Self(T, 0).new Nil(T).new() cons head, rest | T, N = Self(T, N).new {head; rest} diff --git a/doc/JA/syntax/type/advanced/default_param.md b/doc/JA/syntax/type/advanced/default_param.md index 895cd62a..e183cd97 100644 --- a/doc/JA/syntax/type/advanced/default_param.md +++ b/doc/JA/syntax/type/advanced/default_param.md @@ -3,25 +3,25 @@ まず、デフォルト引数の使用例を見る。 ```erg -f: (Int, Int, z |= Int) -> Int -f(x, y, z |= 0) = x + y + z +f: (Int, Int, z := Int) -> Int +f(x, y, z := 0) = x + y + z -g: (Int, Int, z |= Int, w |= Int) -> Int -g(x, y, z |= 0, w |= 1) = x + y + z + w +g: (Int, Int, z := Int, w := Int) -> Int +g(x, y, z := 0, w := 1) = x + y + z + w -fold: ((Int, Int) -> Int, [Int], acc |= Int) -> Int +fold: ((Int, Int) -> Int, [Int], acc := Int) -> Int fold(f, [], acc) = acc -fold(f, arr, acc |= 0) = fold(f, arr[1..], f(acc, arr[0])) +fold(f, arr, acc := 0) = fold(f, arr[1..], f(acc, arr[0])) assert fold(f, [1, 2, 3]) == 6 assert fold(g, [1, 2, 3]) == 8 ``` -`|=`以降の引数はデフォルト引数である。 +`:=`以降の引数はデフォルト引数である。 部分型付け規則は以下の通り。 ```erg -((X, y |= Y) -> Z) <: (X -> Z) -((X, y |= Y, ...) -> Z) <: ((X, ...) -> Z) +((X, y := Y) -> Z) <: (X -> Z) +((X, y := Y, ...) -> Z) <: ((X, ...) -> Z) ``` 1番目は、デフォルト引数のある関数は、ない関数と同一視できる、という意味である。 diff --git a/doc/JA/syntax/type/advanced/marker_trait.md b/doc/JA/syntax/type/advanced/marker_trait.md index 6d51ce46..be11716b 100644 --- a/doc/JA/syntax/type/advanced/marker_trait.md +++ b/doc/JA/syntax/type/advanced/marker_trait.md @@ -17,7 +17,7 @@ Person = Class {.name = Str; .age = Nat} and Light ```erg M = Subsume Marker -MarkedInt = Inherit Int, Impl: M +MarkedInt = Inherit Int, Impl := M i = MarkedInt.new(2) assert i + 1 == 2 @@ -27,5 +27,5 @@ assert i in M マーカークラスは`Excluding`引数で外すことも可能である。 ```erg -NInt = Inherit MarkedInt, Impl: N, Excluding: M +NInt = Inherit MarkedInt, Impl := N, Excluding: M ``` diff --git a/doc/JA/syntax/type/advanced/mut_struct.md b/doc/JA/syntax/type/advanced/mut_struct.md index f5eb6f02..d1efe69b 100644 --- a/doc/JA/syntax/type/advanced/mut_struct.md +++ b/doc/JA/syntax/type/advanced/mut_struct.md @@ -3,7 +3,7 @@ `T!`型は任意の`T`型オブジェクトを入れられて差し替え可能なボックス型であると説明した。 ```erg -Particle! State: {"base", "excited"}! = Class(..., Impl: Phantom State) +Particle! State: {"base", "excited"}! = Class(..., Impl := Phantom State) Particle!. # このメソッドはStateを"base"から"excited"に遷移させる apply_electric_field!(ref! self("base" ~> "excited"), field: Vector) = ... @@ -31,7 +31,7 @@ v: [Str; !1] 可変構造型はもちろんユーザー定義も可能である。ただし、不変構造型とは構成法に関していくつか違いがあるので注意が必要である。 ```erg -Nil T = Class(Impl: Phantom T) +Nil T = Class(Impl := Phantom T) List T, !0 = Inherit Nil T List T, N: Nat! = Class {head = T; rest = List(T, !N-1)} List(T, !N). diff --git a/doc/JA/syntax/type/advanced/overloading.md b/doc/JA/syntax/type/advanced/overloading.md index e7686f5a..af0633ac 100644 --- a/doc/JA/syntax/type/advanced/overloading.md +++ b/doc/JA/syntax/type/advanced/overloading.md @@ -7,10 +7,10 @@ Ergでは __アドホック多相__ をサポートしない。すなわち、 Add1 = Trait { .add1: Self.() -> Self } -IntAdd1 = Patch Int, Impl: Add1 +IntAdd1 = Patch Int, Impl := Add1 IntAdd1. add1 self = self + 1 -RatioAdd1 = Patch Ratio, Impl: Add1 +RatioAdd1 = Patch Ratio, Impl := Add1 RatioAdd1. add1 self = self + 1.0 @@ -35,7 +35,7 @@ assert add1(1.0) == 2.0 ```erg C = Class {.x = Int; .y = Int} C. - new(x, y |= 0) = Self::__new__ {.x; .y} + new(x, y := 0) = Self::__new__ {.x; .y} assert C.new(0, 0) == C.new(0) ``` @@ -62,7 +62,7 @@ id "str" # TypeError: id is not implemented for Str ```erg f x: Int = ... -f(x: Int, y |= 0) = ... +f(x: Int, y := 0) = ... f(1) # which is chosen? ``` diff --git a/doc/JA/syntax/type/advanced/phantom.md b/doc/JA/syntax/type/advanced/phantom.md index 35f57e48..6933bdf9 100644 --- a/doc/JA/syntax/type/advanced/phantom.md +++ b/doc/JA/syntax/type/advanced/phantom.md @@ -32,7 +32,7 @@ List T, N: Nat = Class {head = T; rest = List(T, N-1)} このようなときにちょうどよいのが幽霊型である。幽霊型はサイズ0の型を一般化した型である。 ```erg -Nil T = Class(Impl: Phantom T) +Nil T = Class(Impl := Phantom T) List T, 0 = Inherit Nil T List T, N: Nat = Class {head = T; rest = List(T, N-1)} @@ -46,7 +46,7 @@ assert nil.__size__ == 0 この場合も、`state`はオブジェクトの実体に現れないハリボテの型変数である。 ```erg -VM! State: {"stopped", "running"}! = Class(..., Impl: Phantom! State) +VM! State: {"stopped", "running"}! = Class(..., Impl := Phantom! State) VM!("stopped"). start ref! self("stopped" ~> "running") = self.do_something!() diff --git a/doc/JA/syntax/type/advanced/projection.md b/doc/JA/syntax/type/advanced/projection.md index 88eb0c4e..404edff6 100644 --- a/doc/JA/syntax/type/advanced/projection.md +++ b/doc/JA/syntax/type/advanced/projection.md @@ -8,7 +8,7 @@ Add R = Trait { .AddO = Type } -AddForInt = Patch(Int, Impl: Add Int) +AddForInt = Patch(Int, Impl := Add Int) AddForInt. AddO = Int ``` diff --git a/doc/JA/syntax/type/advanced/special.md b/doc/JA/syntax/type/advanced/special.md index a98cb258..482ad174 100644 --- a/doc/JA/syntax/type/advanced/special.md +++ b/doc/JA/syntax/type/advanced/special.md @@ -40,7 +40,7 @@ Add R = Trait { } ClosedAdd = Subsume Add(Self) -ClosedAddForInt = Patch(Int, Impl: ClosedAdd) +ClosedAddForInt = Patch(Int, Impl := ClosedAdd) ClosedAddForInt. AddO = Int diff --git a/doc/JA/syntax/type/advanced/variance.md b/doc/JA/syntax/type/advanced/variance.md index 5ec7174e..7ff493af 100644 --- a/doc/JA/syntax/type/advanced/variance.md +++ b/doc/JA/syntax/type/advanced/variance.md @@ -133,11 +133,11 @@ K T = Class(...) assert not K(Str) <= K(Object) assert not K(Str) >= K(Object) -InputStream T = Class ..., Impl: Inputs(T) +InputStream T = Class ..., Impl := Inputs(T) # Objectを受け入れるストリームは、Strを受け入れるともみなせる assert InputStream(Str) > InputStream(Object) -OutputStream T = Class ..., Impl: Outputs(T) +OutputStream T = Class ..., Impl := Outputs(T) # Strを出力するストリームは、Objectを出力するともみなせる assert OutputStream(Str) < OutputStream(Object) ``` diff --git a/doc/JA/tips.md b/doc/JA/tips.md index 012f79d7..85c326fd 100644 --- a/doc/JA/tips.md +++ b/doc/JA/tips.md @@ -44,15 +44,15 @@ FinalWrapper. また、`Enum`を使うと、その選択肢となる型がリダイレクト属性として自動的に定義されます。 ```erg -Ok = Class Impl: Singleton -Err = Class Impl: Singleton +Ok = Class Impl := Singleton +Err = Class Impl := Singleton ErrWithInfo = Inherit {info = Str} Status = Enum Ok, Err, ErrWithInfo stat: Status = Status.cons(ErrWithInfo) {info = "error caused by ..."} match! stat: Status.Ok -> ... Status.Err -> ... - Status.ErrWithInfo.{info;} -> ... + Status.ErrWithInfo::{info;} -> ... ``` ```erg diff --git a/examples/add.er b/examples/add.er index 8a1e002c..3ccac7bf 100644 --- a/examples/add.er +++ b/examples/add.er @@ -1,4 +1,4 @@ -Point = Class {x = Int; y = Int}, Impl: Add() and Eq() +Point = Class {x = Int; y = Int}, Impl := Add() and Eq() Point. new x, y = Self::__new__ {x; y} norm &self = self::x**2 + self::y**2 diff --git a/examples/class.er b/examples/class.er index 4a708b49..709c5886 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; y} + new x, y = Self::__new__ {x = x; y = y} norm ref self = self::x**2 + self::y**2 -Point3D = Inherit Point2D, Additional: {z = Int} +Point3D = Inherit Point2D, Additional := {z = Int} Point3D. @Override - new x, y, z = Self::__new__ {x; y; z} + new x, y, z = Self::__new__ {x = x; y = y; z = z} @Override norm ref self = self::x**2 + self::y**2 + self::z**2 -UnpackPoint2D = Class {x = Int; y = Int}, Impl: Unpack +UnpackPoint2D = Class {x = Int; y = Int}, Impl := Unpack -p = UnpackPoint2D.{x = 1; y = 2} -UnpackPoint2D.{x; y} = p +p = UnpackPoint2D::{x = 1; y = 2} +UnpackPoint2D::{x = x; y = x} = p diff --git a/examples/enum.er b/examples/enum.er index 7d59878c..84036916 100644 --- a/examples/enum.er +++ b/examples/enum.er @@ -1,20 +1,20 @@ -LitExpr = Class {.i = Int}, Impl: Show +LitExpr = Class {.i = Int}, Impl := Show LitExpr. new i = Self::__new__ {.i;} show &self = "{self.i}" -AddExpr = Class {.lhs = Expr, .rhs = Expr}, Impl: Show +AddExpr = Class {.lhs = Expr, .rhs = Expr}, Impl := Show AddExpr. new lhs, rhs = Self::__new__ {.lhs; .rhs} show &self = "{self.lhs} + {self.rhs}" -SubExpr = Class {.lhs = Expr, .rhs = Expr}, Impl: Show +SubExpr = Class {.lhs = Expr, .rhs = Expr}, Impl := Show SubExpr. new lhs, rhs = Self::__new__ {.lhs; .rhs} show &self = "{self.lhs} - {self.rhs}" -PosExpr = Class {.expr = Expr}, Impl: Show +PosExpr = Class {.expr = Expr}, Impl := Show PosExpr. new expr = Self::__new__ {.expr;} show &self = "+{self.expr}" -NegExpr = Class {.expr = Expr}, Impl: Show +NegExpr = Class {.expr = Expr}, Impl := Show NegExpr. new expr = Self::__new__ {.expr;} show &self = "-{self.expr}" diff --git a/examples/list.er b/examples/list.er index 083ca0cc..0faafe36 100644 --- a/examples/list.er +++ b/examples/list.er @@ -1,18 +1,18 @@ -Nil T = Class Impl: Phantom(T) and Eq +Nil T = Class Impl := Phantom(T) and Eq Cons T, N = Inherit {head = T; rest = List(T, N-1)} List: (Type, Nat) -> Type List T, 0 = Class Nil T -List T, N = Class Cons(T, N), Impl: Eq +List T, N = Class Cons(T, N), Impl := Eq List. nil T = List(T, 0).new Nil(T).new() - cons|T, N| rest: List(T, N-1), head: T = List(T, N).new Cons(T, N).{head; rest} + cons|T, N| rest: List(T, N-1), head: T = List(T, N).new Cons(T, N)::{head; rest} {nil, cons} = List a = cons(nil(Int), 1) |> cons 2 |> cons 3 match a: - Cons(_, _).{head=h1; rest=Cons(_, _).{head=h2; rest}} -> + Cons(_, _)::{head=h1; rest=Cons(_, _)::{head=h2; rest}} -> assert h1 == 3 assert h2 == 2 - assert rest == Cons.{head = 1; rest = nil(Int)} + assert rest == Cons::{head = 1; rest = nil(Int)} _ -> pass diff --git a/examples/tuple.er b/examples/tuple.er index 66bd3724..aa6e6af6 100644 --- a/examples/tuple.er +++ b/examples/tuple.er @@ -1,8 +1,8 @@ p = (1, 2) assert p.0 == 1 -q = (1, 1.0) -assert q.1 == 1.0 +q = (1, True) +assert q.1 i, j = 0, 1 assert i == 0