Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/shared/main/scala/utils/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ package object utils {
case h :: t => f(h) :: t
case Nil => Nil
}
def takeWhileAndRest(p: A => Boolean): (Ls[A], Ls[A]) = {
val b = new mutable.ListBuffer[A]
var ys = ls
while (!ys.isEmpty && p(ys.head)) {
b += ys.head
ys = ys.tail
}
(b.toList, ys)
}
}

implicit final class OptionHelpers[A](opt: Opt[A]) {
Expand Down
38 changes: 38 additions & 0 deletions hkmc2/shared/src/test/mlscript/commands/StatefulComments.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
:js

:clock 10000
let i = 0
while i < 10000 do
set i += 1
//+ runtime clock time (10000): 0.761641 ms
//│ i = 10000

:clock 1000000
let i = 0
while i < 1000000 do
set i += 1
//+ runtime clock time (1000000): 6.105116 ms
//│ i = 1000000

:freeze date
Date.now()
//+ freeze (date): = 1754638286509

fun choose(choices) =
val index = Math.floor(Math.random() * choices.length)
choices.[index]

:freeze choose
let c = choose(["a", "b", "c"])
//+ freeze (choose): c = "b"

:freeze choose
if c == "a" then
"You chose a!"
else if c == "b" then
"You chose b!"
else
"You chose c!"
//+ freeze (choose): = "You chose b!"


4 changes: 2 additions & 2 deletions hkmc2Benchmarks/src/test/scala/hkmc2/BenchDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import hkmc2.syntax.Keyword
class BenchDiffMaker(val rootPath: Str, val file: os.Path, val preludeFile: os.Path, val predefFile: os.Path, val relativeName: Str)
extends LlirDiffMaker:

override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit =
super.processTerm(blk, inImport)
override def processTerm(blk: semantics.Term.Blk, inImport: Bool, statefulComments: Ls[String])(using Config, Raise): Unit =
super.processTerm(blk, inImport, statefulComments)
4 changes: 2 additions & 2 deletions hkmc2DiffTests/src/test/scala/hkmc2/BbmlDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ abstract class BbmlDiffMaker extends JSBackendDiffMaker:
var bbmlTyper: Opt[BBTyper] = None


override def processTerm(trm: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit =
super.processTerm(trm, inImport)
override def processTerm(trm: semantics.Term.Blk, inImport: Bool, statefulComments: Ls[String])(using Config, Raise): Unit =
super.processTerm(trm, inImport, statefulComments)
if bbmlOpt.isSet then
given Scope = Scope.empty
if bbmlTyper.isEmpty then
Expand Down
34 changes: 25 additions & 9 deletions hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hkmc2

import scala.collection.mutable
import mlscript.utils.*, shorthands.*
import scala.collection.mutable.ListBuffer



Expand All @@ -17,21 +18,25 @@ class Outputter(val out: java.io.PrintWriter):

val exitMarker = "=" * 100
val blockSeparator = "—" * 80

val statefulMarker = "//+ "

val fullBlockSeparator = outputMarker + blockSeparator

def apply(str: String) =
// out.println(outputMarker + str)
str.splitSane('\n').foreach(l => out.println(outputMarker + l))

def stateful(str: String) =
str.splitSane('\n').foreach(l => out.println(statefulMarker + l))


abstract class DiffMaker:

val file: os.Path
val relativeName: Str

def processOrigin(origin: Origin)(using Raise): Unit
def processOrigin(origin: Origin, statefulComments: List[String])(using Raise): Unit



Expand Down Expand Up @@ -162,7 +167,7 @@ abstract class DiffMaker:
).map("\n" + "\tat: " + _).mkString)


def processBlock(origin: Origin): Unit =
def processBlock(origin: Origin, statefulComments: List[String]): Unit =
val globalStartLineNum = origin.startLineNum
val blockLineNum = origin.startLineNum
// * ^ In previous DiffTest versions, these two could be different due to relative line numbers
Expand Down Expand Up @@ -212,7 +217,7 @@ abstract class DiffMaker:
throw d
report(blockLineNum, d :: Nil, showRelativeLineNums.isSet)

processOrigin(origin)(using raise)
processOrigin(origin, statefulComments)(using raise)

