Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9c3cea1
Add `staged` keyword
ChingLongTin Sep 30, 2025
2711dd9
remove comments
yeungsinchun Sep 30, 2025
9298f36
add Block.mls
ChingLongTin Oct 31, 2025
4796cad
Add Block.mls into DiffMaker and prepare the function for lowering
yeungsinchun Oct 28, 2025
b072e4a
add block.mjs also in MLsCompiler
yeungsinchun Oct 30, 2025
35106c0
lower 'staged' annotations to Block and add display of 'staged' anno…
yeungsinchun Oct 30, 2025
4efccdf
amend! Add Block.mls into DiffMaker and prepare the function for lowe…
ChingLongTin Oct 31, 2025
f9ad612
clean up Syntax.mls
ChingLongTin Oct 31, 2025
f763b03
fiddle with printing staged keyword in FunDefn
ChingLongTin Oct 31, 2025
85f86c3
revert whitespace
ChingLongTin Oct 31, 2025
a39765e
revert change to printing
ChingLongTin Oct 31, 2025
ee681c9
move isStaged inside Fun
ChingLongTin Oct 31, 2025
f77c44c
more formatting fixes
ChingLongTin Oct 31, 2025
dcb0ed6
remove import of block inside MLsCompiler
yeungsinchun Oct 31, 2025
bfac96d
update diff test result (for the staged annotation)
yeungsinchun Oct 31, 2025
a81c0e9
fix importing for Block.mls
ChingLongTin Nov 1, 2025
4ae8897
Update Syntax.mls
ChingLongTin Nov 1, 2025
88c3a5e
move detecting staged annotation to ClsLikeDefn
ChingLongTin Nov 4, 2025
17701b9
move importing to diff testing compiler flag
ChingLongTin Nov 4, 2025
5ccb64c
revert formatting changes
ChingLongTin Nov 4, 2025
980d89c
Update Printer.scala
ChingLongTin Nov 5, 2025
ce1e38c
Update Lowering.scala
ChingLongTin Nov 5, 2025
ec3e7b8
Merge branch 'hkmc2' into staged-syntax
ChingLongTin Nov 5, 2025
317c6f6
remove redundant :pt
ChingLongTin Nov 5, 2025
9023eec
fix syntax error
ChingLongTin Nov 5, 2025
e4d7b22
move instrumentation to JSBackendDiffMaker
ChingLongTin Nov 6, 2025
6576f28
rename compiler flag to :staging
ChingLongTin Nov 6, 2025
4191464
Revert "remove redundant :pt"
ChingLongTin Nov 6, 2025
817e828
combine import statements to list
ChingLongTin Nov 6, 2025
b10e975
Revert "move instrumentation to JSBackendDiffMaker"
ChingLongTin Nov 6, 2025
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
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ case class Config(
sanityChecks: Opt[SanityChecks],
effectHandlers: Opt[EffectHandlers],
liftDefns: Opt[LiftDefns],
stageCode: Bool,
target: CompilationTarget,
):

Expand All @@ -35,6 +36,7 @@ object Config:
// sanityChecks = S(SanityChecks(light = true)),
effectHandlers = N,
liftDefns = N,
stageCode = false,
target = CompilationTarget.JS
)

Expand Down
15 changes: 15 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Instrumentation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hkmc2
package codegen

import utils.*
import hkmc2.Message.MessageContext

class Instrumentation(using Raise) extends BlockTransformer(new SymbolSubst()):
def transform(prgm: Program) = Program(prgm.imports, applyBlock(prgm.main))

override def applyDefn(d: Defn)(k: Defn => Block): Block = d match
case defn: ClsLikeDefn =>
if defn.sym.defn.exists(_.hasStagedModifier.isDefined) && defn.companion.isDefined
then raise(WarningReport(msg"`staged` keyword doesn't do anything currently." -> defn.sym.toLoc :: Nil))
super.applyDefn(defn)(k)
case b => super.applyDefn(b)(k)
9 changes: 8 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import utils.*

import hkmc2.Message.MessageContext

import codegen.Instrumentation

import semantics.*, ucs.FlatPattern
import hkmc2.{semantics => sem}
import semantics.{Term => st}
Expand Down Expand Up @@ -1068,7 +1070,11 @@ class Lowering()(using Config, TL, Raise, State, Ctx):

val bufferable = BufferableTransform().transform(lifted)

