From 507526e9296f98ba73d91cb6743a5a98c01f35ab Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Wed, 6 Nov 2024 22:29:35 +0800 Subject: [PATCH 1/3] Add support for `this`, `undefined`, and `null` --- .../main/scala/hkmc2/codegen/Lowering.scala | 2 + .../scala/hkmc2/semantics/Elaborator.scala | 10 +++- .../src/main/scala/hkmc2/semantics/Term.scala | 3 ++ .../src/main/scala/hkmc2/syntax/Keyword.scala | 1 + .../main/scala/hkmc2/syntax/ParseRule.scala | 11 +++- .../src/test/mlscript/basics/Literals.mls | 35 ++++++++++++ .../src/test/mlscript/basics/OpBlocks.mls | 2 +- .../test/mlscript/basics/RefinedClasses.mls | 4 -- hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls | 40 ++++++++++---- .../shared/src/test/mlscript/codegen/This.mls | 54 +++++++++++++++++++ .../shared/src/test/mlscript/parser/Class.mls | 6 +-- hkmc2/shared/src/test/mlscript/parser/Of.mls | 2 +- .../src/test/mlscript/ucs/syntax/Is.mls | 2 +- .../test/mlscript/ucs/syntax/SimpleUCS.mls | 4 +- 14 files changed, 149 insertions(+), 27 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/codegen/This.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 4ce4ac069..cb546a2dd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -57,6 +57,8 @@ class Lowering(using TL, Raise, Elaborator.State): fs.foldRight[Ls[Path] => Block](args => k(Value.Arr(args.reverse)))((a, acc) => args => subTerm(a.value)(r => acc(r :: args)) )(Nil) + case st.This(sym) => + k(subst(Value.This(sym))) case st.Ref(sym) => sym match case sym: LocalSymbol => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index c13242910..44ea623db 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -21,6 +21,7 @@ object Elaborator: def nest(outer: Opt[MemberSymbol[?]]): Ctx = Ctx(outer, Some(this), Map.empty, Map.empty) def get(name: Str): Opt[Symbol] = locals.get(name).orElse(members.get(name)).orElse(parent.flatMap(_.get(name))) + def getOuter: Opt[Symbol] = outer.orElse(parent.flatMap(_.getOuter)) lazy val allMembers: Map[Str, Symbol] = parent.fold(Map.empty)(_.allMembers) ++ members object Ctx: val empty: Ctx = Ctx(N, N, Map.empty, Map.empty) @@ -87,8 +88,13 @@ extends Importer: term(bod), ), Term.Assgn(lt, sym.ref(id)))) case _ => ??? // TODO error - case Ident("true") => Term.Lit(Tree.BoolLit(true)) - case Ident("false") => Term.Lit(Tree.BoolLit(false)) + case id @ Ident("this") => + ctx.getOuter match + case S(sym) => + Term.This(sym.asInstanceOf) + case N => + raise(ErrorReport(msg"Cannot use 'this' outside of an object scope." -> tree.toLoc :: Nil)) + Term.Error case id @ Ident("Alloc") => Term.Ref(allocSkolemSym)(id, 1) case id @ Ident(name) => ctx.get(name) match diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index bd5ed716f..cb376dbc1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -11,6 +11,7 @@ enum Term extends Statement: case Error case Lit(lit: Literal) case Ref(sym: Symbol)(val tree: Tree.Ident, val refNum: Int) + case This(sym: MemberSymbol[?]) case App(lhs: Term, rhs: Term)(val tree: Tree.App, val resSym: FlowSymbol) case TyApp(lhs: Term, targs: Ls[Term]) case Sel(prefix: Term, nme: Tree.Ident) @@ -120,6 +121,8 @@ sealed trait Statement extends AutoLocated: td.rhs.toList case Import(sym, pth) => Nil case Try(body, finallyDo) => body :: finallyDo :: Nil + case This(_) => Nil + case Neg(e) => e :: Nil protected def children: Ls[Located] = this match case t: Lit => t.lit.asTree :: Nil diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala index ad0e419bd..40ddea9ca 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala @@ -106,6 +106,7 @@ object Keyword: val `private` = Keyword("private", N, N) val `return` = Keyword("return", N, curPrec) val `import` = Keyword("import", N, curPrec) + val `this` = Keyword("this", N, N) // * The lambda operator is special: // * it should associate very strongly on the left and very loosely on the right diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala index a4e1fc748..61f7e46c7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala @@ -258,11 +258,18 @@ object ParseRule: modified(`return`), modified(`import`), // TODO improve – only allow strings // modified(`type`), + singleKw(`true`)(BoolLit(true)), + singleKw(`false`)(BoolLit(false)), + singleKw(`undefined`)(UnitLit(true)), + singleKw(`null`)(UnitLit(false)), + singleKw(`this`)(Ident("this")), standaloneExpr, - Kw(`true`)(ParseRule("'true' keyword")(End(BoolLit(true)))), - Kw(`false`)(ParseRule("'false' keyword")(End(BoolLit(false)))), ) + def singleKw[T](kw: Keyword)(v: T): Alt[T] = + Kw(kw)(ParseRule(s"'${kw.name}' keyword")(End(v))) + + val prefixRulesAllowIndentedBlock: ParseRule[Tree] = ParseRule(prefixRules.name)(prefixRules.alts :+ (Blk( diff --git a/hkmc2/shared/src/test/mlscript/basics/Literals.mls b/hkmc2/shared/src/test/mlscript/basics/Literals.mls index 5ff77d12b..6dd52aee3 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Literals.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Literals.mls @@ -1,25 +1,60 @@ +:js 1 +//│ = 1 () (1) +//│ = 1 (1,) +//│ = 1 +:w (1, 2) +//│ ╔══[WARNING] Pure expression in statement position +//│ ║ l.16: (1, 2) +//│ ╙── ^ +//│ = 2 +:w (1, 2,) +//│ ╔══[WARNING] Pure expression in statement position +//│ ║ l.23: (1, 2,) +//│ ╙── ^ +//│ = 2 [] +//│ = [] [1] +//│ = [ 1 ] [1,] +//│ = [ 1 ] [1, 2] +//│ = [ 1, 2 ] [1, 2,] +//│ = [ 1, 2 ] + + +null +//│ = null + +undefined + + +0.5 +//│ = 0.5 + +:todo +NaN +//│ ╔══[ERROR] Name not found: NaN +//│ ║ l.55: NaN +//│ ╙── ^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 698322b37..8c7a1a453 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -188,7 +188,7 @@ fun f(x) = if x fun f(x) = if x is 0 then "a" is 1 then "b" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead //│ ║ l.190: is 1 then "b" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead diff --git a/hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls b/hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls index f2a210f14..e3bdf60cc 100644 --- a/hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls @@ -30,11 +30,7 @@ class Bar { val x: Int } //│ ║ l.28: class Bar { val x: Int } //│ ╙── ^^^^^^^^^^^^^^ -:todo class Foo() extends Bar { x = 1 } with val y = this.x -//│ ╔══[ERROR] Name not found: this -//│ ║ l.35: val y = this.x -//│ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls b/hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls index ef3e98a6b..f8803f418 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls @@ -4,8 +4,17 @@ `42 //│ Type: CodeBase[out Int, ⊥, ?] +:p +:fixme // this should created a quoted keyword `false -//│ Type: CodeBase[out Bool, ⊥, ?] +//│ |`|false| +//│ Parsed: +//│ Quoted(Ident(false)) +//│ ╔══[ERROR] Name not found: false +//│ ║ l.9: `false +//│ ╙── ^^^^^ +//│ ═══[ERROR] Cannot quote Error +//│ Type: CodeBase[⊥, ⊥, ?] `"rua" //│ Type: CodeBase[out Str, ⊥, ?] @@ -23,7 +32,7 @@ x `=> `42 :e x `=> 42 //│ ╔══[ERROR] Type error in unquoted term -//│ ║ l.24: x `=> 42 +//│ ║ l.33: x `=> 42 //│ ║ ^^ //│ ╙── because: cannot constrain Int <: CodeBase[out α12_3, out α13_3, ?] //│ Type: CodeBase[out ⊤ -> ⊥, ⊥, ?] @@ -52,7 +61,7 @@ f `=> x `=> y `=> f`(x, y) :e `let x = 42 `in x //│ ╔══[ERROR] Type error in unquoted term -//│ ║ l.53: `let x = 42 `in x +//│ ║ l.62: `let x = 42 `in x //│ ║ ^^ //│ ╙── because: cannot constrain Int <: CodeBase[out α122_2, out α123_2, ?] //│ Type: CodeBase[⊥, ⊥, ?] @@ -60,7 +69,7 @@ f `=> x `=> y `=> f`(x, y) :e `let x = `0 `in 1 //│ ╔══[ERROR] Type error in unquoted term -//│ ║ l.61: `let x = `0 `in 1 +//│ ║ l.70: `let x = `0 `in 1 //│ ║ ^ //│ ╙── because: cannot constrain Int <: CodeBase[out α131_3, out α132_3, ?] //│ Type: CodeBase[⊥, ⊥, ?] @@ -68,7 +77,16 @@ f `=> x `=> y `=> f`(x, y) :fixme `if `true then `true else `false -//│ ═══[ERROR] Cannot quote If(Let($scrut@80,Unquoted(Quoted(Lit(BoolLit(true)))),Cons(Branch(Ref($scrut@80),LitPat(BoolLit(true)),Else(Unquoted(Quoted(Lit(BoolLit(true)))))),Else(Unquoted(Quoted(Lit(BoolLit(false)))))))) +//│ ╔══[ERROR] Name not found: true +//│ ║ l.79: `if `true then `true else `false +//│ ╙── ^^^^ +//│ ╔══[ERROR] Name not found: true +//│ ║ l.79: `if `true then `true else `false +//│ ╙── ^^^^ +//│ ╔══[ERROR] Name not found: false +//│ ║ l.79: `if `true then `true else `false +//│ ╙── ^^^^^ +//│ ═══[ERROR] Cannot quote If(Let($scrut@80,Unquoted(Quoted(Error)),Cons(Branch(Ref($scrut@80),LitPat(BoolLit(true)),Else(Unquoted(Quoted(Error)))),Else(Unquoted(Quoted(Error)))))) //│ Type: CodeBase[⊥, ⊥, ?] @@ -83,16 +101,16 @@ run(`1) :e run(1) //│ ╔══[ERROR] Type error in integer literal with expected type CodeBase[out α140_1, ⊥, ?] -//│ ║ l.84: run(1) -//│ ║ ^ +//│ ║ l.102: run(1) +//│ ║ ^ //│ ╙── because: cannot constrain Int <: CodeBase[out α140_1, ⊥, ?] //│ Type: ⊥ :e x `=> run(x) //│ ╔══[ERROR] Type error in reference with expected type CodeBase[out α143_3, ⊥, ?] -//│ ║ l.92: x `=> run(x) -//│ ║ ^ +//│ ║ l.110: x `=> run(x) +//│ ║ ^ //│ ╟── because: cannot constrain CodeBase[α141_3, <α>142_3, ⊥] <: CodeBase[out α143_3, ⊥, ?] //│ ╙── because: cannot constrain D( <α>142_3 ) <: ⊥ //│ Type: CodeBase[out CodeBase[out α144_3, ?, ?] -> α144_3, out α146_2, ?] @@ -100,12 +118,12 @@ x `=> run(x) :e `let x = `42 `in run(x) //│ ╔══[ERROR] Type error in reference with expected type CodeBase[out α150_3, ⊥, ?] -//│ ║ l.101: `let x = `42 `in run(x) +//│ ║ l.119: `let x = `42 `in run(x) //│ ║ ^ //│ ╟── because: cannot constrain CodeBase[α147_2, <α>149_3, ⊥] <: CodeBase[out α150_3, ⊥, ?] //│ ╙── because: cannot constrain D( <α>149_3 ) <: ⊥ //│ ╔══[ERROR] Type error in unquoted term -//│ ║ l.101: `let x = `42 `in run(x) +//│ ║ l.119: `let x = `42 `in run(x) //│ ║ ^^^^^^ //│ ╟── because: cannot constrain α150_3 <: CodeBase[out α151_3, out α152_3, ?] //│ ╟── because: cannot constrain α150_3 <: ¬(~{CodeBase[out α151_3, out α152_3, ?]}) diff --git a/hkmc2/shared/src/test/mlscript/codegen/This.mls b/hkmc2/shared/src/test/mlscript/codegen/This.mls new file mode 100644 index 000000000..b173550df --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/This.mls @@ -0,0 +1,54 @@ +:js + + +:sjs +fun test(x) = + [this.a, x] +//│ JS: +//│ function test(x) { return [ globalThis.a, x ] }; undefined + +test(123) +//│ = [ undefined, 123 ] + +val a = 1 +//│ a = 1 + +test(123) +//│ = [ 1, 123 ] + +:sjs +module Test with + val a = 2 + fun test1(x) = + test(x) + fun test2(x) = + [this.a, x] +//│ JS: +//│ this.Test = new class Test { +//│ constructor() { +//│ this.a = 2; +//│ } +//│ test1(x) { +//│ return globalThis.test(x) +//│ } +//│ test2(x1) { +//│ return [ +//│ this.a, +//│ x1 +//│ ] +//│ } +//│ toString() { return "Test"; } +//│ }; +//│ undefined + + +Test.test1(123) +//│ = [ 1, 123 ] + +Test.test2(123) +//│ = [ 2, 123 ] + + + + + diff --git a/hkmc2/shared/src/test/mlscript/parser/Class.mls b/hkmc2/shared/src/test/mlscript/parser/Class.mls index 77dbe1d38..f2c922d97 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Class.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Class.mls @@ -15,7 +15,7 @@ class Foo :pe class Foo extends -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, 'false' keyword, or block after start of statement; found end of input instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, expression, or block after start of statement; found end of input instead //│ ║ l.17: class Foo extends //│ ╙── ^ //│ Parsed: @@ -54,7 +54,7 @@ class Foo with :pe with -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'with' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'with' keyword instead //│ ║ l.56: with //│ ╙── ^^^^ //│ Parsed: @@ -98,7 +98,7 @@ Foo class Foo extends Bar -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'extends' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'extends' keyword instead //│ ║ l.100: extends Bar //│ ╙── ^^^^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead diff --git a/hkmc2/shared/src/test/mlscript/parser/Of.mls b/hkmc2/shared/src/test/mlscript/parser/Of.mls index cedc94bd6..b5c80e5a1 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Of.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Of.mls @@ -64,7 +64,7 @@ f of :pe log of 1 -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'of' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'of' keyword instead //│ ║ l.66: of 1 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls index 0e081c155..ed554a124 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls @@ -11,7 +11,7 @@ x x is A is B -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead //│ ║ l.13: is B //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls index 7c158ae80..c65c3d8ea 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls @@ -124,7 +124,7 @@ fun f(x, y, b) = is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead //│ ║ l.124: is None then "bruh" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected identifier here @@ -142,7 +142,7 @@ fun g(x, y, b) = is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, expression, 'true' keyword, or 'false' keyword after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead //│ ║ l.142: is None then "bruh" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected identifier here From fa8c05dadb7c1e480c2c3c44b82244282d7dbcf2 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Wed, 6 Nov 2024 22:44:06 +0800 Subject: [PATCH 2/3] Slightly improve parse error message --- .../main/scala/hkmc2/syntax/ParseRule.scala | 10 +++++---- .../src/main/scala/hkmc2/syntax/Parser.scala | 22 +++++++++---------- .../src/test/mlscript/basics/OpBlocks.mls | 2 +- .../shared/src/test/mlscript/parser/Class.mls | 6 ++--- hkmc2/shared/src/test/mlscript/parser/Of.mls | 2 +- .../src/test/mlscript/ucs/syntax/Is.mls | 2 +- .../test/mlscript/ucs/syntax/SimpleUCS.mls | 4 ++-- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala index 61f7e46c7..642ab3b7d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala @@ -23,7 +23,7 @@ enum Alt[+A]: case End(a) => End(f(a)) case b: Blk[rest, A] => Blk(b.rest)((tree, rest) => f(b.k(tree, rest))) -class ParseRule[+A](val name: Str)(val alts: Alt[A]*): +class ParseRule[+A](val name: Str, val omitAltsStr: Bool = false)(val alts: Alt[A]*): def map[B](f: A => B): ParseRule[B] = ParseRule(name)(alts.map(_.map(f))*) @@ -34,7 +34,9 @@ class ParseRule[+A](val name: Str)(val alts: Alt[A]*): lazy val exprAlt = alts.collectFirst { case alt: Alt.Expr[rst, A] => alt } lazy val blkAlt = alts.collectFirst { case alt: Alt.Blk[rst, A] => alt } - def whatComesAfter: Str = + def mkAfterStr: Str = if omitAltsStr then "in this position" else s"after $name" + + def whatComesAfter: Str = if omitAltsStr then name else alts.map: case Alt.Kw(kw) => s"'${kw.name}' keyword" case Alt.Expr(rest) => "expression" @@ -157,7 +159,7 @@ object ParseRule: // ) { case (lhs, body) => Let(lhs, lhs, body) } ) - val prefixRules: ParseRule[Tree] = ParseRule("start of statement")( + val prefixRules: ParseRule[Tree] = ParseRule("start of statement", omitAltsStr = true)( letLike(`let`), letLike(`set`), Kw(`new`): @@ -271,7 +273,7 @@ object ParseRule: val prefixRulesAllowIndentedBlock: ParseRule[Tree] = - ParseRule(prefixRules.name)(prefixRules.alts :+ + ParseRule(prefixRules.name, prefixRules.omitAltsStr)(prefixRules.alts :+ (Blk( ParseRule("block"): End(()) diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala index f92ef0c2d..bd557bf39 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala @@ -253,7 +253,7 @@ abstract class Parser( consume val blk = rec(toks, S(tok.innerLoc), tok.describe).concludeWith(_.blockOf(subRule, allowNewlines)) // FIXME allowNewlines? if blk.isEmpty then - err((msg"Expected ${subRule.whatComesAfter} after ${subRule.name}; found end of block instead" -> S(loc) :: Nil)) + err((msg"Expected ${subRule.whatComesAfter} ${subRule.mkAfterStr}; found end of block instead" -> S(loc) :: Nil)) errExpr blk ::: blockContOf(rule) case _ => @@ -282,10 +282,10 @@ abstract class Parser( parseRule(CommaPrecNext, exprAlt.rest).map(res => exprAlt.k(e, res)).getOrElse(errExpr) :: blockContOf(rule) case N => // TODO dedup? - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> S(loc) :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil)) errExpr :: blockContOf(rule) case N => - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> S(loc) :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil)) errExpr :: blockContOf(rule) case N => @@ -312,7 +312,7 @@ abstract class Parser( case S(res) => S(res) case N => - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> S(loc) :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil)) N @@ -324,7 +324,7 @@ abstract class Parser( case S(res) => S(res) case N => consume - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> S(loc) :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil)) N yeetSpaces match // case (tok @ (id: IDENT), loc) :: _ if Keyword.all.get(id.name).exists(_.leftPrecOrMin < prec) => @@ -334,7 +334,7 @@ abstract class Parser( // case S(res) => // S(res) // case N => - // err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found end of phrase instead" -> S(loc.left) :: Nil)) + // err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found end of phrase instead" -> S(loc.left) :: Nil)) // N case (tok @ (id: IDENT), loc) :: _ => Keyword.all.get(id.name) match @@ -373,14 +373,14 @@ abstract class Parser( rule.emptyAlt match case S(res) => S(res) case N => - // err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> lastLoc :: Nil)) - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> S(l0) :: Nil)) + // err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> lastLoc :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(l0) :: Nil)) N case (br @ BRACKETS(Indent | Curly, toks), loc) :: _ => // rule.blkAlt match // case S(res) => S(res) // case N => - // err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${tok.describe} instead" -> lastLoc :: Nil)) + // err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> lastLoc :: Nil)) // N if verbose then printDbg("$ found an indented" + (toks match @@ -421,7 +421,7 @@ abstract class Parser( case S(res) => S(res) case N => - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found end of input instead" -> lastLoc :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found end of input instead" -> lastLoc :: Nil)) N @@ -924,7 +924,7 @@ abstract class Parser( .getOrElse(errExpr) case N => // TODO other alts...? - err((msg"Expected ${rule.whatComesAfter} after ${rule.name}; found ${kw.name} instead" -> S(l0) :: Nil)) + err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${kw.name} instead" -> S(l0) :: Nil)) acc case _ => acc case _ => diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 8c7a1a453..5741266b9 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -188,7 +188,7 @@ fun f(x) = if x fun f(x) = if x is 0 then "a" is 1 then "b" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'is' keyword instead //│ ║ l.190: is 1 then "b" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead diff --git a/hkmc2/shared/src/test/mlscript/parser/Class.mls b/hkmc2/shared/src/test/mlscript/parser/Class.mls index f2c922d97..6644f1846 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Class.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Class.mls @@ -15,7 +15,7 @@ class Foo :pe class Foo extends -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, expression, or block after start of statement; found end of input instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found end of input instead //│ ║ l.17: class Foo extends //│ ╙── ^ //│ Parsed: @@ -54,7 +54,7 @@ class Foo with :pe with -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'with' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'with' keyword instead //│ ║ l.56: with //│ ╙── ^^^^ //│ Parsed: @@ -98,7 +98,7 @@ Foo class Foo extends Bar -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'extends' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'extends' keyword instead //│ ║ l.100: extends Bar //│ ╙── ^^^^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead diff --git a/hkmc2/shared/src/test/mlscript/parser/Of.mls b/hkmc2/shared/src/test/mlscript/parser/Of.mls index b5c80e5a1..0263fe2e5 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Of.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Of.mls @@ -64,7 +64,7 @@ f of :pe log of 1 -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'of' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'of' keyword instead //│ ║ l.66: of 1 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls index ed554a124..7a5bb271b 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls @@ -11,7 +11,7 @@ x x is A is B -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'is' keyword instead //│ ║ l.13: is B //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead diff --git a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls index c65c3d8ea..9e69771ef 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls @@ -124,7 +124,7 @@ fun f(x, y, b) = is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'is' keyword instead //│ ║ l.124: is None then "bruh" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected identifier here @@ -142,7 +142,7 @@ fun g(x, y, b) = is None then "bruh" Some(xv) and b then xv + b _ then "roll" -//│ ╔══[PARSE ERROR] Expected 'let' keyword, 'set' keyword, 'new' keyword, 'in' keyword, 'if' keyword, 'else' keyword, 'case' keyword, 'region' keyword, 'fun' keyword, 'val' keyword, 'type' keyword, 'class' keyword, 'trait' keyword, 'module' keyword, 'open' keyword, 'abstract' keyword, 'mut' keyword, 'virtual' keyword, 'override' keyword, 'declare' keyword, 'public' keyword, 'private' keyword, 'out' keyword, 'return' keyword, 'import' keyword, 'true' keyword, 'false' keyword, 'undefined' keyword, 'null' keyword, 'this' keyword, or expression after start of statement; found 'is' keyword instead +//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'is' keyword instead //│ ║ l.142: is None then "bruh" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected identifier here From fe43485e6077f2e32a6f766b197287f5d6f7d061 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Wed, 6 Nov 2024 22:54:58 +0800 Subject: [PATCH 3/3] Add various tests --- .../src/test/mlscript/basics/OfLambdaArgs.mls | 18 +++++ .../src/test/mlscript/codegen/Arrays.mls | 2 +- .../test/mlscript/codegen/BadFunctions.mls | 11 +++ .../src/test/mlscript/codegen/CaseOfCase.mls | 2 +- .../test/mlscript/codegen/ClassInClass.mls | 2 +- .../src/test/mlscript/codegen/ClassInFun.mls | 2 +- .../test/mlscript/codegen/ClassMatching.mls | 2 +- .../test/mlscript/codegen/CodegenScratch.mls | 2 +- .../src/test/mlscript/codegen/EarlyReturn.mls | 2 +- .../src/test/mlscript/codegen/ElseLess.mls | 2 +- .../src/test/mlscript/codegen/FunInClass.mls | 2 +- .../src/test/mlscript/codegen/Functions.mls | 2 +- .../test/mlscript/codegen/FunctionsThis.mls | 2 +- .../src/test/mlscript/codegen/IfThenElse.mls | 6 +- .../test/mlscript/codegen/Juxtapositions.mls | 2 +- .../src/test/mlscript/codegen/Modules.mls | 2 +- .../test/mlscript/codegen/ParamClasses.mls | 2 +- .../test/mlscript/codegen/PlainClasses.mls | 2 +- .../src/test/mlscript/codegen/PredefJS.mls | 2 + .../src/test/mlscript/codegen/ReboundLet.mls | 2 +- .../shared/src/test/mlscript/codegen/Repl.mls | 2 +- .../src/test/mlscript/codegen/SetIn.mls | 2 +- .../src/test/mlscript/codegen/ThisCalls.mls | 79 +++++++++++++++++++ .../test/mlscript/syntax/KeywordStutters.mls | 57 +++++++++++++ .../ucs/normalization/SimplePairMatches.mls | 67 ++++++++++++++++ 25 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls create mode 100644 hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls create mode 100644 hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls create mode 100644 hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls create mode 100644 hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls diff --git a/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls b/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls new file mode 100644 index 000000000..f5a7f8bed --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls @@ -0,0 +1,18 @@ +:js + + +fun id(x) = x +fun foo(f, g) = f(g(1)) + + +foo(id(x => 1), id(y => 2)) +//│ = 1 + +foo of (id of x => 1), id of y => 2 +//│ = 1 + +:re +foo(id of x => 1, id of y => 2) +//│ ═══[RUNTIME ERROR] TypeError: g is not a function + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls index a414dc53f..3ebd62e35 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) let empty = [] diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls b/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls new file mode 100644 index 000000000..e22b74cee --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls @@ -0,0 +1,11 @@ +:js + + +:fixme +fun (let) oops = 1 +//│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead +//│ ║ l.5: fun (let) oops = 1 +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: getters (of class String) + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls index aa4ae8cd2..15601c685 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) class Some[out A](value: A) diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls index 4e56325ee..b310e0243 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls index 84980f689..a43330d5a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index 08422b987..1a220fd41 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) class Some[out A](value: A) diff --git a/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls b/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls index 76660e13b..3f5cc140f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :global :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls index 9fa6ebd19..5a3658d75 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) fun f() = diff --git a/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls b/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls index 80cf48745..8e329832d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) class Foo(a) diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls index a21257a1a..1216607f9 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls index d496ff9af..c4f8c26b6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) fun foo() = diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls index 228c6684e..225712127 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index b57102936..3fff33b60 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :elt @@ -10,11 +10,11 @@ if true then 1 else 0 //│ Blk: //│ stats = Nil //│ res = If of Let: -//│ sym = $scrut@29 +//│ sym = $scrut@31 //│ term = Lit of BoolLit of true //│ tail = Cons: \ //│ head = Branch: -//│ scrutinee = Ref of $scrut@29 +//│ scrutinee = Ref of $scrut@31 //│ pattern = LitPat of BoolLit of true //│ continuation = Else of Lit of IntLit of 1 //│ tail = Else of Lit of IntLit of 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls b/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls index 8d58319a7..0181395fe 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) 42 log() diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index f0c6971ec..2fb12229d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls index 65b4c1696..8ae410adc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :global :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls index 8f8439e90..6c7816655 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) :global :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/PredefJS.mls b/hkmc2/shared/src/test/mlscript/codegen/PredefJS.mls index 894761b85..bfe49d07c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PredefJS.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PredefJS.mls @@ -1,6 +1,8 @@ // JS implementations for decls/Predef.mls: +fun id(x) = x + fun log(msg) = globalThis.console.log(msg) diff --git a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls index b30b7d93a..0b27bb9a7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls index 5007f5c7a..aaf2fb704 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls @@ -48,7 +48,7 @@ let x = 1, log(x), x //│ x = 1 :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) //│ x = 1 :showRepl diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 917a0d42c..dfed62a74 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -1,7 +1,7 @@ :js :import PredefJS.mls -//│ Imported 1 member(s) +//│ Imported 2 member(s) let x = 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls b/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls new file mode 100644 index 000000000..9a483dde5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls @@ -0,0 +1,79 @@ +:js + +:import PredefJS.mls +//│ Imported 2 member(s) + + +module Example with + val a = 1 + fun f(x) = [x, a] + +Example.f(2) +//│ = [ 2, 1 ] + +let oops = Example.f +//│ oops = [Function: f] + + +// * JavaScript nonsense +oops(2) +//│ = [ 2, undefined ] + +oops.call(Example, 2) +//│ = [ 2, 1 ] + + +// * We could use that syntax as a shorthand: +// * TODO need to support varargs... +fun (.) call(receiver, f)(arg) = f.call(receiver, arg) + + +// * Notice how it parses with the correct precedence: +:pt +:sjs +Example . oops(2) +//│ Parsed tree: +//│ App: +//│ lhs = App: +//│ lhs = Ident of "." +//│ rhs = Tup of Ls of +//│ Ident of "Example" +//│ Ident of "oops" +//│ rhs = Tup of Ls of +//│ IntLit of 2 +//│ JS: +//│ let tmp; tmp = this.call(this.Example, this.oops); tmp(2) +//│ = [ 2, 1 ] + +Example. oops(2) +//│ = [ 2, 1 ] + +// * This one is a normal method call; there is no `oops` in `Example` +:re +Example .oops(2) +//│ ═══[RUNTIME ERROR] TypeError: this.Example.oops is not a function + + +id(Example) . oops(2) +//│ = [ 2, 1 ] + + +class Example2(val a) with + val a = 1 + fun f(inc) = Example2(a + inc) + +let oops = Example2(0).f +//│ oops = [Function: f] + +:todo +new Example(1) + . oops(2) +//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(.),App(Ident(oops),Tup(List(IntLit(2))))))) (of class hkmc2.syntax.Tree$OpBlock) + +:todo +new Example(1) + . oops(2) + . oops(2) +//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(.),App(Ident(oops),Tup(List(IntLit(2))))), (Ident(.),App(Ident(oops),Tup(List(IntLit(2))))))) (of class hkmc2.syntax.Tree$OpBlock) + + diff --git a/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls new file mode 100644 index 000000000..2cdf252cf --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls @@ -0,0 +1,57 @@ +:js + + +let + x = 0 + y = 0 +//│ x = 0 +//│ y = 0 + +set + x += 1 + y += 2 + +[x, y] +//│ = [ 1, 2 ] + + +:p +abstract + class + Foo(a) + Bar(b) +//│ |abstract|→|class|→|Foo|(|a|)|↵|Bar|(|b|)|←|←| +//│ Parsed: +//│ Modified(keyword 'abstract',None,TypeDef(Cls,App(Ident(Foo),Tup(List(Ident(a)))),None,None)) +//│ Modified(keyword 'abstract',None,TypeDef(Cls,App(Ident(Bar),Tup(List(Ident(b)))),None,None)) + +:todo +abstract class + Foo(a) + Bar(b) +//│ ╔══[PARSE ERROR] Unexpected identifier here +//│ ║ l.31: Bar(b) +//│ ╙── ^^^ + + +abstract class Foo +module + A extends Foo + B extends Foo + + +// * Would be cool if something like this was supported: +:pe +:ge +module \$ extends Foo + A + B +//│ ╔══[LEXICAL ERROR] unexpected character '$' +//│ ║ l.46: module \$ extends Foo +//│ ╙── ^ +//│ ╔══[PARSE ERROR] Expected end of input; found error instead +//│ ║ l.46: module \$ extends Foo +//│ ╙── ^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Invalid or unexpected token + + diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls new file mode 100644 index 000000000..8f12a04d2 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -0,0 +1,67 @@ +:js + + +class Pair(a, b) +class A +class B + + +:sjs +x => if x is Pair(A, B) then 1 +//│ JS: +//│ (x) => { +//│ let param0, param1; +//│ if (x instanceof this.Pair) { +//│ param0 = x.a; +//│ param1 = x.b; +//│ if (param0 instanceof this.A) { +//│ if (param1 instanceof this.B) { +//│ return 1 +//│ } else { +//│ throw new this.Error("match error") +//│ } +//│ } else { +//│ throw new this.Error("match error") +//│ } +//│ } else { +//│ throw new this.Error("match error") +//│ } +//│ } +//│ = [Function (anonymous)] + + +// :e // FIXME: should be an exhaustiveness error +:sjs +fun f(x) = if x is + Pair(A, A) then 1 + Pair(B, B) then 2 +//│ JS: +//│ function f(x) { +//│ let param0, param1; +//│ if (x instanceof globalThis.Pair) { +//│ param0 = x.a; +//│ param1 = x.b; +//│ if (param0 instanceof globalThis.A) { +//│ if (param1 instanceof globalThis.A) { +//│ return 1 +//│ } else { +//│ throw new globalThis.Error("match error") +//│ } +//│ } else { +//│ if (param0 instanceof globalThis.B) { +//│ if (param1 instanceof globalThis.B) { +//│ return 2 +//│ } else { +//│ throw new globalThis.Error("match error") +//│ } +//│ } else { +//│ throw new globalThis.Error("match error") +//│ } +//│ } +//│ } else { +//│ throw new globalThis.Error("match error") +//│ } +//│ }; +//│ undefined + +