Skip to content

Commit

Permalink
Add imperfect support for operator blocks & fix misc. issues
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK committed Dec 19, 2024
1 parent f0a04d2 commit a248f23
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 45 deletions.
4 changes: 4 additions & 0 deletions hkmc2/jvm/src/test/scala/hkmc2/DiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ abstract class DiffMaker:
if diff.forall(l => l.startsWith(output.outputMarker) || l.startsWith(output.diffMidMarker) || l.startsWith(output.diff3MidMarker) || l.isEmpty) then {
for _ <- 1 to blankLines do out.println()
} else {
val blockLineNum = allLines.size - lines.size + 1
failures += blockLineNum
doFail(blockLineNum,
s"Unmerged non-output changes at $relativeName.${file.ext}:" + blockLineNum)
unmergedChanges += allLines.size - lines.size + 1
out.println(output.diffBegMarker)
diff.foreach(out.println)
Expand Down
9 changes: 8 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ object Elaborator:
val Num = assumeBuiltinCls("Num")
val Str = assumeBuiltinCls("Str")
val Predef = assumeBuiltinMod("Predef")
def getBuiltinOp(op: Str): Opt[Str] = if getBuiltin(op).isDefined then builtinBinOps.get(op) else N
def getBuiltinOp(op: Str): Opt[Str] =
if getBuiltin(op).isDefined then builtinBinOps.get(op) else N

object Ctx:
abstract class Elem:
Expand Down Expand Up @@ -319,6 +320,12 @@ extends Importer:
Term.Deref(term(rhs))
case App(Ident("~"), Tree.Tup(rhs :: Nil)) =>
term(rhs)
case tree @ App(lhs, OpBlock(ops)) =>
ops.foldLeft(term(lhs)):
case (acc, (op, arg)) =>
val sym = FlowSymbol("‹app-res›")
val tup = new Tup(Nil) // TODO
Term.App(term(op), Term.Tup(PlainFld(acc) :: PlainFld(term(arg)) :: Nil)(tup))(tree, sym)
case tree @ App(lhs, rhs) =>
val sym = FlowSymbol("‹app-res›")
val lt = term(lhs, inAppPrefix = true)
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 @@ -340,6 +340,9 @@ sealed abstract class Elem:
case Spd(_, term) => term :: Nil
def showDbg: Str
final case class Fld(flags: FldFlags, term: Term, asc: Opt[Term]) extends Elem with FldImpl
object PlainFld:
def apply(term: Term) = Fld(FldFlags.empty, term, N)
def unapply(fld: Fld): Opt[Term] = S(fld.term)
final case class Spd(eager: Bool, term: Term) extends Elem:
def showDbg: Str = (if eager then "..." else "..") + term.showDbg

Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ abstract class Parser(
consume
val bod = yeetSpaces match
case Nil | (COMMA, _) :: _ => N
case _ => S(simpleExprImpl(prec))
case _ => S(expr(prec))
Spread(if dotDotDot then Keyword.`...` else Keyword.`..`, S(loc), bod)
case (tok, loc) :: _ =>
TODO(tok)
Expand Down
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ enum Tree extends AutoLocated:
case id: Ident => S(N, id, N)
case Spread(Keyword.`..`, _, S(id: Ident)) => S(S(false), id, N)
case Spread(Keyword.`...`, _, S(id: Ident)) => S(S(true), id, N)
case Spread(Keyword.`..`, _, S(und: Under)) => S(S(false), new Ident("_").withLocOf(und), N)
case Spread(Keyword.`...`, _, S(und: Under)) => S(S(true), new Ident("_").withLocOf(und), N)
case InfixApp(lhs: Ident, Keyword.`:`, rhs) => S(N, lhs, S(rhs))
case TermDef(ImmutVal, inner, _) => inner.asParam

Expand Down
4 changes: 4 additions & 0 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ const Predef$class = class Predef {
};
this.TraceLogger = new TraceLogger$class;
this.TraceLogger.class = TraceLogger$class;

const this$Predef = this;
this.Test = class Test {
constructor() {
let tmp;
tmp = this$Predef.print("Test");
this.y = 1;
}
toString() { return "Test"; }
Expand Down
1 change: 1 addition & 0 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mls
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ module TraceLogger with
else ()

class Test with
print("Test")
val y = 1

46 changes: 46 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
:todo


fun foo(x){f} =
f(x)
//│ ╔══[ERROR] Expected a valid definition head, found block instead
//│ ║ l.4: fun foo(x){f} =
//│ ╙── ^

foo(1){x => x}
//│ ╔══[ERROR] Name not found: foo
//│ ║ l.10: foo(1){x => x}
//│ ╙── ^^^
//│ ╔══[ERROR] Illegal juxtaposition right-hand side.
//│ ║ l.10: foo(1){x => x}
//│ ╙── ^^^^^^

foo(1)
x => x
//│ ╔══[ERROR] Name not found: foo
//│ ║ l.18: foo(1)
//│ ╙── ^^^
//│ ╔══[ERROR] Illegal juxtaposition right-hand side.
//│ ║ l.19: x => x
//│ ╙── ^^^^^^

foo(1) { x => x }
//│ ╔══[ERROR] Name not found: foo
//│ ║ l.27: foo(1) { x => x }
//│ ╙── ^^^
//│ ╔══[ERROR] Illegal juxtaposition right-hand side.
//│ ║ l.27: foo(1) { x => x }
//│ ╙── ^^^^^^

foo(1) with x => x
//│ ╔══[PARSE ERROR] Expected end of input; found 'with' keyword instead
//│ ║ l.35: foo(1) with x => x
//│ ╙── ^^^^
//│ ╔══[ERROR] Name not found: foo
//│ ║ l.35: foo(1) with x => x
//│ ╙── ^^^





85 changes: 85 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
:js


let xs = [0, 1, 2]
//│ xs = [ 0, 1, 2 ]

xs. print()
//│ > 0,1,2

let f = xs. print
//│ f = [Function (anonymous)]

f()
//│ > 0,1,2

xs.reverse()
//│ = [ 2, 1, 0 ]

xs
//│ = [ 2, 1, 0 ]

:re
xs.map(x => x * 2)
//│ ═══[RUNTIME ERROR] Error: Function expected 1 arguments but got 3

xs.map((x, i, a) => x * 2)
//│ = [ 4, 2, 0 ]

:fixme
xs.map((x, ...) => x * 2)
//│ /!!!\ Uncaught error: scala.MatchError: Spread(keyword '...',Some(Loc(11,14,MiscArrayTests.mls:+30)),None) (of class hkmc2.syntax.Tree$Spread)

xs.map((x, ..._) => x * 2)
//│ = [ 4, 2, 0 ]

fun map(xs, f) = xs.map((x, ...bs) => f(x))

xs. map(x => x * 2). print()
//│ > 4,2,0

:fixme
:p
xs.
map(x => x * 2)
//│ |xs|.|→|map|(|x| |=>| |x| |*| |2|)|←|
//│ Parsed:
//│ App(Ident(.),Tup(List(Ident(xs), Block(List(App(Ident(map),Tup(List(InfixApp(Tup(List(Ident(x))),keyword '=>',App(Ident(*),Tup(List(Ident(x), IntLit(2)))))))))))))
//│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1

:fixme
:pt
xs
. map(f)
//│ Parsed tree:
//│ App:
//│ lhs = Ident of "xs"
//│ rhs = OpBlock of Ls of
//│ Tuple2:
//│ _1 = Ident of "."
//│ _2 = App:
//│ lhs = Ident of "map"
//│ rhs = Tup of Ls of
//│ Ident of "f"
//│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1

:fixme
xs
. map(x => x * 2)
//│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1

:fixme
xs
. map(x => x * 2)
. print()
//│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1

fun f = print(0)

f
//│ > 0

f
//│ > 0


82 changes: 63 additions & 19 deletions hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
:js


:todo
2
+ 2
//│ = 4

2
+ 2
+ 2
//│ = 6

2
+ 1
* 2
//│ = 6

2
+ 1
* 2
//│ = 4

:pe
2
+ 1
* 2
//│ ╔══[PARSE ERROR] Expected end of input; found indented block instead
//│ ║ l.26: * 2
//│ ╙── ^^
//│ = 3


let f = x => x * 2
//│ f = [Function (anonymous)]

2
+ 1
|> f
//│ = 6


fun f(x) = x
+ 1
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),IntLit(1)))) (of class hkmc2.syntax.Tree$OpBlock)

:todo
f(123)
//│ = 124


fun f(x) = x
* 2
+ 1
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(*),IntLit(2)), (Ident(+),IntLit(1)))) (of class hkmc2.syntax.Tree$OpBlock)

f(123)
//│ = 247


:pt
:todo
fun f(x) = x
+ 1
* 2
Expand All @@ -33,10 +74,12 @@ fun f(x) = x
//│ Tuple2:
//│ _1 = Ident of "*"
//│ _2 = IntLit of 2
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),IntLit(1)), (Ident(*),IntLit(2)))) (of class hkmc2.syntax.Tree$OpBlock)

f(123)
//│ = 248


:pt
:todo
fun f(x) = x
+ 1
* 2
Expand All @@ -58,16 +101,17 @@ fun f(x) = x
//│ Tuple2:
//│ _1 = Ident of "*"
//│ _2 = IntLit of 2
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),App(IntLit(1),OpBlock(List((Ident(*),IntLit(2)))))))) (of class hkmc2.syntax.Tree$OpBlock)

f(123)
//│ = 125


:pe
:fixme
:pe
fun f(x) = x { + 1, * 2 }
//│ ╔══[PARSE ERROR] Unexpected comma in this position
//│ ║ l.66: fun f(x) = x { + 1, * 2 }
//│ ╙── ^
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),App(Ident(*),Tup(List(IntLit(1), IntLit(2))))))) (of class hkmc2.syntax.Tree$OpBlock)
//│ ║ l.111: fun f(x) = x { + 1, * 2 }
//│ ╙── ^


:pt
Expand Down Expand Up @@ -118,10 +162,10 @@ fun f(x) = if x
> 0 then "a"
is 0 then "b"
//│ ╔══[PARSE ERROR] Expect an operator instead of 'is' keyword
//│ ║ l.119: is 0 then "b"
//│ ║ l.163: is 0 then "b"
//│ ╙── ^^
//│ ╔══[PARSE ERROR] Unexpected 'is' keyword here
//│ ║ l.119: is 0 then "b"
//│ ║ l.163: is 0 then "b"
//│ ╙── ^^
//│ ═══[ERROR] Unrecognized operator branch.

Expand All @@ -133,11 +177,11 @@ fun f(x) = if x
foo(A) then a
bar(B) then b
//│ ╔══[ERROR] Unrecognized term split (juxtaposition).
//│ ║ l.132: fun f(x) = if x
//│ ║ l.176: fun f(x) = if x
//│ ║ ^
//│ ║ l.133: foo(A) then a
//│ ║ l.177: foo(A) then a
//│ ║ ^^^^^^^^^^^^^^^
//│ ║ l.134: bar(B) then b
//│ ║ l.178: bar(B) then b
//│ ╙── ^^^^^^^^^^^^^^^


Expand All @@ -146,10 +190,10 @@ fun f(x) = if x
is 0 then "a"
is 1 then "b"
//│ ╔══[PARSE ERROR] Expected start of statement in this position; found 'is' keyword instead
//│ ║ l.147: is 1 then "b"
//│ ║ l.191: is 1 then "b"
//│ ╙── ^^
//│ ╔══[PARSE ERROR] Expected end of input; found literal instead
//│ ║ l.147: is 1 then "b"
//│ ║ l.191: is 1 then "b"
//│ ╙── ^
//│ = [Function: f]

Expand Down
20 changes: 20 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/PredefTest.mls
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
:js


Predef.Test
//│ = [class Test]

Test === Predef.Test
//│ = true

Predef.Test === globalThis.Predef.Test
//│ = true

1 is Predef.Test
//│ = false

:fixme
new Predef.Test is Predef.Test
//│ ═══[RUNTIME ERROR] TypeError: tmp2 is not a constructor

(new Predef.Test) is Predef.Test
//│ > Test
//│ = true

new Predef.Test
//│ > Test
//│ = Test { y: 1 }

(new Predef.Test).y
//│ > Test
//│ = 1

:re
(new Predef.Test).x
//│ > Test
//│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined'


Loading

0 comments on commit a248f23

Please sign in to comment.