Skip to content

Commit

Permalink
Fix parsing bug and add Stack zipping with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK committed Dec 12, 2024
1 parent d427826 commit 3127ccf
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 20 deletions.
6 changes: 3 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Keyword(
def assumeRightPrec: Int = rightPrec.getOrElse(lastWords(s"$this does not have right precedence"))
def leftPrecOrMin: Int = leftPrec.getOrElse(Int.MinValue)
def rightPrecOrMin: Int = rightPrec.getOrElse(Int.MinValue)
// def rightPrecOrMax: Int = rightPrec.getOrElse(Int.MaxValue)
def rightPrecOrMax: Int = rightPrec.getOrElse(Int.MaxValue)
override def toString: Str = s"keyword '$name'"

object Keyword:
Expand Down Expand Up @@ -108,8 +108,8 @@ object Keyword:
val `abstract` = Keyword("abstract", N, N)
val `constructor` = Keyword("constructor", N, N)
val `virtual` = Keyword("virtual", N, N)
val `true` = Keyword("true", N, curPrec)
val `false` = Keyword("false", N, curPrec)
val `true` = Keyword("true", N, N)
val `false` = Keyword("false", N, N)
val `public` = Keyword("public", N, N)
val `private` = Keyword("private", N, N)
val `return` = Keyword("return", N, curPrec)
Expand Down
11 changes: 6 additions & 5 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,9 @@ abstract class Parser(
yeetSpaces match
case (tok @ BRACKETS(Indent | Curly, toks), loc) :: _ if subRule.blkAlt.isEmpty =>
consume
rec(toks, S(tok.innerLoc), tok.describe).concludeWith(_.parseRule(kw.assumeRightPrec, subRule))
rec(toks, S(tok.innerLoc), tok.describe).concludeWith(_.parseRule(kw.rightPrecOrMax, subRule))
case _ =>
parseRule(kw.assumeRightPrec, subRule)
parseRule(kw.rightPrecOrMax, subRule)
case N =>
if verbose then printDbg(s"$$ cannot find a rule starting with: ${id.name}")
rule.exprAlt match
Expand Down Expand Up @@ -460,9 +460,10 @@ abstract class Parser(

// TODO: rm `allowIndentedBlock`? Seems it can always be `true`
def expr(prec: Int, allowIndentedBlock: Bool = true)(using Line): Tree =
parseRule(prec,
if allowIndentedBlock then prefixRulesAllowIndentedBlock else prefixRules
).getOrElse(errExpr) // * a `None` result means an alread-reported error
val res = parseRule(prec,
if allowIndentedBlock then prefixRulesAllowIndentedBlock else prefixRules
).getOrElse(errExpr) // * a `None` result means an alread-reported error
exprCont(res, prec, allowIndentedBlock)

def simpleExpr(prec: Int)(using Line): Tree = wrap(prec)(simpleExprImpl(prec))
def simpleExprImpl(prec: Int): Tree =
Expand Down
1 change: 1 addition & 0 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Predef$class = class Predef {
constructor() {
this.assert = console.assert;
this.MatchResult = function MatchResult(captures1) { return new MatchResult.class(captures1); };
this.MatchResult.class = class MatchResult {
constructor(captures) {
Expand Down
2 changes: 2 additions & 0 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mls
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ fun (|>.) call(receiver, f)(...args) = f.call(receiver, ...args)
fun print(x) =
console.log(String(x))

val assert = console.assert

fun tupleSlice(xs, i, j) =
globalThis.Array.prototype.slice.call(xs, i, xs.length - j)

Expand Down
120 changes: 120 additions & 0 deletions hkmc2/shared/src/test/mlscript-compile/Stack.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Predef from "./Predef.mjs";
const Stack$class = class Stack {
constructor() {
this.Cons = function Cons(head1, tail1) { return new Cons.class(head1, tail1); };
Expand All @@ -24,6 +25,125 @@ const Stack$class = class Stack {
} else {
return false;
}
}
reverseAndAppend(xs1, tail) {
let param0, param1, h, t, tmp;
if (xs1 instanceof this.Cons.class) {
param0 = xs1.head;
param1 = xs1.tail;
h = param0;
t = param1;
tmp = this.Cons(h, tail);
return this.reverseAndAppend(t, tmp);
} else {
if (xs1 instanceof this.Nil.class) {
return tail;
} else {
throw new globalThis.Error("match error");
}
}
}
reverse(xs2) {
return this.reverseAndAppend(xs2, this.Nil);
}
fromArray(arr) {
let ls, i, len, scrut, tmp, tmp1, tmp2, tmp3;
ls = this.Nil;
i = 0;
len = arr.length;
tmp4: while (true) {
scrut = i < len;
if (scrut) {
tmp = arr.at(i) ?? null;
tmp1 = this.Cons(tmp, ls);
ls = tmp1;
tmp2 = i + 1;
i = tmp2;
tmp3 = null;
continue tmp4;
} else {
tmp3 = null;
}
break;
}
return ls;
}
toReverseArray(xs3) {
let arr1, i, param0, param1, h, t, tmp, tmp1;
arr1 = [];
i = 0;
tmp2: while (true) {
if (xs3 instanceof this.Cons.class) {
param0 = xs3.head;
param1 = xs3.tail;
h = param0;
t = param1;
tmp = arr1.push(h) ?? null;
xs3 = t;
tmp1 = null;
continue tmp2;
} else {
tmp1 = null;
}
break;
}
return arr1;
}
zip(...xss) {
let tmp, tmp1;

const this$Stack = this;
function go(heads, tails) {
return (caseScrut) => {
let param0, param1, h, t, param01, param11, h2, t2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11;
if (caseScrut instanceof this$Stack.Cons.class) {
param0 = caseScrut.head;
param1 = caseScrut.tail;
h = param0;
t = param1;
if (h instanceof this$Stack.Cons.class) {
param01 = h.head;
param11 = h.tail;
h2 = param01;
t2 = param11;
tmp2 = this$Stack.Cons(h2, heads);
tmp3 = this$Stack.Cons(t2, tails);
tmp4 = go(tmp2, tmp3);
return tmp4(t) ?? null;
} else {
if (h instanceof this$Stack.Nil.class) {
tmp5 = go(heads, tails);
return tmp5(t) ?? null;
} else {
throw new globalThis.Error("match error");
}
}
} else {
if (caseScrut instanceof this$Stack.Nil.class) {
if (heads instanceof this$Stack.Nil.class) {
if (tails instanceof this$Stack.Nil.class) {
tmp6 = true;
} else {
tmp6 = false;
}
tmp7 = Predef.assert(tmp6) ?? null;
return (tmp7 , this$Stack.Nil);
} else {
tmp8 = this$Stack.toReverseArray(heads);
tmp9 = go(this$Stack.Nil, this$Stack.Nil);
tmp10 = this$Stack.reverse(tails);
tmp11 = tmp9(tmp10) ?? null;
return this$Stack.Cons(tmp8, tmp11);
}
} else {
throw new globalThis.Error("match error");
}
}
};
}
tmp = go(this.Nil, this.Nil);
tmp1 = this.fromArray(xss);
return tmp(tmp1) ?? null;
}
toString() { return "Stack"; }
}; const Stack = new Stack$class;
Expand Down
40 changes: 40 additions & 0 deletions hkmc2/shared/src/test/mlscript-compile/Stack.mls
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

import "./Predef.mls"
open Predef


// TODO
// type Stack[A] = Stack.Cons[A] | Stack.Nil

Expand All @@ -9,4 +13,40 @@ object Nil

fun isEmpty(xs) = xs is Nil

fun reverseAndAppend(xs, tail) = if xs is
h :: t then reverseAndAppend(t, h :: tail)
Nil then tail

fun reverse(xs) = reverseAndAppend(xs, Nil)

fun fromArray(arr) =
let
ls = Nil
i = 0
len = arr.length
while i < len do
ls = arr.at(i) :: ls
set i += 1
ls

fun toReverseArray(xs) =
let
arr = []
i = 0
while xs is
h :: t do
arr.push(h)
set xs = t
arr

fun zip(...xss) =
fun go(heads, tails) = case
h :: t and h is
h2 :: t2 then go(h2 :: heads, t2 :: tails)(t)
Nil then go(heads, tails)(t)
Nil and heads is
Nil then assert(tails is Nil); Nil
else heads toReverseArray() :: go(Nil, Nil)(tails reverse())
go(Nil, Nil) of fromArray(xss)


19 changes: 8 additions & 11 deletions hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ fun f(x) = x { + 1, * 2 }
//│ ╔══[PARSE ERROR] Unexpected comma in this position
//│ ║ l.66: fun f(x) = x { + 1, * 2 }
//│ ╙── ^
//│ ╔══[PARSE ERROR] Unexpected operator here
//│ ║ l.66: fun f(x) = x { + 1, * 2 }
//│ ╙── ^
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),IntLit(1)))) (of class hkmc2.syntax.Tree$OpBlock)
//│ /!!!\ Uncaught error: scala.MatchError: OpBlock(List((Ident(+),App(Ident(*),Tup(List(IntLit(1), IntLit(2))))))) (of class hkmc2.syntax.Tree$OpBlock)


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

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


Expand All @@ -149,10 +146,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.150: is 1 then "b"
//│ ║ l.147: is 1 then "b"
//│ ╙── ^^
//│ ╔══[PARSE ERROR] Expected end of input; found literal instead
//│ ║ l.150: is 1 then "b"
//│ ║ l.147: is 1 then "b"
//│ ╙── ^
//│ = [Function: f]

Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/codegen/While.mls
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ f(Cons(1, Cons(2, Cons(3, 0))))

:fixme
() => while true then 0
//│ /!!!\ Uncaught error: scala.MatchError: InfixApp(InfixApp(Tup(List()),keyword '=>',IfLike(keyword 'while',None,BoolLit(true))),keyword 'then',IntLit(0)) (of class hkmc2.syntax.Tree$InfixApp)
//│ /!!!\ Uncaught error: scala.MatchError: InfixApp(IfLike(keyword 'while',None,BoolLit(true)),keyword 'then',IntLit(0)) (of class hkmc2.syntax.Tree$InfixApp)

:fixme
while log("Hello World"); false
Expand Down
62 changes: 62 additions & 0 deletions hkmc2/shared/src/test/mlscript/std/StackTests.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
:js

import "../../mlscript-compile/Stack.mls"

open Stack


let
s1 = 1 :: 2 :: 3 :: Nil
s2 = "a" :: "b" :: "c" :: Nil
s3 = true :: false :: Nil
//│ > Cons {
//│ > head: 1,
//│ > tail: Cons { head: 2, tail: Cons { head: 3, tail: [Nil] } }
//│ s1 = }
//│ > Cons {
//│ > head: 'a',
//│ > tail: Cons { head: 'b', tail: Cons { head: 'c', tail: [Nil] } }
//│ s2 = }
//│ > Cons {
//│ > head: true,
//│ > tail: Cons { head: false, tail: Nil { class: [class Nil] } }
//│ s3 = }


// *** Zipping ***

zip of Nil
//│ = Nil { class: [class Nil] }

zip of s1
//│ > Cons {
//│ > head: [ 1 ],
//│ > tail: Cons { head: [ 2 ], tail: Cons { head: [Array], tail: [Nil] } }
//│ = }

zip of s1, s2
//│ > Cons {
//│ > head: [ 1, 'a' ],
//│ > tail: Cons { head: [ 2, 'b' ], tail: Cons { head: [Array], tail: [Nil] } }
//│ = }

zip of s1, s3
//│ > Cons {
//│ > head: [ 1, true ],
//│ > tail: Cons {
//│ > head: [ 2, false ],
//│ > tail: Cons { head: [Array], tail: [Nil] }
//│ > }
//│ = }

s1 zip(s2)
//│ > Cons {
//│ > head: [ 1, 'a' ],
//│ > tail: Cons { head: [ 2, 'b' ], tail: Cons { head: [Array], tail: [Nil] } }
//│ = }

print of ...
s1 zip of s2, s3
//│ > Cons(1,a,true, Cons(2,b,false, Cons(3,c, Nil)))


0 comments on commit 3127ccf

Please sign in to comment.