Skip to content

Commit

Permalink
Merge branch 'hkmc2' into hkmc2-ucs2
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK authored Nov 7, 2024
2 parents 72bf8ee + fe43485 commit 00de2a2
Show file tree
Hide file tree
Showing 40 changed files with 421 additions and 63 deletions.
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
10 changes: 8 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 15 additions & 6 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))*)

Expand All @@ -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"
Expand Down Expand Up @@ -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`):
Expand Down Expand Up @@ -258,13 +260,20 @@ 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 :+
ParseRule(prefixRules.name, prefixRules.omitAltsStr)(prefixRules.alts :+
(Blk(
ParseRule("block"):
End(())
Expand Down
22 changes: 11 additions & 11 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ =>
Expand Down Expand Up @@ -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 =>
Expand All @@ -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


Expand All @@ -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) =>
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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 _ =>
Expand Down
35 changes: 35 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/Literals.mls
Original file line number Diff line number Diff line change
@@ -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
//│ ╙── ^^^


18 changes: 18 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls
Original file line number Diff line number Diff line change
@@ -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


2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
Expand Down
4 changes: 0 additions & 4 deletions hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls
Original file line number Diff line number Diff line change
Expand Up @@ -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
//│ ╙── ^^^^


40 changes: 29 additions & 11 deletions hkmc2/shared/src/test/mlscript/bbml/bbQQ.mls
Original file line number Diff line number Diff line change
Expand Up @@ -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, ⊥, ?]
Expand All @@ -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 ⊤ -> ⊥, ⊥, ?]
Expand Down Expand Up @@ -52,23 +61,32 @@ 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[⊥, ⊥, ?]

: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[⊥, ⊥, ?]


: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[⊥, ⊥, ?]


Expand All @@ -83,29 +101,29 @@ 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, ?]

: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, ?]})
Expand Down
Loading

0 comments on commit 00de2a2

Please sign in to comment.