diff --git a/nescant/src/main/scala/com/htmlism/nescant/GlobalAddress.scala b/nescant/src/main/scala/com/htmlism/nescant/GlobalAddress.scala index d083ed1..5c8c05c 100644 --- a/nescant/src/main/scala/com/htmlism/nescant/GlobalAddress.scala +++ b/nescant/src/main/scala/com/htmlism/nescant/GlobalAddress.scala @@ -1,5 +1,11 @@ package com.htmlism.nescant -object GlobalAddress {} +object GlobalAddress { + implicit val sourceForGlobalAddress: Source[GlobalAddress] = + new Source[GlobalAddress] {} + + implicit val sinkForGlobalAddress: Sink[GlobalAddress] = + new Sink[GlobalAddress] {} +} case class GlobalAddress(n: Int) diff --git a/nescant/src/main/scala/com/htmlism/nescant/Operand.scala b/nescant/src/main/scala/com/htmlism/nescant/Operand.scala index 9684c45..0042902 100644 --- a/nescant/src/main/scala/com/htmlism/nescant/Operand.scala +++ b/nescant/src/main/scala/com/htmlism/nescant/Operand.scala @@ -1,7 +1,10 @@ package com.htmlism.nescant -sealed trait Operand +trait Operand[A] { + def encode(x: A): String +} -case class LiteralOperand(value: String) extends Operand - -case class NamedOperand(name: String, value: String) extends Operand +object Operand { + implicit val operandForInt: Operand[Int] = + _.toString +} diff --git a/nescant/src/main/scala/com/htmlism/nescant/ReadWriteLocation.scala b/nescant/src/main/scala/com/htmlism/nescant/ReadWriteLocation.scala new file mode 100644 index 0000000..4e1aee3 --- /dev/null +++ b/nescant/src/main/scala/com/htmlism/nescant/ReadWriteLocation.scala @@ -0,0 +1,21 @@ +package com.htmlism.nescant + +/** + * @param name A name for this location, used to alias its address + * + * @tparam A The input type of the write and the output type of the read + */ +case class ReadWriteLocation[A](name: String, address: ZeroPageAddress) + +object ReadWriteLocation { + implicit def sourceForReadWriteLocation[A: Operand]: Source[ReadWriteLocation[A]] = + new Source[ReadWriteLocation[A]] {} + + implicit def sinkForReadWriteLocation[A: Operand]: Sink[ReadWriteLocation[A]] = + new Sink[ReadWriteLocation[A]] {} + + implicit def operandForReadWriteLocation[A: Operand]: Operand[ReadWriteLocation[A]] = + new Operand[ReadWriteLocation[A]] { + def encode(x: ReadWriteLocation[A]): String = "" + } +} diff --git a/nescant/src/main/scala/com/htmlism/nescant/Source.scala b/nescant/src/main/scala/com/htmlism/nescant/Source.scala index 8592c88..2cf3ed5 100644 --- a/nescant/src/main/scala/com/htmlism/nescant/Source.scala +++ b/nescant/src/main/scala/com/htmlism/nescant/Source.scala @@ -3,6 +3,6 @@ package com.htmlism.nescant trait Source[A] object Source { - // TODO int can be a source - // TODO a volitile device can be a source + implicit val sourceForInt: Source[Int] = + new Source[Int] {} } diff --git a/nescant/src/main/scala/com/htmlism/nescant/VolatileDevice.scala b/nescant/src/main/scala/com/htmlism/nescant/VolatileDevice.scala new file mode 100644 index 0000000..8822d0d --- /dev/null +++ b/nescant/src/main/scala/com/htmlism/nescant/VolatileDevice.scala @@ -0,0 +1,22 @@ +package com.htmlism.nescant + +//import com.htmlism.mos6502.model._ + +/** + * For a memory-mapped device that may return different values across multiple reads (e.g. a random number generator) + * + * @param name A name for this device, used to alias its address + * + * @tparam A The output type of the read + */ +case class VolatileDevice[A](name: String, address: ZeroPageAddress) + +object VolatileDevice { + implicit def sourceForVolatileDevice[A: Operand]: Source[VolatileDevice[A]] = + new Source[VolatileDevice[A]] {} + + implicit def operandForVolatileDevice[A: Operand]: Operand[VolatileDevice[A]] = + new Operand[VolatileDevice[A]] { + def encode(x: VolatileDevice[A]): String = "" + } +} diff --git a/nescant/src/main/scala/com/htmlism/nescant/ZeroPageAddress.scala b/nescant/src/main/scala/com/htmlism/nescant/ZeroPageAddress.scala index 9a4bf0d..79b1f62 100644 --- a/nescant/src/main/scala/com/htmlism/nescant/ZeroPageAddress.scala +++ b/nescant/src/main/scala/com/htmlism/nescant/ZeroPageAddress.scala @@ -1,5 +1,11 @@ package com.htmlism.nescant -object ZeroPageAddress {} +object ZeroPageAddress { + implicit val sourceForZeroPageAddress: Source[ZeroPageAddress] = + new Source[ZeroPageAddress] {} + + implicit val sinkForZeroPageAddress: Sink[ZeroPageAddress] = + new Sink[ZeroPageAddress] {} +} case class ZeroPageAddress(n: Int) diff --git a/nescant/src/main/scala/com/htmlism/nescant/dsl/package.scala b/nescant/src/main/scala/com/htmlism/nescant/dsl/package.scala index 3ae5fa5..b61731e 100644 --- a/nescant/src/main/scala/com/htmlism/nescant/dsl/package.scala +++ b/nescant/src/main/scala/com/htmlism/nescant/dsl/package.scala @@ -8,4 +8,10 @@ package object dsl { def g: GlobalAddress = GlobalAddress(n) } + + implicit class SinkOps[A: Sink](x: A) { + def write[B: Source](src: B): Unit = { + val _ = src + } + } } diff --git a/nescant/src/test/scala/com/htmlism/nescant/dsl/SinkSpec.scala b/nescant/src/test/scala/com/htmlism/nescant/dsl/SinkSpec.scala new file mode 100644 index 0000000..6bb1afc --- /dev/null +++ b/nescant/src/test/scala/com/htmlism/nescant/dsl/SinkSpec.scala @@ -0,0 +1,21 @@ +package com.htmlism.nescant +package dsl + +import org.scalatest.flatspec._ +import org.scalatest.matchers._ + +class SinkSpec extends AnyFlatSpec with should.Matchers { + "A zero page address" should "be a sink" in { + 123.z.write(456) + } + + "A global address" should "be a sink" in { + 123.g.write(456) + } + + "A read write location" should "be a sink" in { + val sink = ReadWriteLocation[Int]("", 0.z) + + sink.write(456) + } +} diff --git a/nescant/src/test/scala/com/htmlism/nescant/dsl/SourceSpec.scala b/nescant/src/test/scala/com/htmlism/nescant/dsl/SourceSpec.scala new file mode 100644 index 0000000..c5ca46c --- /dev/null +++ b/nescant/src/test/scala/com/htmlism/nescant/dsl/SourceSpec.scala @@ -0,0 +1,31 @@ +package com.htmlism.nescant +package dsl + +import org.scalatest.flatspec._ +import org.scalatest.matchers._ + +class SourceSpec extends AnyFlatSpec with should.Matchers { + "A number" should "be a source" in { + 123.z.write(456) + } + + "A zero page address" should "be a source" in { + 123.z.write(456.z) + } + + "A global address" should "be a source" in { + 123.z.write(456.g) + } + + "A volatile device" should "be a source" in { + val src = VolatileDevice[Int]("", 0.z) + + 123.z.write(src) + } + + "A read write location" should "be a source" in { + val src = ReadWriteLocation[Int]("", 0.z) + + 123.z.write(src) + } +}