Verified Commit ac964f52 authored by Soren's avatar Soren

Flesh out the inner workings of bonk enough to be developed.

Previously, bonk was very spaghetti in terms of how to develop for it and where to start. This commit changes a lot of that by implementing a starting point for parsing programs. Unlike JSR-223, you can say that a script is valid before it is run (increasing atomicity).
parent 6de7e161
......@@ -13,7 +13,8 @@ val shared = Seq(
),
libraryDependencies ++= Seq(
"com.chuusai" %%% "shapeless" % "2.3.3",
"org.scalameta" %%% "scalameta" % "4.0.0"
"org.scalameta" %%% "scalameta" % "4.0.0",
"org.scalatest" %%% "scalatest" % "3.2.0-SNAP10" % "test"
)
)
......
package bonk
trait EntryPoint[A <: StructuredLanguage[A], P] {}
trait EntryPoint[S, A <: StructuredLanguage[A, _, _, _], P] {}
package bonk
/** A language environment that contains current memory or variables */
trait Environment[L <: Language[L], E] {
trait Environment[L <: Language[L, _, _, _], E] {
def toUsefulMap[K, V](e: E): Map[K, V]
......
package bonk
/** Defines some sort of programming language or dialect */
sealed trait Language[A <: Language[A]] {
sealed trait Language[L <: Language[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C] {
final type ConstructedScript = C
/** Verifies whether the script is valid in the language and returns a post-validated script */
def verify[S, E](script: S, env: E)(
implicit containerDef: ScriptConstructor[C, L, F, S],
environmentDef: Environment[L, E]
): Either[F, ConstructedScript]
/** Runs the script from start to finish, giving some sort of output */
def run[S, E](script: S, env: E)(
implicit containerDef: ScriptContainer[A, S],
environmentDef: Environment[A, E]
): Either[Error[A], Output[A]]
def run[E](script: ConstructedScript, env: E)(
environmentDef: Environment[L, E]
): Either[F, O]
}
/** Unstructured languages just run instructions, they have no functions/subroutines/labels */
trait UnstructuredLanguage[A <: UnstructuredLanguage[A]] extends Language[A]
trait UnstructuredLanguage[L <: UnstructuredLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends Language[L, O, F, C]
/** A structured language defines some sort of way to enter the script arbitrarily */
sealed trait StructuredLanguage[A <: StructuredLanguage[A]] extends Language[A] {
sealed trait StructuredLanguage[L <: StructuredLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends Language[L, O, F, C] {
/** Runs a script's entry point */
def run[S, P, E](script: S, entry: P, env: E)(
implicit containerDef: ScriptContainer[A, S],
entryPointDef: EntryPoint[A, P],
environmentDef: Environment[A, E]
): Either[Error[A], Output[A]]
def run[P, E](script: ConstructedScript, entry: P, env: E)(
entryPointDef: EntryPoint[ConstructedScript, L, P],
environmentDef: Environment[L, E]
): Either[F, O]
}
trait UntypedLanguage[A <: UntypedLanguage[A]] extends StructuredLanguage[A]
trait UntypedLanguage[L <: UntypedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends StructuredLanguage[L, O, F, C]
sealed trait TypedLanguage[A <: TypedLanguage[A]] extends StructuredLanguage[A] {
sealed trait TypedLanguage[L <: TypedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends StructuredLanguage[L, O, F, C] {
type Definition <: TypeDefinition[A]
type Transformer[T] = TypeTransformer[A, Definition, T]
type Definition <: TypeDefinition[L]
type Transformer[T] = TypeTransformer[L, Definition, T]
/** Gets a value from a specific entry point */
def get[T, S, P, E](script: S, entry: P, environment: E)(
def get[T, P, E](script: ConstructedScript, entry: P, environment: E)(
implicit typeTransformer: Transformer[T],
containerDef: ScriptContainer[A, S],
entryPointDef: EntryPoint[A, P],
environmentDef: Environment[A, E]
entryPointDef: EntryPoint[ConstructedScript, L, P],
environmentDef: Environment[L, E]
): T
/** Evaluates the type of a script stub */
def typeOf[S, E](script: S, environment: E)(
implicit containerDef: ScriptContainer[A, S],
environmentDef: Environment[A, E]
def typeOf[E](script: ConstructedScript, environment: E)(
implicit environmentDef: Environment[L, E]
): Definition
}
trait DynamicallyTypedLanguage[A <: DynamicallyTypedLanguage[A]] extends TypedLanguage[A]
sealed trait StaticallyTypedLanguage[A <: StaticallyTypedLanguage[A]] extends TypedLanguage[A]
trait NonGenerifiedLanguage[A <: NonGenerifiedLanguage[A]] extends StaticallyTypedLanguage[A]
trait GenerifiedLanguage[A <: GenerifiedLanguage[A]] extends StaticallyTypedLanguage[A]
trait DynamicallyTypedLanguage[L <: DynamicallyTypedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[
F,
L,
O,
C
], C]
extends TypedLanguage[L, O, F, C]
sealed trait StaticallyTypedLanguage[L <: StaticallyTypedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[
F,
L,
O,
C
], C]
extends TypedLanguage[L, O, F, C]
trait NonGenerifiedLanguage[L <: NonGenerifiedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends StaticallyTypedLanguage[L, O, F, C]
trait GenerifiedLanguage[L <: GenerifiedLanguage[L, O, F, C], O <: Output[O, L, F, C], F <: Failure[F, L, O, C], C]
extends StaticallyTypedLanguage[L, O, F, C]
package bonk
trait Output[L <: Language[L]] {}
trait Output[O <: Output[O, L, F, C], L <: Language[L, O, F, C], F <: Failure[F, L, O, C], C]
trait Error[L <: Language[L]] {}
trait Failure[F <: Failure[F, L, O, C], L <: Language[L, O, F, C], O <: Output[O, L, F, C], C] {
type P <: Position
}
trait Position
package bonk
trait ScriptConstructor[C, L <: Language[L, _, F, C], F <: Failure[F, L, _, C], S] {
def construct(s: S): Either[F, C]
}
package bonk
trait ScriptContainer[L <: Language[L], S] {
def as[T](s: S): Option[T] = {
s match {
case t: T => Some(t)
case _ => None
}
}
}
package bonk
trait TypeDefinition[L <: TypedLanguage[L]] {}
trait TypeDefinition[L <: TypedLanguage[L, _, _, _]] {}
package bonk
trait TypeTransformer[L <: TypedLanguage[L], D <: TypeDefinition[L], T] {
trait TypeTransformer[L <: TypedLanguage[L, _, _, _], D <: TypeDefinition[L], T] {
def transform(t: D): T
......
......@@ -2,6 +2,6 @@ import shapeless.Generic
package object bonk {
type LanguageDefinition[L <: Language[L]] = Generic[Language[L]]
type LanguageDefinition[L <: Language[L, _, _, _]] = Generic[L]
}
......@@ -4,60 +4,41 @@ import bonk._
import scala.meta._
class Scala private() extends GenerifiedLanguage[Scala] {
class Scala private () extends GenerifiedLanguage[Scala, ScalaOutput, ScalaFailure, Stat] {
override type Definition = ScalaTypeDefinition
type Scope = Either[ScalaTypeDefinition, ScalaValueDefinition[_]]
def expression(s: String): Either[Parsed.Error, Stat] = {
s.parse[Stat].toEither
}
def evaluate[E](value: Stat, e: E)(implicit environmentDef: Environment[Scala, E]): Either[Error[Scala], Output[Scala]] = {
val map = environmentDef.toUsefulMap[String, Scope](e)
??? // Todo make scoping and evaluating
}
/** Gets a value from a specific entry point */
override def get[T, S, P, E](script: S, entry: P, environment: E)(
override def get[T, P, E](script: ConstructedScript, entry: P, environment: E)(
implicit typeTransformer: Transformer[T],
containerDef: ScriptContainer[Scala, S],
entryPointDef: EntryPoint[Scala, P],
entryPointDef: EntryPoint[ConstructedScript, Scala, P],
environmentDef: Environment[Scala, E]
): T = ???
/** Evaluates the type of a script stub */
override def typeOf[S, E](script: S, environment: E)(
implicit containerDef: ScriptContainer[Scala, S],
environmentDef: Environment[Scala, E]
): Scala.Definition = ???
override def typeOf[E](script: ConstructedScript, environment: E)(
implicit environmentDef: Environment[Scala, E]
): Definition = ???
/** Runs a script's entry point */
override def run[S, P, E](script: S, entry: P, env: E)(
implicit containerDef: ScriptContainer[Scala, S],
entryPointDef: EntryPoint[Scala, P],
override def run[P, E](script: ConstructedScript, entry: P, env: E)(
entryPointDef: EntryPoint[ConstructedScript, Scala, P],
environmentDef: Environment[Scala, E]
): Either[Error[Scala], Output[Scala]] =
???
): Either[ScalaFailure, ScalaOutput] = ???
/** Runs the script from start to finish, giving some sort of output */
override def run[S, E](script: S, env: E)(
implicit containerDef: ScriptContainer[Scala, S],
/** Verifies whether the script is valid in the language and returns a post-validated script */
override def verify[S, E](script: S, env: E)(
implicit containerDef: ScriptConstructor[ConstructedScript, Scala, ScalaFailure, S],
environmentDef: Environment[Scala, E]
): Either[Error[Scala], Output[Scala]] = {
containerDef.as[String](script) match {
case Some(str) =>
expression(str) match {
case Right(expr) =>
evaluate(expr, env)
case Left(error) => Left(new Error[Scala] {}) // TODO fix error and success types
}
case None =>
Left(new Error[Scala] {})
}
): Either[ScalaFailure, ConstructedScript] = {
containerDef.construct(script)
}
/** Runs the script from start to finish, giving some sort of output */
override def run[E](script: ConstructedScript, env: E)(
environmentDef: Environment[Scala, E]
): Either[ScalaFailure, ScalaOutput] = ???
}
object Scala extends Scala()
package bonk.sc
import bonk.{Failure, Output}
import scala.meta.Stat
class ScalaOutput extends Output[ScalaOutput, Scala, ScalaFailure, Stat] {}
class ScalaFailure extends Failure[ScalaFailure, Scala, ScalaOutput, Stat] {}
package bonk.sc
import bonk.ScriptContainer
case class ScalaScript(source: String) extends ScriptContainer[Scala, String]
package bonk.sc
import bonk.TypeTransformer
case class ScalaValueDefinition[T](name: String, v: T)(implicit transformer: TypeTransformer[Scala, ScalaTypeDefinition, T])
case class ScalaValueDefinition[T](name: String, v: T)(
implicit transformer: TypeTransformer[Scala, ScalaTypeDefinition, T]
)
package bonk
import scala.meta._
package object sc {
implicit def stringScalaScriptConstructor: ScriptConstructor[Stat, Scala, ScalaFailure, String] =
new ScriptConstructor[Stat, Scala, ScalaFailure, String] {
override def construct(s: String): Either[ScalaFailure, Stat] =
s.parse[Stat].toEither.fold(_ => Left(new ScalaFailure), Right(_))
}
implicit def mapScalaEnvironment[K, V]: Environment[Scala, Map[K, V]] = new Environment[Scala, Map[K, V]] {
override def toUsefulMap[K2, V2](e: Map[K, V]): Map[K2, V2] = Map() // FIXME: This is invalid and bad
}
}
package bonk
import bonk.sc.Scala
import org.scalatest.WordSpec
import scala.meta._
class ScalaSpec extends WordSpec {
"Scala" when {
"verifying a valid script" should {
"produce an error on an invalid script" in {
assert(Scala.verify("3 object (", Map()).isLeft)
}
"produce a statement on a valid script" in {
assertResult("3 + 5".parse[Stat].get.structure)(Scala.verify("3 + 5", Map()).right.get.structure)
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment