diff --git a/src/main/scala/com/htmlism/mos6502/dsl/AsmDocumentContext.scala b/src/main/scala/com/htmlism/mos6502/dsl/AsmDocumentContext.scala
new file mode 100644
index 0000000..648b71c
--- /dev/null
+++ b/src/main/scala/com/htmlism/mos6502/dsl/AsmDocumentContext.scala
@@ -0,0 +1,43 @@
+package com.htmlism.mos6502.dsl
+
+import scala.collection.mutable.ListBuffer
+
+case class AsmDocument(xs: List[TopLevelAsmDocumentFragment])
+
+class AsmDocumentContext {
+ private val xs: ListBuffer[TopLevelAsmDocumentFragment] =
+ ListBuffer()
+
+ def push(x: TopLevelAsmDocumentFragment): Unit =
+ xs.append(x)
+
+ def toDoc: AsmDocument =
+ AsmDocument(xs.toList)
+}
+
+sealed trait TopLevelAsmDocumentFragment
+
+sealed trait AsmBlockFragment extends TopLevelAsmDocumentFragment
+
+case class DefineGroup(xs: List[Definition[_]]) extends TopLevelAsmDocumentFragment
+
+class DefineGroupContext {
+ private val xs: ListBuffer[Definition[_]] =
+ ListBuffer()
+
+ def push(x: Definition[_]): Unit =
+ xs.append(x)
+
+ def toGroup: DefineGroup =
+ DefineGroup(xs.toList)
+}
+
+case class Definition[A : Operand](name: String, x: A)
+
+class AsmBlockContext {
+ private val xs: ListBuffer[Statement] =
+ ListBuffer()
+
+ def push(x: Statement): Unit =
+ xs.append(x)
+}
\ No newline at end of file
diff --git a/src/main/scala/com/htmlism/mos6502/dsl/DslDemo.scala b/src/main/scala/com/htmlism/mos6502/dsl/DslDemo.scala
index 58a3b8f..fc4c684 100644
--- a/src/main/scala/com/htmlism/mos6502/dsl/DslDemo.scala
+++ b/src/main/scala/com/htmlism/mos6502/dsl/DslDemo.scala
@@ -49,14 +49,6 @@ object DslDemo extends App {
println()
println()
}
-
- implicit class AddressOps(n: Int) {
- def z: ZeroAddress =
- ZeroAddress(n)
-
- def addr: GlobalAddress =
- GlobalAddress(n)
- }
}
object registers {
diff --git a/src/main/scala/com/htmlism/mos6502/dsl/package.scala b/src/main/scala/com/htmlism/mos6502/dsl/package.scala
new file mode 100644
index 0000000..ae118d9
--- /dev/null
+++ b/src/main/scala/com/htmlism/mos6502/dsl/package.scala
@@ -0,0 +1,35 @@
+package com.htmlism.mos6502
+
+package object dsl {
+ def asmDoc(f: AsmDocumentContext => Unit): AsmDocument = {
+ val ctx: AsmDocumentContext =
+ new AsmDocumentContext
+
+ f(ctx)
+
+ ctx
+ .toDoc
+ }
+
+ def group(f: DefineGroupContext => Unit)(implicit ctx: AsmDocumentContext): Unit = {
+ val g: DefineGroupContext =
+ new DefineGroupContext
+
+ f(g)
+
+ ctx
+ .push(g.toGroup)
+ }
+
+ def define[A : Operand](name: String, x: A)(implicit ctx: DefineGroupContext): Unit =
+ ctx
+ .push(Definition(name, x))
+
+ implicit class AddressOps(n: Int) {
+ def z: ZeroAddress =
+ ZeroAddress(n)
+
+ def addr: GlobalAddress =
+ GlobalAddress(n)
+ }
+}
diff --git a/src/test/scala/com/htmlism/mos6502/dsl/DslSpec.scala b/src/test/scala/com/htmlism/mos6502/dsl/DslSpec.scala
new file mode 100644
index 0000000..2555e9d
--- /dev/null
+++ b/src/test/scala/com/htmlism/mos6502/dsl/DslSpec.scala
@@ -0,0 +1,39 @@
+package com.htmlism.mos6502.dsl
+
+import org.scalatest.flatspec._
+import org.scalatest.matchers._
+
+class DslSpec extends AnyFlatSpec with should.Matchers {
+
+ "the dsl" should "compile" in {
+ val doc =
+ asmDoc { implicit ctx =>
+ group { implicit g =>
+ define("snakeBodyStart", 0x12.z)
+ define("snakeDirection", 0x02.z)
+ define("snakeLength", 0x03.z)
+ }
+
+ group { implicit g =>
+ define("ASCII_w", 0x77)
+ define("ASCII_a", 0x61)
+ define("ASCII_s", 0x73)
+ define("ASCII_d", 0x64)
+ }
+ }
+
+ doc shouldEqual AsmDocument(List(
+ DefineGroup(List(
+ Definition("snakeBodyStart", 0x12.z),
+ Definition("snakeDirection", 0x02.z),
+ Definition("snakeLength", 0x03.z)
+ )),
+ DefineGroup(List(
+ Definition("ASCII_w", 0x77),
+ Definition("ASCII_a", 0x61),
+ Definition("ASCII_s", 0x73),
+ Definition("ASCII_d", 0x64)
+ ))
+ ))
+ }
+}
diff --git a/src/test/scala/com/htmlism/mos6502/dsl/MySuite.scala b/src/test/scala/com/htmlism/mos6502/dsl/MySuite.scala
new file mode 100644
index 0000000..ded4a91
--- /dev/null
+++ b/src/test/scala/com/htmlism/mos6502/dsl/MySuite.scala
@@ -0,0 +1,34 @@
+package com.htmlism.mos6502.dsl
+
+import weaver.SimpleIOSuite
+import cats.effect._
+
+// Suites must be "objects" for them to be picked by the framework
+object MySuite extends SimpleIOSuite {
+
+ // A test for non-effectful (pure) functions
+ pureTest("hello pure") {
+ expect("hello".size == 6)
+ }
+
+ val random = IO(java.util.UUID.randomUUID())
+
+ // A test for side-effecting functions
+ simpleTest("hello side-effects") {
+ for {
+ x <- random
+ y <- random
+ } yield expect(x != y)
+ }
+
+ // A test with logs
+ loggedTest("hello logs") { log =>
+ for {
+ x <- random
+ _ <- log.info(s"x : $x")
+ y <- random
+ _ <- log.info(s"y : $y")
+ } yield expect(x != y)
+ }
+
+}