val res = MergeMatchArmTransformer.applyBlock(bufferable)
val merged = MergeMatchArmTransformer.applyBlock(bufferable)

val res =
if config.stageCode then Instrumentation(using summon).applyBlock(merged)
else merged

Program(
imps.map(imp => imp.sym -> imp.str),
Expand All @@ -1095,6 +1101,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
def reportAnnotations(target: Statement, annotations: Ls[Annot]): Unit =
annotations.foreach:
case Annot.Untyped => ()
case Annot.Modifier(syntax.Keyword("staged")) => ()
case annot => raise:
WarningReport(msg"This annotation has no effect." -> annot.toLoc :: Nil)

Expand Down
3 changes: 2 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ object Printer:
val docPubFlds = if publicFields.isEmpty then doc"" else doc" # ${pubFields}"
val docBody = if publicFields.isEmpty && privateFields.isEmpty then doc"" else doc" { #{ ${docPrivFlds}${docPubFlds} #} # }"
val docCtorParams = if clsParams.isEmpty then doc"" else doc"(${ctorParams.mkDocument(", ")})"
doc"class ${own.fold("")(_.toString+"::")}${sym.nme}${docCtorParams}${docBody}"
val docStaged = if sym.defn.forall(_.hasStagedModifier.isEmpty) then doc"" else doc"staged "
doc"${docStaged}class ${own.fold("")(_.toString+"::")}${sym.nme}${docCtorParams}${docBody}"

def mkDocument(arg: Arg)(using Raise, Scope): Document =
val doc = mkDocument(arg.value)
Expand Down
4 changes: 3 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ object Elaborator:
val definitionMetadataSymbol = TempSymbol(N, "definitionMetadata")
val prettyPrintSymbol = TempSymbol(N, "prettyPrint")
val termSymbol = TempSymbol(N, "Term")
val blockSymbol = TempSymbol(N, "Block")
val shapeSymbol = TempSymbol(N, "Shape")
val wasmSymbol = TempSymbol(N, "wasm")
val effectSigSymbol = ClassSymbol(DummyTypeDef(syntax.Cls), Ident("EffectSig"))
val nonLocalRetHandlerTrm =
Expand Down Expand Up @@ -319,7 +321,7 @@ extends Importer:
case _ => N

def annot(tree: Tree): Ctxl[Opt[Annot]] = tree match
case Keywrd(kw @ (Keyword.`abstract` | Keyword.`declare` | Keyword.`data`)) => S(Annot.Modifier(kw))
case Keywrd(kw @ (Keyword.`abstract` | Keyword.`declare` | Keyword.`data` | Keyword.`staged`)) => S(Annot.Modifier(kw))
case _ => term(tree) match
case Term.Error => N
case trm =>
Expand Down
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ sealed abstract class Definition extends Declaration, Statement:
val annotations: Ls[Annot]
def hasDeclareModifier: Opt[Annot.Modifier] = annotations.collectFirst:
case mod @ Annot.Modifier(Keyword.`declare`) => mod
def hasStagedModifier: Opt[Annot.Modifier] = annotations.collectFirst:
case mod @ Annot.Modifier(Keyword.`staged`) => mod

sealed trait CompanionValue extends Definition

Expand Down
3 changes: 2 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ object Keyword:
val `abstract` = Keyword("abstract", N, N)
val `constructor` = Keyword("constructor", N, N)
val `virtual` = Keyword("virtual", N, N)
val `staged` = Keyword("staged", N, N)
val `true` = Keyword("true", N, N)
val `false` = Keyword("false", N, N)
val `public` = Keyword("public", N, N)
Expand Down Expand Up @@ -168,5 +169,5 @@ object Keyword:
type LetLike = `let`.type | `set`.type

type Modifier = `in`.type | `out`.type | `mut`.type | `abstract`.type | `declare`.type | `data`.type | `virtual`.type | `override`.type |
`public`.type | `private`.type
`public`.type | `private`.type | `staged`.type

3 changes: 2 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/syntax/Lexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ object Lexer:
"undefined",
"abstract",
"constructor",
"virtual"
"virtual",
"staged"
)

private val SEP = "┊"
Expand Down
1 change: 1 addition & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ class ParseRules(using State):
modified(`public`),
modified(`private`),
modified(`out`),
modified(`staged`),
singleKw(`true`)(BoolLit(true)),
singleKw(`false`)(BoolLit(false)),
singleKw(`undefined`)(UnitLit(false)),
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 @@ -243,6 +243,8 @@ enum Tree extends AutoLocated:
Annotated(kw, s.desugared)
case Modified(kw @ Keywrd(Keyword.`abstract`), s) =>
Annotated(kw, s.desugared)
case Modified(kw @ Keywrd(Keyword.`staged`), s) =>
Annotated(kw, s.desugared)
case Modified(kw @ Keywrd(Keyword.`mut`), TermDef(ImmutVal, anme, rhs)) =>
TermDef(MutVal, anme, rhs).withLocOf(this).desugared
case _ => m
Expand Down
Empty file.
Empty file.
28 changes: 28 additions & 0 deletions hkmc2/shared/src/test/mlscript/staging/Syntax.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

:pt
staged module A
//│ Parsed tree:
//│ Modified:
//│ modifier = Keywrd of keyword 'staged'
//│ body = TypeDef:
//│ k = Mod
//│ head = Ident of "A"
//│ rhs = N

// TODO: reject these annotations?
staged object Foo

staged class Foo

staged fun f() = 0

:js
:slot
:staging
:w
staged module A
//│ ╔══[WARNING] `staged` keyword doesn't do anything currently.
//│ ║ l.23: staged module A
//│ ╙── ^^^^^^^^
//│ Pretty Lowered:
//│ define staged class A in set block$res = undefined in end
5 changes: 5 additions & 0 deletions hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:

val runtimeNme = baseScp.allocateName(Elaborator.State.runtimeSymbol)
val termNme = baseScp.allocateName(Elaborator.State.termSymbol)
val blockNme = baseScp.allocateName(Elaborator.State.blockSymbol)
val shapeNme = baseScp.allocateName(Elaborator.State.shapeSymbol)
val definitionMetadataNme = baseScp.allocateName(Elaborator.State.definitionMetadataSymbol)
val prettyPrintNme = baseScp.allocateName(Elaborator.State.prettyPrintSymbol)

Expand All @@ -57,6 +59,9 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker:
h.execute(s"const $definitionMetadataNme = Symbol.for(\"mlscript.definitionMetadata\");")
h.execute(s"const $prettyPrintNme = Symbol.for(\"mlscript.prettyPrint\");")
if importQQ.isSet then importRuntimeModule(termNme, termFile)
if stageCode.isSet then
importRuntimeModule(blockNme, blockFile)
importRuntimeModule(shapeNme, shapeFile)
h

private var hostCreated = false
Expand Down
10 changes: 10 additions & 0 deletions hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ abstract class MLsDiffMaker extends DiffMaker:
val predefFile: os.Path // * Contains MLscript standard library definitions
val runtimeFile: os.Path = predefFile/os.up/"Runtime.mjs" // * Contains MLscript runtime definitions
val termFile: os.Path = predefFile/os.up/"Term.mjs" // * Contains MLscript runtime term definitions
val blockFile: os.Path = predefFile/os.up/"Block.mjs" // * Contains MLscript runtime block definitions
val shapeFile: os.Path = predefFile/os.up/"Shape.mjs" // * Contains MLscript runtime shape definitions

val wd = file / os.up

Expand Down Expand Up @@ -68,6 +70,7 @@ abstract class MLsDiffMaker extends DiffMaker:
val stackSafe = Command("stackSafe")(_.trim)
val liftDefns = NullaryCommand("lift")
val importQQ = NullaryCommand("qq")
val stageCode = NullaryCommand("staging")

def mkConfig: Config =
import Config.*
Expand All @@ -93,6 +96,7 @@ abstract class MLsDiffMaker extends DiffMaker:
,
)),
liftDefns = Opt.when(liftDefns.isSet)(LiftDefns()),
stageCode = stageCode.isSet,
target = if wasm.isSet then CompilationTarget.Wasm else CompilationTarget.JS,
)

Expand Down Expand Up @@ -156,6 +160,12 @@ abstract class MLsDiffMaker extends DiffMaker:
given Config = mkConfig
processTrees(
PrefixApp(Keywrd(`import`), StrLit(termFile.toString)) :: Nil)
if stageCode.isSet then
given Config = mkConfig
processTrees(
PrefixApp(Keywrd(`import`), StrLit(blockFile.toString))
:: PrefixApp(Keywrd(`import`), StrLit(shapeFile.toString))
:: Nil)
super.init()


Expand Down
Loading