// Note: when `todo` is set, we allow the lack of errors.
// Use `todo` when the errors are expected but not yet implemented.
Expand Down Expand Up @@ -270,8 +275,8 @@ abstract class DiffMaker:
output("/!\\ Unrecognized command: " + cmd)

rec(ls)
case line :: ls if line.startsWith(output.outputMarker) //|| line.startsWith(oldOutputMarker)
=> rec(ls)
case line :: ls if line.startsWith(output.outputMarker) || line.startsWith(output.statefulMarker)
=> rec(ls) //|| line.startsWith(oldOutputMarker)
case line :: ls if line.startsWith("//") =>
out.println(line)
rec(ls)
Expand Down Expand Up @@ -301,21 +306,32 @@ abstract class DiffMaker:

val blockLineNum = allLines.size - lines.size + 1

val block = (l :: ls.takeWhile(l => (l.nonEmpty || consumeEmptyLines.isSet) && !(
val (blockU, rest) = (l :: ls).takeWhileAndRest((l => (l.nonEmpty || consumeEmptyLines.isSet) && !(
l.startsWith(output.outputMarker)
|| l.startsWith(output.diffBegMarker)
|| l.startsWith(output.statefulMarker)
// || l.startsWith(oldOutputMarker)
))).toIndexedSeq
)))
val block = blockU.toIndexedSeq
block.foreach(out.println)
val processedBlock = block
val processedBlockStr = processedBlock.mkString
val fph = new FastParseHelpers(block)

val origin = Origin(file, blockLineNum, fph)

val statefulComments = rest.takeWhile(l => l.nonEmpty && (
l.startsWith(output.statefulMarker)
|| l.startsWith(output.outputMarker)
|| l.startsWith(output.diffBegMarker)
|| l.startsWith(output.diffMidMarker)
|| l.startsWith(output.diff3MidMarker)
|| l.startsWith(output.diffEndMarker)
)).filter(l => l.startsWith(output.statefulMarker))
.map(l => l.stripPrefix(output.statefulMarker).trim)

try

processBlock(origin)
processBlock(origin, statefulComments)

catch
case oh_noes: ThreadDeath => throw oh_noes
Expand Down
44 changes: 40 additions & 4 deletions hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:
val showJS = NullaryCommand("sjs")
val showRepl = NullaryCommand("showRepl")
val traceJS = NullaryCommand("traceJS")
val runtimeClock = Command("clock")(_.trim)
val runtimeClockPrefix = "runtime clock time"

val statefulOutput = Command("freeze")(_.trim)
val statatefulOutputPrefix = "freeze"

val expect = Command("expect"): ln =>
ln.trim

Expand Down Expand Up @@ -63,8 +69,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:
override def run(): Unit =
try super.run() finally if hostCreated then host.terminate()

override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit =
super.processTerm(blk, inImport)
override def processTerm(blk: semantics.Term.Blk, inImport: Bool, statefulComments: Ls[String])(using Config, Raise): Unit =
super.processTerm(blk, inImport, statefulComments)

val outerRaise: Raise = summon
val reportedMessages = mutable.Set.empty[Str]
Expand Down Expand Up @@ -162,11 +168,28 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:

// * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first
host.execute(s"$resNme = undefined")

val timingId = runtimeClock.get.flatMap(s => if s == "" then N else S(s))
val startTime = timingId.map(_ => System.nanoTime())

mkQuery(preStr, jsStr): stdout =>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right thing to time?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just do the timing in node.js? Add a module with a time function that prints the elabsed time:

time of () =>
  foo(123)
> 456ms
= 42

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would probably make more sense, and this could be frozen. at the moment, I'm not sure if stateful comments are particularly useful for anything other than freeze

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I would suggest only using freeze instead.

stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check)
.foreach: line =>
output(s"> ${line}")

startTime.foreach: start =>
val endTime = System.nanoTime()
val elapsedMs = (endTime - start) / 1_000_000.0
statefulComments.find(_.startsWith(runtimeClockPrefix)) match
case S(time) =>
val cachedId = time.stripPrefix(s"${runtimeClockPrefix} (").split(')').headOption.getOrElse("")
if cachedId == timingId.get then
output.stateful(time)
else
output.stateful(s"${runtimeClockPrefix} (${timingId.get}): ${elapsedMs} ms")
case N =>
output.stateful(s"${runtimeClockPrefix} (${timingId.get}): ${elapsedMs} ms")

if traceJS.isSet then
host.execute(s"$runtimeNme.TraceLogger.enabled = false")

Expand Down Expand Up @@ -205,6 +228,19 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:
case "undefined" if anon =>
case "()" if anon =>
case _ =>
output(s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}")

val outputStr = s"${if anon then "" else s"$nme "}= ${result.indentNewLines("| ")}"
val currId = statefulOutput.get.getOrElse("")
// TODO: avoid execution if cache not invalidated
if statefulOutput.isSet then
statefulComments.find(_.startsWith(statatefulOutputPrefix)) match
case S(prev) =>
val cachedId = prev.stripPrefix(s"${statatefulOutputPrefix} (").split(')').headOption.getOrElse("")
if cachedId == currId then
output.stateful(prev)
else
output.stateful(s"${statatefulOutputPrefix} (${currId}): ${outputStr}")
case N =>
output.stateful(s"${statatefulOutputPrefix} (${currId}): ${outputStr}")
else
output(outputStr)

4 changes: 2 additions & 2 deletions hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ abstract class LlirDiffMaker extends BbmlDiffMaker:
entry = wholeProg.last.entry
)

override def processTerm(trm: Blk, inImport: Bool)(using Config, Raise): Unit =
super.processTerm(trm, inImport)
override def processTerm(trm: Blk, inImport: Bool, statefulComments: Ls[String])(using Config, Raise): Unit =
super.processTerm(trm, inImport, statefulComments)
if llir.isSet then
val low = ltl.givenIn:
codegen.Lowering()
Expand Down
16 changes: 8 additions & 8 deletions hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ abstract class MLsDiffMaker extends DiffMaker:
processTrees(
Modified(`import`, N, StrLit(predefFile.toString))
:: Open(Ident("Predef"))
:: Nil)
:: Nil, Nil)
if importQQ.isSet then
given Config = mkConfig
processTrees(
Modified(`import`, N, StrLit(termFile.toString)) :: Nil)
Modified(`import`, N, StrLit(termFile.toString)) :: Nil, Nil)
super.init()


Expand Down Expand Up @@ -185,7 +185,7 @@ abstract class MLsDiffMaker extends DiffMaker:
if verbose then
output(s"Imported ${resBlk.definedSymbols.size} member(s)")
curCtx = ctxWithImports
processTerm(e, inImport = true)
processTerm(e, inImport = true, Ls.empty)
catch
case err: Throwable =>
uncaught(err)
Expand All @@ -195,7 +195,7 @@ abstract class MLsDiffMaker extends DiffMaker:
override def emitDbg(str: String): Unit = output(str)


def processOrigin(origin: Origin)(using Raise): Unit =
def processOrigin(origin: Origin, statefulComments: List[String])(using Raise): Unit =
val oldCtx = curCtx

given Config = mkConfig
Expand Down Expand Up @@ -223,7 +223,7 @@ abstract class MLsDiffMaker extends DiffMaker:
// output(s"AST: $res")

if parseOnly.isUnset then
processTrees(res)(using summon, raise)
processTrees(res, statefulComments)(using summon, raise)

if showContext.isSet then
output("Env:")
Expand All @@ -234,7 +234,7 @@ abstract class MLsDiffMaker extends DiffMaker:

private var blockNum = 0

def processTrees(trees: Ls[syntax.Tree])(using Config, Raise): Unit =
def processTrees(trees: Ls[syntax.Tree], statefulComments: Ls[String])(using Config, Raise): Unit =
val elab = Elaborator(etl, file / os.up, prelude)
// val blockSymbol =
// semantics.TopLevelSymbol("block#"+blockNum)
Expand All @@ -251,11 +251,11 @@ abstract class MLsDiffMaker extends DiffMaker:
output(s"Elaborated tree:")
output(e.showAsTree(using post))

processTerm(e, inImport = false)
processTerm(e, inImport = false, statefulComments)



def processTerm(trm: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit =
def processTerm(trm: semantics.Term.Blk, inImport: Bool, statefulComments: Ls[String])(using Config, Raise): Unit =
val resolver = Resolver(rtl)
curICtx = resolver.traverseBlock(trm)(using curICtx)

Expand Down
Loading