Initial stab, borrowing stuff from the JITD compiler
This commit is contained in:
commit
16d51b71a5
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.bloop
|
||||
.metals
|
||||
/out
|
9
astral/src/com/astraldb/Astral.scala
Normal file
9
astral/src/com/astraldb/Astral.scala
Normal file
|
@ -0,0 +1,9 @@
|
|||
package com.astraldb
|
||||
|
||||
object Astral
|
||||
{
|
||||
def main(args: Array[String]): Unit =
|
||||
{
|
||||
println("Hello!")
|
||||
}
|
||||
}
|
32
astral/src/com/astraldb/ast/AST.scala
Normal file
32
astral/src/com/astraldb/ast/AST.scala
Normal file
|
@ -0,0 +1,32 @@
|
|||
package com.astraldb.ast
|
||||
|
||||
import com.astraldb.spec.Constant
|
||||
|
||||
sealed trait AST
|
||||
{
|
||||
def children: Seq[AST]
|
||||
|
||||
def find[T](f: AST => Option[T]): Option[T] =
|
||||
{
|
||||
f(this).orElse {
|
||||
children.foldLeft(None:Option[T]){ (ret, child) =>
|
||||
if(ret.isEmpty) { child.find(f) }
|
||||
else { ret }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class AList(elements: Seq[AST]) extends AST
|
||||
{
|
||||
def children: Seq[AST] = elements
|
||||
}
|
||||
case class ANode(label: String, elements: Seq[AST]) extends AST
|
||||
{
|
||||
def children: Seq[AST] = elements
|
||||
}
|
||||
case class Leaf(value: Constant) extends AST
|
||||
{
|
||||
def children: Seq[AST] = Seq.empty
|
||||
}
|
||||
|
10
astral/src/com/astraldb/spec/Definition.scala
Executable file
10
astral/src/com/astraldb/spec/Definition.scala
Executable file
|
@ -0,0 +1,10 @@
|
|||
package com.astraldb.spec;
|
||||
|
||||
import com.astraldb.typecheck._
|
||||
|
||||
case class Definition(
|
||||
nodes:Seq[Node],
|
||||
// rules:Seq[Rule]
|
||||
) {
|
||||
}
|
||||
|
178
astral/src/com/astraldb/spec/Expression.scala
Executable file
178
astral/src/com/astraldb/spec/Expression.scala
Executable file
|
@ -0,0 +1,178 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
|
||||
object CmpTypes extends Enumeration {
|
||||
type T = Value
|
||||
val Eq, Neq, Lt, Lte, Gt, Gte = Value
|
||||
|
||||
def opString(op:T):String =
|
||||
{
|
||||
op match {
|
||||
case Eq => "=="
|
||||
case Neq => "!="
|
||||
case Lt => "<"
|
||||
case Lte => "<="
|
||||
case Gt => ">"
|
||||
case Gte => ">="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ArithTypes extends Enumeration {
|
||||
type T = Value
|
||||
val Add, Sub, Mul, Div, And, Or = Value
|
||||
|
||||
def opString(op:T):String =
|
||||
{
|
||||
op match {
|
||||
case Add => "+"
|
||||
case Sub => "-"
|
||||
case Mul => "*"
|
||||
case Div => "/"
|
||||
case And => "&&"
|
||||
case Or => "||"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class Expression
|
||||
{
|
||||
def eq(other:Expression) = Cmp(CmpTypes.Eq, this, other)
|
||||
def neq(other:Expression) = Cmp(CmpTypes.Neq, this, other)
|
||||
def lt(other:Expression) = Cmp(CmpTypes.Lt, this, other)
|
||||
def lte(other:Expression) = Cmp(CmpTypes.Lte, this, other)
|
||||
def gt(other:Expression) = Cmp(CmpTypes.Gt, this, other)
|
||||
def gte(other:Expression) = Cmp(CmpTypes.Gte, this, other)
|
||||
|
||||
def and(other:Expression) =
|
||||
{
|
||||
(this, other) match {
|
||||
case (BoolConstant(true), _) => other
|
||||
case (BoolConstant(false), _) => this
|
||||
case (_, BoolConstant(true)) => this
|
||||
case (_, BoolConstant(false)) => other
|
||||
case _ => Arith(ArithTypes.And, this, other)
|
||||
}
|
||||
}
|
||||
def or(other:Expression) =
|
||||
{
|
||||
(this, other) match {
|
||||
case (BoolConstant(false), _) => other
|
||||
case (BoolConstant(true), _) => this
|
||||
case (_, BoolConstant(false)) => this
|
||||
case (_, BoolConstant(true)) => other
|
||||
case _ => Arith(ArithTypes.Or, this, other)
|
||||
}
|
||||
}
|
||||
|
||||
def plus(other:Expression) = Arith(ArithTypes.Add, this, other)
|
||||
def minus(other:Expression) = Arith(ArithTypes.Sub, this, other)
|
||||
def times(other:Expression) = Arith(ArithTypes.Mul, this, other)
|
||||
def dividedBy(other:Expression) = Arith(ArithTypes.Div, this, other)
|
||||
|
||||
def get(field:String) = StructSubscript(this, field)
|
||||
def get(index:Int) = ArraySubscript(this, index)
|
||||
|
||||
def disassemble: Seq[Expression]
|
||||
def reassemble(in: Seq[Expression]): Expression
|
||||
def rebuild(fn:(Expression => Expression)): Expression =
|
||||
reassemble(disassemble.map { fn(_) })
|
||||
}
|
||||
|
||||
sealed abstract class Constant(val t:Type) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression]()
|
||||
def reassemble(in: Seq[Expression]): Expression = this
|
||||
}
|
||||
case class IntConstant(i:Integer) extends Constant(TInt())
|
||||
{
|
||||
override def toString:String = i.toString
|
||||
}
|
||||
case class FloatConstant(f:Double) extends Constant(TFloat())
|
||||
{
|
||||
override def toString:String = f.toString
|
||||
}
|
||||
case class BoolConstant(b:Boolean) extends Constant(TBool())
|
||||
{
|
||||
override def toString:String = if(b){ "true" } else { "false" }
|
||||
}
|
||||
|
||||
case class Var(v:String) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression]()
|
||||
def reassemble(in: Seq[Expression]): Expression = this
|
||||
override def toString = v.toString
|
||||
}
|
||||
case class Cmp(t: CmpTypes.T, lhs:Expression, rhs:Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](lhs, rhs)
|
||||
def reassemble(in: Seq[Expression]): Expression = Cmp(t, in(0), in(1))
|
||||
override def toString = s"($lhs) ${CmpTypes.opString(t)} ($rhs)"
|
||||
}
|
||||
case class Arith(t: ArithTypes.T, lhs:Expression, rhs:Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](lhs, rhs)
|
||||
def reassemble(in: Seq[Expression]): Expression = Arith(t, in(0), in(1))
|
||||
override def toString = s"($lhs) ${ArithTypes.opString(t)} ($rhs)"
|
||||
}
|
||||
case class FunctionalIfThenElse(condition:Expression, ifTrue:Expression, ifFalse:Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](condition, ifTrue, ifFalse)
|
||||
def reassemble(in: Seq[Expression]): Expression = FunctionalIfThenElse(in(0), in(1), in(2))
|
||||
override def toString = s"($condition) ? ($ifTrue) : ($ifFalse)"
|
||||
}
|
||||
case class ArraySubscript(target:Expression, index:Integer) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](target)
|
||||
def reassemble(in: Seq[Expression]): Expression = ArraySubscript(in(0), index)
|
||||
override def toString = s"$target[$index]"
|
||||
}
|
||||
case class StructSubscript(target:Expression, field:String) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](target)
|
||||
def reassemble(in: Seq[Expression]): Expression = StructSubscript(in(0), field)
|
||||
override def toString = s"$target.$field"
|
||||
}
|
||||
case class NodeSubscript(target:Expression, field:String) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](target)
|
||||
def reassemble(in: Seq[Expression]): Expression = NodeSubscript(in(0), field)
|
||||
override def toString = s"$target->$field"
|
||||
}
|
||||
case class NodeCast(nodeType: String,target:Expression, field:String) extends Expression
|
||||
{
|
||||
def disassemble = Seq[Expression](target)
|
||||
def reassemble(in: Seq[Expression]): Expression = NodeSubscript(in(0), field)
|
||||
override def toString = s"($nodeType *)$target->$field"
|
||||
}
|
||||
case class FunctionCall(name:String, args:Seq[Expression]) extends Expression
|
||||
{
|
||||
def disassemble = args
|
||||
def reassemble(in: Seq[Expression]): Expression = FunctionCall(name, in)
|
||||
override def toString = s"$name(${args.mkString(", ")})"
|
||||
}
|
||||
case class WrapNode(target: Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq(target)
|
||||
def reassemble(in: Seq[Expression]): Expression = WrapNode(in(0))
|
||||
override def toString = s"wrap ${target.toString}"
|
||||
}
|
||||
case class UnWrapHandle(target: Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq(target)
|
||||
def reassemble(in: Seq[Expression]): Expression = UnWrapHandle(in(0))
|
||||
override def toString = s"unwraphandleref ${target.toString}"
|
||||
}
|
||||
case class WrapNodeRef(target: Expression) extends Expression
|
||||
{
|
||||
def disassemble = Seq(target)
|
||||
def reassemble(in: Seq[Expression]): Expression = WrapNodeRef(in(0))
|
||||
override def toString = s"wrapnoderef ${target.toString}"
|
||||
}
|
||||
|
||||
case class MakeNode(nodeType: String, fields: Seq[Expression]) extends Expression
|
||||
{
|
||||
def disassemble = fields
|
||||
def reassemble(in: Seq[Expression]): Expression = MakeNode(nodeType, in)
|
||||
override def toString = s"allocate ${nodeType}(${fields.mkString(",")})"
|
||||
}
|
12
astral/src/com/astraldb/spec/Field.scala
Executable file
12
astral/src/com/astraldb/spec/Field.scala
Executable file
|
@ -0,0 +1,12 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
case class Field(name:String, t:Type)
|
||||
{
|
||||
override def toString = s"$name:${Type.toString(t)}"
|
||||
}
|
||||
|
||||
object FieldConversions {
|
||||
implicit def tuple2Field(t:(String, Type)): Field = Field(t._1, t._2)
|
||||
}
|
36
astral/src/com/astraldb/spec/FunctionDefinition.scala
Executable file
36
astral/src/com/astraldb/spec/FunctionDefinition.scala
Executable file
|
@ -0,0 +1,36 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
import com.astraldb.typecheck.FunctionSignature
|
||||
|
||||
object FunctionArgType extends Enumeration
|
||||
{
|
||||
type T = Value
|
||||
val Input, OutputRef, ConstInputRef = Value
|
||||
|
||||
def isConst(t: T): Boolean =
|
||||
t match {
|
||||
case ConstInputRef => true
|
||||
case Input | OutputRef => false
|
||||
}
|
||||
def isByRef(t: T): Boolean =
|
||||
t match {
|
||||
case ConstInputRef | OutputRef => true
|
||||
case Input => false
|
||||
}
|
||||
}
|
||||
|
||||
case class FunctionDefinition(
|
||||
name: String,
|
||||
ret: Option[Type],
|
||||
args: Seq[(String, Type, FunctionArgType.T)],
|
||||
body: Statement
|
||||
)
|
||||
{
|
||||
def signature =
|
||||
ret match {
|
||||
case Some(t) =>
|
||||
FunctionSignature(name, args.map { _._2 }, t)
|
||||
case None =>
|
||||
FunctionSignature(name, args.map { _._2 })
|
||||
}
|
||||
}
|
12
astral/src/com/astraldb/spec/Node.scala
Executable file
12
astral/src/com/astraldb/spec/Node.scala
Executable file
|
@ -0,0 +1,12 @@
|
|||
package com.astraldb.spec;
|
||||
|
||||
case class Node(val name:String, val fields:Seq[Field])
|
||||
{
|
||||
def renderName = name+"Node"
|
||||
def enumName = "JITD_NODE_"+name
|
||||
def scope = fields.map { f => f.name -> f.t }.toMap
|
||||
|
||||
override def toString =
|
||||
name + "(" + fields.map { _.toString }.mkString(", ") + ")"
|
||||
|
||||
}
|
223
astral/src/com/astraldb/spec/Pattern.scala
Normal file
223
astral/src/com/astraldb/spec/Pattern.scala
Normal file
|
@ -0,0 +1,223 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
import com.astraldb.ast._
|
||||
import com.astraldb.spec.MatchPattern.Scope
|
||||
|
||||
/**
|
||||
* A rule for matching patterns
|
||||
*/
|
||||
sealed trait MatchPattern
|
||||
{
|
||||
/**
|
||||
* Apply the match pattern to a specified abstract syntax tree
|
||||
*
|
||||
* Return the updated scope, or None if the pattern does not match
|
||||
*/
|
||||
def apply(node: AST, scope: Scope): Option[Scope]
|
||||
}
|
||||
object MatchPattern
|
||||
{
|
||||
type Scope = Map[String, AST]
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean logic over match patterns
|
||||
*/
|
||||
sealed trait BooleanMatchPattern extends MatchPattern
|
||||
|
||||
|
||||
/**
|
||||
* Boolean not.
|
||||
*/
|
||||
case class MatchNot(child: MatchPattern) extends BooleanMatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
child(node, scope) match {
|
||||
case None => Some(Map.empty)
|
||||
case Some(_) => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean and
|
||||
*/
|
||||
case class MatchAnd(children: Seq[MatchPattern]) extends BooleanMatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
children.foldLeft(Some(scope):Option[Scope]){ (scope, child) =>
|
||||
scope.flatMap { child(node, _) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean or
|
||||
*/
|
||||
case class MatchOr(children: Seq[MatchPattern]) extends BooleanMatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
{
|
||||
children.foldLeft(None: Option[Scope]) { (retScope, child) =>
|
||||
if(retScope.isDefined) { retScope }
|
||||
else { child(node, scope) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a node type.
|
||||
*
|
||||
* Return a match if the current AST element is a node of the labeled type,
|
||||
* and if all child matchers match as well.
|
||||
*/
|
||||
case class MatchNode(nodeLabel: String, children: Seq[MatchPattern]) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
node match {
|
||||
case ANode(label, elements) if label == nodeLabel
|
||||
&& elements.size == children.size =>
|
||||
children.zip(elements).foldLeft(Some(scope):Option[Scope]) { case (scope, (child, element)) =>
|
||||
scope.flatMap { child(element, _) }
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a node, solely based on its type
|
||||
*
|
||||
* Return a match if the current AST element is a node of the labeled type.
|
||||
* Ignore the children.
|
||||
*/
|
||||
case class MatchNodeType(nodeLabel: String) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
node match {
|
||||
case ANode(label, elements) if label == nodeLabel => Some(scope)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a node exactly.
|
||||
*
|
||||
* Return a match if the current AST element is identical to the AST provided
|
||||
* in the pattern
|
||||
*/
|
||||
case class MatchExact(pattern: AST) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
if(node == pattern){ Some(scope) }
|
||||
else { None }
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a descendant identified by a provided path of indices
|
||||
*
|
||||
* The provided path specifies descendants by their positions. The 0th
|
||||
* index is the first child of a Node or a List AST. The matcher returns
|
||||
* a match if the specified path exists, and if the provided pattern matches
|
||||
* the element at the specified position.
|
||||
*/
|
||||
case class MatchPath(path: Seq[Int], pattern: MatchPattern) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
path.foldLeft( Some(node):Option[AST] ) { (target, idx) =>
|
||||
target match {
|
||||
case Some(ANode(_, elements)) if elements.size > idx => Some(elements(idx))
|
||||
case Some(AList(elements)) if elements.size > idx => Some(elements(idx))
|
||||
case _ => None
|
||||
}
|
||||
} match {
|
||||
case None => None
|
||||
case Some(target) => pattern(target, scope)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match some descendent.
|
||||
*
|
||||
* The pattern is applied to all descendents of the current node. The matcher
|
||||
* returns the first match it finds.
|
||||
*/
|
||||
case class MatchRecursive(pattern: MatchPattern) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
node.find { pattern(_, scope) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the current node to the scope
|
||||
*
|
||||
* The current node is bound to the scope and the provided pattern is applied
|
||||
* as normal.
|
||||
*/
|
||||
case class MatchBind(symbol: String, pattern: MatchPattern) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
pattern(node, scope ++ Map(symbol -> node))
|
||||
}
|
||||
|
||||
/**
|
||||
* Always return a match
|
||||
*/
|
||||
case object MatchAny extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
Some(scope)
|
||||
}
|
||||
|
||||
/**
|
||||
* Never return a match
|
||||
*/
|
||||
case object MatchNone extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the current node against an element of the scope.
|
||||
*
|
||||
* Returns a match if the provided symbol exists in the scope and the current
|
||||
* node is exactly equal to the symbol's value in the scope.
|
||||
*/
|
||||
case class MatchLookup(symbol: String) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
scope.get(symbol).flatMap { scopeNode =>
|
||||
if(node == scopeNode){ Some(scope) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a matcher to an element of the scope
|
||||
*
|
||||
* Returns a match if the provided symbol exists in the scope and the provided
|
||||
* pattern exactly matches the symbol's value in the scope.
|
||||
*/
|
||||
case class MatchScope(symbol: String, pattern: MatchPattern) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
scope.get(symbol).flatMap { scopeNode =>
|
||||
pattern(scopeNode, scope)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a matcher to every element of a list
|
||||
*
|
||||
* Returns a match if the current element is a list and every element of the
|
||||
* list matches the provided pattern
|
||||
*/
|
||||
case class MatchList(pattern: MatchPattern) extends MatchPattern
|
||||
{
|
||||
def apply(node: AST, scope: Scope): Option[Scope] =
|
||||
node match {
|
||||
case AList(elements) =>
|
||||
elements.foldLeft( Some(scope): Option[Scope] ){ (scope, element) =>
|
||||
scope.flatMap { pattern(element, _) }
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
128
astral/src/com/astraldb/spec/Statement.scala
Executable file
128
astral/src/com/astraldb/spec/Statement.scala
Executable file
|
@ -0,0 +1,128 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
object StatementConversions {
|
||||
implicit def seq2block(s:Seq[Statement]): Block = Block(s)
|
||||
}
|
||||
|
||||
sealed abstract class Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement]
|
||||
def reassembleStatement(in: Seq[Statement]): Statement
|
||||
def rebuildStatement(fn: Statement => Statement): Statement =
|
||||
reassembleStatement(disasssembleStatement.map { fn(_) })
|
||||
def disasssembleExpression: Seq[Expression]
|
||||
def reassembleExpression(in: Seq[Expression]): Statement
|
||||
def rebuildExpression(fn: Expression => Expression): Statement =
|
||||
reassembleExpression(disasssembleExpression.map { fn(_) })
|
||||
.rebuildStatement { _.rebuildExpression(fn) }
|
||||
|
||||
def toString(prefix: String): String
|
||||
override def toString: String = toString("")
|
||||
|
||||
def blockSeq: Seq[Statement] = Seq(this)
|
||||
def ++(other: Statement): Block = Block(this.blockSeq ++ other.blockSeq)
|
||||
}
|
||||
|
||||
case class IfThenElse(condition:Expression, ifTrue:Statement, ifFalse:Statement = Block(Seq())) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq(ifTrue, ifFalse)
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = IfThenElse(condition, in(0), in(1))
|
||||
def disasssembleExpression: Seq[Expression] = Seq(condition)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = IfThenElse(in(0), ifTrue, ifFalse)
|
||||
def toString(prefix: String) = s"${prefix}if($condition)\n"+ifTrue.toString(prefix+" ")+s"\n${prefix}else\n"+ifFalse.toString(prefix+" ")
|
||||
}
|
||||
case class Declare(name:String, t:Option[Type], v:Expression) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq(v)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = Declare(name, t, in(0))
|
||||
def toString(prefix: String) = s"${prefix}var $name${t.map { x => ":"+Type.toString(x)}.getOrElse("")} = $v"
|
||||
}
|
||||
|
||||
case class Assign(name:String, v:Expression, atomic:Boolean = false) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq(v)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = Assign(name, in(0), atomic)
|
||||
def toString(prefix: String) = s"${prefix}$name = $v"
|
||||
}
|
||||
|
||||
case class ExtractNode(name:String, v:Expression, nodeHandlers: Seq[(String, Statement)], onFail: Statement) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq(onFail) ++ nodeHandlers.map { _._2 }
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = ExtractNode(name, v, nodeHandlers.zip(in.tail).map { case ((name, _), handler) => (name, handler) }, in(0))
|
||||
def disasssembleExpression: Seq[Expression] = Seq(v)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = ExtractNode(name, in(0), nodeHandlers, onFail)
|
||||
def toString(prefix: String) = s"${prefix}extract $v into $name { \n"+nodeHandlers.map {
|
||||
case (nodeType, onMatch) => s"${prefix} case $nodeType -> \n${onMatch.toString(prefix+" ")}\n"
|
||||
}.mkString+s"\n${prefix} else -> \n${onFail.toString(prefix+" ")}\n${prefix}}"
|
||||
}
|
||||
case class Block(statements:Seq[Statement]) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = statements
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = Block(in)
|
||||
def disasssembleExpression: Seq[Expression] = Seq()
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = this
|
||||
def toString(prefix: String) = (Seq(prefix+"{")++statements.map{ _.toString(prefix+" ") }++Seq(prefix+"}")).mkString("\n")
|
||||
override def blockSeq = statements
|
||||
}
|
||||
case class ForEach(loopvar:String, over:Expression, body:Statement) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq(body)
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = ForEach(loopvar, over, in(0))
|
||||
def disasssembleExpression: Seq[Expression] = Seq(over)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = ForEach(loopvar, in(0), body)
|
||||
def toString(prefix: String) = s"${prefix}for($loopvar in $over)\n${body.toString(prefix+" ")}"
|
||||
}
|
||||
case class Void(v:Expression) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq(v)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = Void(in(0))
|
||||
def toString(prefix: String) = s"${prefix}void $v"
|
||||
}
|
||||
case class Return(v:Expression) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq(v)
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = Return(in(0))
|
||||
def toString(prefix: String) = s"${prefix}return $v"
|
||||
}
|
||||
case class Error(msg:String) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq()
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = this
|
||||
def toString(prefix: String) = prefix+"error: "+msg
|
||||
}
|
||||
case class Comment(msg:String) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq()
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = this
|
||||
def toString(prefix: String) = prefix+"rem: "+msg
|
||||
}
|
||||
case class SetRemoveFunction(name:String,nodeType:String,v:Expression) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq()
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = this
|
||||
def toString(prefix: String) = prefix+"removing from set: "+name
|
||||
}
|
||||
case class SetAddFunction(name:String,nodeType:String,v:Expression) extends Statement
|
||||
{
|
||||
def disasssembleStatement: Seq[Statement] = Seq()
|
||||
def reassembleStatement(in: Seq[Statement]): Statement = this
|
||||
def disasssembleExpression: Seq[Expression] = Seq()
|
||||
def reassembleExpression(in: Seq[Expression]): Statement = this
|
||||
def toString(prefix: String) = s"${prefix} adding to set: $v"
|
||||
}
|
37
astral/src/com/astraldb/spec/Type.scala
Executable file
37
astral/src/com/astraldb/spec/Type.scala
Executable file
|
@ -0,0 +1,37 @@
|
|||
package com.astraldb.spec
|
||||
|
||||
sealed abstract class Type {
|
||||
def array = TArray(this)
|
||||
}
|
||||
|
||||
sealed abstract class PrimType extends Type
|
||||
|
||||
case class TKey() extends PrimType { override def toString = "key" }
|
||||
case class TRecord() extends Type
|
||||
|
||||
case class TInt() extends PrimType
|
||||
case class TFloat() extends PrimType
|
||||
case class TBool() extends PrimType
|
||||
case class TArray(t:Type) extends Type
|
||||
case class TStruct(fields:Seq[Field]) extends Type
|
||||
case class TNodeRef() extends Type
|
||||
case class TNode(t:String) extends Type
|
||||
case class TIterator() extends Type
|
||||
case class THandleRef() extends Type
|
||||
object Type
|
||||
{
|
||||
def toString(t: Type): String =
|
||||
t match {
|
||||
case TKey() => "key"
|
||||
case TRecord() => "record"
|
||||
case TInt() => "int"
|
||||
case TFloat() => "float"
|
||||
case TBool() => "bool"
|
||||
case TArray(nested) => s"array[${Type.toString(nested)}]"
|
||||
case TStruct(fields) => s"struct[${fields.map { _.toString }.mkString{", "}}]"
|
||||
case TNodeRef() => "noderef"
|
||||
case THandleRef() => "handleref"
|
||||
case TNode(t) => s"node[$t]"
|
||||
case TIterator() => "iterator"
|
||||
}
|
||||
}
|
33
astral/src/com/astraldb/typecheck/FunctionSignature.scala
Executable file
33
astral/src/com/astraldb/typecheck/FunctionSignature.scala
Executable file
|
@ -0,0 +1,33 @@
|
|||
package com.astraldb.typecheck
|
||||
|
||||
import com.astraldb.spec._
|
||||
|
||||
abstract class FunctionSignature()
|
||||
{
|
||||
def apply(args:Seq[Type]): Option[Type]
|
||||
def name: String
|
||||
}
|
||||
|
||||
class FunctionArgError(function:FunctionSignature, args:Seq[Type]) extends Exception
|
||||
{
|
||||
override def toString =
|
||||
function.toString + " <- (" + args.map { Type.toString(_) }.mkString(", ") + ")"
|
||||
}
|
||||
|
||||
class SimpleFunctionSignature(val name:String, args:Seq[Type], ret:Option[Type]) extends FunctionSignature
|
||||
{
|
||||
def apply(cmpArgs:Seq[Type]) =
|
||||
if(args == cmpArgs){ ret; }
|
||||
else { throw new FunctionArgError(this, cmpArgs) }
|
||||
override def toString: String =
|
||||
ret.getOrElse("void").toString+" "+name+"("+args.map { Type.toString(_) }.mkString(", ")+")"
|
||||
}
|
||||
|
||||
object FunctionSignature
|
||||
{
|
||||
def apply(name:String, args:Seq[Type], ret:Type) =
|
||||
new SimpleFunctionSignature(name, args, Some(ret))
|
||||
def apply(name:String, args:Seq[Type]) =
|
||||
new SimpleFunctionSignature(name, args, None)
|
||||
|
||||
}
|
295
astral/src/com/astraldb/typecheck/Typechecker.scala
Executable file
295
astral/src/com/astraldb/typecheck/Typechecker.scala
Executable file
|
@ -0,0 +1,295 @@
|
|||
package com.astraldb.typecheck
|
||||
|
||||
import com.astraldb.spec._
|
||||
|
||||
class TypeError(msg: String, ctx: Expression, scope:Map[String, Type]) extends Exception(msg+" in "+ctx.toString)
|
||||
{
|
||||
override def toString:String = {
|
||||
val scopeLen = scope.map { case (k, _ ) => k.length }.max
|
||||
"-----------------------\n"+msg + " in " + ctx + "\n\n"+"---- Current Scope ----\n"+
|
||||
scope.map { case (k, t) => " " + k.padTo(scopeLen, " ").mkString + " <- " + t }.mkString("\n")+
|
||||
"\n-----------------------"
|
||||
}
|
||||
|
||||
def rebind(newCtx:Expression) = new TypeError(msg, newCtx, scope)
|
||||
def rebind(newCtx:Statement) = new StatementError(msg, Seq(newCtx), scope)
|
||||
}
|
||||
|
||||
class StatementError(msg: String, ctx: Seq[Statement], scope:Map[String, Type]) extends Exception(msg+" in "+ctx(0).toString)
|
||||
{
|
||||
override def toString:String = {
|
||||
val scopeLen = scope.map { case (k, _ ) => k.length }.max
|
||||
"-----------------------\n"+msg + " in " + ctx.head + "\n\n"+"---- Current Scope ----\n"+
|
||||
scope.map { case (k, t) => " " + k.padTo(scopeLen, " ").mkString + " <- " + t }.mkString("\n")+
|
||||
"\n-----------------------"+
|
||||
(if(ctx.length > 1) { "\n -- in -- \n"+ctx.tail.map { _.toString }.mkString("\n -- in -- \n")} else { "" })
|
||||
}
|
||||
def trace(stmt: Statement) = new StatementError(msg, ctx :+ stmt, scope)
|
||||
}
|
||||
|
||||
|
||||
class Typechecker(functions: Map[String, FunctionSignature], nodeTypes: Map[String, Node]) {
|
||||
|
||||
def comparisonCompatible(t1:Type, t2:Type): Boolean =
|
||||
{
|
||||
|
||||
if(t1 == t2) { return true; }
|
||||
(t1, t2) match {
|
||||
case (TIterator(), _) => return comparisonCompatible(TRecord(), t2)
|
||||
case (_, TIterator()) => return comparisonCompatible(t1, TRecord())
|
||||
case (TKey(), TRecord()) => return true
|
||||
case (TRecord(), TKey()) => return true
|
||||
case _ => return false
|
||||
}
|
||||
}
|
||||
|
||||
def typeOf(e: Expression, scope: Map[String, Type]): Type =
|
||||
{
|
||||
val error = (msg:String) => throw new TypeError(msg, e, scope)
|
||||
val recur = (r:Expression) => try { typeOf(r, scope) } catch { case t: TypeError => throw t.rebind(e) }
|
||||
e match {
|
||||
case c:Constant => c.t
|
||||
case ArraySubscript(arr, _) => {
|
||||
recur(arr) match {
|
||||
case TArray(nested) => nested
|
||||
case _ => error("Subscript of Non-Array: "+arr)
|
||||
}
|
||||
}
|
||||
case StructSubscript(struct, subscript) => {
|
||||
recur(struct) match {
|
||||
case TStruct(fields) =>
|
||||
fields.find { _.name.equals(subscript) } match {
|
||||
case Some(field) => field.t
|
||||
case None => error("Invalid Struct Subscript: "+subscript)
|
||||
}
|
||||
case _ => error("Subscript of Non-Struct: "+struct)
|
||||
}
|
||||
}
|
||||
case NodeSubscript(node, subscript) => {
|
||||
recur(node) match {
|
||||
case TNode(nodeType) =>
|
||||
nodeTypes(nodeType).fields.find { _.name.equals(subscript) } match {
|
||||
case Some(field) => field.t
|
||||
case None => error("Invalid Node Subscript: "+subscript)
|
||||
}
|
||||
case _ => error("Subscript of Non-Node: "+node)
|
||||
}
|
||||
}
|
||||
case Cmp(op, a, b) => {
|
||||
if(comparisonCompatible(recur(a), recur(b))){
|
||||
return TBool()
|
||||
} else {
|
||||
error("Invalid Comparison")
|
||||
}
|
||||
}
|
||||
case Arith(_, a, b) => {
|
||||
(recur(a), recur(b)) match {
|
||||
case (TInt(), TInt()) => return TInt()
|
||||
case (TInt(), TFloat())
|
||||
| (TFloat(), TInt())
|
||||
| (TFloat(), TFloat()) => return TFloat()
|
||||
case _ => error("Invalid Arithmetic")
|
||||
}
|
||||
}
|
||||
case FunctionCall(name, args) => {
|
||||
try {
|
||||
functions.getOrElse(name, {
|
||||
error("Undefined function")
|
||||
})(args.map { recur }).getOrElse { error("Using return value from void function") }
|
||||
} catch {
|
||||
case e:FunctionArgError => error(e.toString)
|
||||
}
|
||||
}
|
||||
case FunctionalIfThenElse(c, t, e) => {
|
||||
(recur(c), recur(t), recur(e)) match {
|
||||
case (TBool(), a, b) if a == b => a
|
||||
case (TBool(), _, _) => error("Incompatible functional then-else clauses")
|
||||
case (_, _, _) => error("Non-Boolean if-then-else condition")
|
||||
}
|
||||
}
|
||||
case Var(name) => {
|
||||
scope.get(name) match {
|
||||
case Some(t) => t
|
||||
case None => error(s"Variable '$name' not in scope")
|
||||
}
|
||||
}
|
||||
case WrapNode(target) => {
|
||||
recur(target) match {
|
||||
case TNode(_) => TNodeRef()
|
||||
case _ => error("Can't wrap a non-node")
|
||||
}
|
||||
}
|
||||
case UnWrapHandle(target) => {
|
||||
recur(target) match {
|
||||
case THandleRef() => TNodeRef()
|
||||
//case TNodeRef() => TNodeRef()
|
||||
case _ => error("Can't unwrap a non-handle")
|
||||
}
|
||||
}
|
||||
case WrapNodeRef(target) => {
|
||||
recur(target) match {
|
||||
case TNodeRef() => THandleRef()
|
||||
case _ => error("Can't wrap a non-node-ref")
|
||||
}
|
||||
}
|
||||
case MakeNode(nodeType, fields) => {
|
||||
for( (field, expr) <- nodeTypes(nodeType).fields.zip(fields)){
|
||||
if(recur(expr) != field.t) {
|
||||
error("Invalid node constructor")
|
||||
}
|
||||
}
|
||||
TNode(nodeType)
|
||||
}
|
||||
case NodeCast(nodeType,node, subscript) => {
|
||||
recur(node) match {
|
||||
case TNode(nodeType) =>
|
||||
nodeTypes(nodeType).fields.find { _.name.equals(subscript) } match {
|
||||
case Some(field) => field.t
|
||||
case None => error("Invalid Node Subscript: "+subscript)
|
||||
}
|
||||
case _ => error("Subscript of Non-Node: "+node)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def check(stmt: Statement, scope: Map[String, Type], returnType:Option[Type]): Map[String, Type] =
|
||||
{
|
||||
val exprType = (e:Expression) => try { typeOf(e, scope) } catch { case e:TypeError => throw e.rebind(stmt) }
|
||||
val error = (msg:String) => throw new StatementError(msg, Seq(stmt), scope)
|
||||
val recur = (rstmt: Statement, rscope: Map[String, Type]) => try { check(rstmt, rscope, returnType) } catch { case e:StatementError => throw e.trace(stmt) }
|
||||
stmt match {
|
||||
case Block(elems) => {
|
||||
elems.foldLeft(scope) { (currScope, currStmt) =>
|
||||
recur(currStmt, currScope)
|
||||
}
|
||||
scope
|
||||
}
|
||||
case Assign(tgt, expr, false) => {
|
||||
val tgtType = scope.getOrElse(tgt, { error("Assignment to undefined variable: "+tgt) })
|
||||
|
||||
if(exprType(expr) != tgtType){
|
||||
error("Assignment to "+tgt+" of incorrect type")
|
||||
}
|
||||
if(tgtType == TNodeRef())
|
||||
{
|
||||
error("Non-atomic assignment to a NodeRef")
|
||||
}
|
||||
if(tgtType == THandleRef()){
|
||||
error("Non-atomic assignment to a NodeHandle")
|
||||
}
|
||||
scope
|
||||
}
|
||||
|
||||
case Assign(tgt,expr,true) => {
|
||||
val tgtType = scope.getOrElse(tgt, { error("Assignment to undefined variable: "+tgt) })
|
||||
if(tgtType != THandleRef() || exprType(expr)!= TNodeRef())
|
||||
{
|
||||
error("Atomic assignment into wrong types ")
|
||||
}
|
||||
scope
|
||||
}
|
||||
|
||||
case Declare(tgt, tOption, expr) => {
|
||||
if(scope contains tgt) {
|
||||
error("Overriding existing variable")
|
||||
}
|
||||
val tRet = tOption match {
|
||||
case Some(t) => if(t != exprType(expr)){
|
||||
error("Declaring expression of incorrect type")
|
||||
} else { t }
|
||||
case None => exprType(expr)
|
||||
}
|
||||
scope + (tgt -> tRet)
|
||||
}
|
||||
|
||||
case ExtractNode(name, expr, matchers, onFail) => {
|
||||
if(scope contains name) {
|
||||
error("Overriding existing variable")
|
||||
}
|
||||
val exp = exprType(expr)
|
||||
if(exp != THandleRef()) {
|
||||
error("Doesn't evaluate to an (extractable) node/Handle reference")
|
||||
}
|
||||
for( (nodeType, handler) <- matchers ){
|
||||
if(!(nodeTypes contains nodeType)) {
|
||||
error(s"Invalid Node Type: '$nodeType'")
|
||||
}
|
||||
recur(handler, scope + (name -> TNode(nodeType)))
|
||||
}
|
||||
recur(onFail, scope)
|
||||
scope
|
||||
}
|
||||
|
||||
case Return(expr) => {
|
||||
if(exprType(expr) != returnType.getOrElse {
|
||||
error(s"Invalid Return Type (Found: ${exprType(expr)}; Void Function)")
|
||||
}) {
|
||||
error(s"Invalid Return Type (Found: ${exprType(expr)}; Expected: $returnType)")
|
||||
}
|
||||
scope
|
||||
}
|
||||
case Void(expr @ FunctionCall(name, args)) => {
|
||||
try {
|
||||
functions.getOrElse(name, {
|
||||
error("Undefined function")
|
||||
})(args.map { exprType(_) })
|
||||
} catch {
|
||||
case e:FunctionArgError => error(e.toString)
|
||||
}
|
||||
scope
|
||||
}
|
||||
case Void(expr) => {
|
||||
exprType(expr)
|
||||
scope
|
||||
}
|
||||
case ForEach(loopvar, expr, body) => {
|
||||
if(scope contains loopvar) {
|
||||
error("Overriding existing variable")
|
||||
}
|
||||
exprType(expr) match {
|
||||
case TArray(nested) =>
|
||||
recur(body, scope + (loopvar -> nested))
|
||||
case _ =>
|
||||
error("Invalid loop target")
|
||||
}
|
||||
scope
|
||||
}
|
||||
case IfThenElse(c, t, e) => {
|
||||
if(exprType(c) != TBool()){
|
||||
error("Invalid if-then-else condition")
|
||||
}
|
||||
recur(t, scope)
|
||||
recur(e, scope)
|
||||
scope
|
||||
}
|
||||
case Error(_) => scope
|
||||
case Comment(_) => scope
|
||||
case SetRemoveFunction(_,_,_) => scope
|
||||
case SetAddFunction(_,_,_) => scope
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def check(globals: Map[String,Type])(fn: FunctionDefinition): FunctionDefinition =
|
||||
{
|
||||
check(fn.body, globals ++ fn.args.map { case (name, t, _) => name -> t }.toMap, fn.ret)
|
||||
return fn
|
||||
}
|
||||
def check(globals: (String,Type)*)(fn: FunctionDefinition): FunctionDefinition =
|
||||
check(globals.toMap)(fn)
|
||||
|
||||
def check(fn: FunctionDefinition): FunctionDefinition =
|
||||
{
|
||||
check(fn.body, fn.args.map { case (name, t, _) => name -> t }.toMap, fn.ret)
|
||||
return fn
|
||||
}
|
||||
|
||||
def withFunctions(newFuncs: Map[String, FunctionSignature]): Typechecker =
|
||||
new Typechecker(functions ++ newFuncs, nodeTypes)
|
||||
|
||||
def withFunctions(newFuncs: (String, FunctionSignature)*): Typechecker =
|
||||
withFunctions(newFuncs.toMap)
|
||||
|
||||
}
|
30
build.sc
Normal file
30
build.sc
Normal file
|
@ -0,0 +1,30 @@
|
|||
import mill._
|
||||
import mill.scalalib._
|
||||
import mill.scalalib.publish._
|
||||
|
||||
object astral extends ScalaModule with PublishModule {
|
||||
val VERSION = "0.0.1-SNAPSHOT"
|
||||
|
||||
def scalaVersion = "3.2.1"
|
||||
|
||||
def mainClass = Some("com.astraldb.Astral")
|
||||
|
||||
/*************************************************
|
||||
*** Backend Dependencies
|
||||
*************************************************/
|
||||
// def ivyDeps = Agg(
|
||||
// )
|
||||
|
||||
def publishVersion = VERSION
|
||||
override def pomSettings = PomSettings(
|
||||
description = "The Astral Compiler",
|
||||
organization = "com.astraldb",
|
||||
url = "http://astraldb.com",
|
||||
licenses = Seq(License.`Apache-2.0`),
|
||||
versionControl = VersionControl.github("UBOdin", "astral"),
|
||||
developers = Seq(
|
||||
Developer("okennedy", "Oliver Kennedy", "https://odin.cse.buffalo.edu"),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in a new issue