From f1a5fa4cc815be65db9950b91a6107833639e832 Mon Sep 17 00:00:00 2001 From: Oliver Kennedy Date: Thu, 13 Jul 2023 13:18:35 -0400 Subject: [PATCH] Simplistic type hierarchy for ASTs --- .../src/com/astraldb/spec/ASTDefinition.scala | 15 +++++ .../src/com/astraldb/spec/Definition.scala | 19 +++++-- .../compiler/src/com/astraldb/spec/Node.scala | 12 +++- .../compiler/src/com/astraldb/spec/Type.scala | 12 +++- .../com/astraldb/typecheck/Typecheck.scala | 55 ++++++++++++------- 5 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 astral/compiler/src/com/astraldb/spec/ASTDefinition.scala diff --git a/astral/compiler/src/com/astraldb/spec/ASTDefinition.scala b/astral/compiler/src/com/astraldb/spec/ASTDefinition.scala new file mode 100644 index 0000000..fb63014 --- /dev/null +++ b/astral/compiler/src/com/astraldb/spec/ASTDefinition.scala @@ -0,0 +1,15 @@ +package com.astraldb.spec + +case class ASTDefinition( + family: Type.AST, + nodes: Set[Node] +) +{ + val subtypes:Map[Type.ASTSubtype, Set[Node]] = + nodes.flatMap { node => + node.supertypes.map { st => + st -> node + } + } + .groupMap { _._1 } { _._2 } +} \ No newline at end of file diff --git a/astral/compiler/src/com/astraldb/spec/Definition.scala b/astral/compiler/src/com/astraldb/spec/Definition.scala index 31368c9..7621e9b 100755 --- a/astral/compiler/src/com/astraldb/spec/Definition.scala +++ b/astral/compiler/src/com/astraldb/spec/Definition.scala @@ -4,10 +4,14 @@ import scala.collection.mutable import com.astraldb.expression._ case class Definition( - nodes:Map[String, Seq[Node]], + asts: Map[String, ASTDefinition], rules:Seq[Rule], globals: Map[String, Type], ) { + + def nodes = + asts.mapValues { _.nodes } + override def toString = s"""/////// ASTs ////// |${nodes.map { case (family, nodeTypes) => s"Ast(${family})(\n ${nodeTypes.mkString(",\n ")}\n)" }.mkString("\n\n")} @@ -15,6 +19,7 @@ case class Definition( |/////// Rules ///// |${rules.mkString("\n\n")} """.stripMargin + val familyOfNode: Map[String, String] = nodes.flatMap { case (family, elements) => elements.map { _.name -> family } } .toMap @@ -32,7 +37,12 @@ class HardcodedDefinition { lazy val definition: Definition = Definition( - nodes = nodes.mapValues { _.toSeq }.toMap, + asts = nodes.map { case (f, n) => + val family = Type.AST(f) + f -> ASTDefinition( + family = family, + nodes = n.map { _.copy(family = family) }.toSet + )}.toMap, rules = rules.toSeq, globals = globals.toMap ) @@ -43,13 +53,12 @@ class HardcodedDefinition import FieldConversions._ - - def Ast(label: String)(newNodes: Node*): Unit = + def Ast(label: String)(newNodes: => Node*): Unit = nodes.getOrElseUpdate(label, mutable.Buffer.empty) .appendAll(newNodes) def Node(label: String)(fields: (String, Type)*): Node = - com.astraldb.spec.Node(label, fields) + com.astraldb.spec.Node(label, fields, supertypes = Set.empty, family = null) def Rule(label: String, family: String)(pattern: Match)(replacement: Expression): Unit = rules.append( diff --git a/astral/compiler/src/com/astraldb/spec/Node.scala b/astral/compiler/src/com/astraldb/spec/Node.scala index fc32da3..1902e3a 100755 --- a/astral/compiler/src/com/astraldb/spec/Node.scala +++ b/astral/compiler/src/com/astraldb/spec/Node.scala @@ -1,6 +1,11 @@ package com.astraldb.spec; -case class Node(val name:String, val fields:Seq[Field]) +case class Node( + val name: String, + val fields: Seq[Field], + val family: Type.AST, + val supertypes: Set[Type.ASTSubtype] +) { def renderName = name+"Node" def enumName = "JITD_NODE_"+name @@ -9,4 +14,9 @@ case class Node(val name:String, val fields:Seq[Field]) override def toString = name + "(" + fields.map { _.toString }.mkString(", ") + ")" + def withSupertypes(supertypes: String*): Node = + copy(supertypes = this.supertypes ++ supertypes.map { Type.ASTSubtype(_) }) + + def allSupertypes: Set[Type.ASTType] = supertypes ++ Set(family) + } \ No newline at end of file diff --git a/astral/compiler/src/com/astraldb/spec/Type.scala b/astral/compiler/src/com/astraldb/spec/Type.scala index b3f8c49..ed645e1 100755 --- a/astral/compiler/src/com/astraldb/spec/Type.scala +++ b/astral/compiler/src/com/astraldb/spec/Type.scala @@ -19,12 +19,20 @@ object Type override def toString: String = s"Native[${name}]" def scalaType: String = name } - case class AST(family: String) extends Type + + sealed trait ASTType extends Type + + case class AST(family: String) extends ASTType { override def toString: String = s"Ast[${family}]" def scalaType: String = family } - case class Node(nodeType: String) extends Type + case class ASTSubtype(typeName: String) extends ASTType + { + override def toString: String = s"ASTSubtype[${typeName}]" + def scalaType: String = typeName + } + case class Node(nodeType: String) extends ASTType { override def toString: String = s"Node[${nodeType}]" def scalaType: String = nodeType diff --git a/astral/compiler/src/com/astraldb/typecheck/Typecheck.scala b/astral/compiler/src/com/astraldb/typecheck/Typecheck.scala index 7c294e5..be295a4 100644 --- a/astral/compiler/src/com/astraldb/typecheck/Typecheck.scala +++ b/astral/compiler/src/com/astraldb/typecheck/Typecheck.scala @@ -12,10 +12,8 @@ object Typecheck { (source, target) match { case (a, b) if a == b => true - case (Type.Node(label), Type.AST(family)) => - schema.nodes.get(family) - .map { _.exists { _.name == label } } - .getOrElse { false } + case (Type.Node(label), o:Type.ASTType) => + schema.nodesByName(label).allSupertypes contains o case (Type.Union(elems), a) => elems.forall { escalatesTo(_, a, schema) } case (a, Type.Union(elems)) => @@ -30,14 +28,16 @@ object Typecheck { if(a == b){ return a } (a, b) match { - case (Type.Node(label), Type.AST(family)) if - schema.nodes.get(family) - .map { _.exists { _.name == label } } - .getOrElse { false } => a - case (Type.AST(family), Type.Node(label)) if - schema.nodes.get(family) - .map { _.exists { _.name == label } } - .getOrElse { false } => b + case (n@Type.Node(label), o:Type.ASTType) if + schema.nodesByName(label) + .allSupertypes contains o => n + case (o:Type.ASTType, n@Type.Node(label)) if + schema.nodesByName(label) + .allSupertypes contains o => n + case (n:Type.ASTSubtype, a:Type.AST) if + schema.asts(a.family).subtypes contains n => n + case (a:Type.AST, n:Type.ASTSubtype) if + schema.asts(a.family).subtypes contains n => n case (Type.Union(elems), _) if elems contains b => a case (_, Type.Union(elems)) if elems contains a => b case (_, Type.Any) => a @@ -53,14 +53,29 @@ object Typecheck { if(a == b){ return a } (a, b) match { - case (Type.Node(label), Type.AST(family)) if - schema.nodes.get(family) - .map { _.exists { _.name == label } } - .getOrElse { false } => b - case (Type.AST(family), Type.Node(label)) if - schema.nodes.get(family) - .map { _.exists { _.name == label } } - .getOrElse { false } => a + case (n:Type.Node, o:Type.ASTType) if + schema.nodesByName(n.nodeType) + .allSupertypes contains o => o + case (o:Type.ASTType, n@Type.Node(label)) if + schema.nodesByName(label) + .allSupertypes contains o => o + case (n:Type.ASTSubtype, o:Type.AST) if + schema.asts(o.family).subtypes contains n => o + case (o:Type.AST, n:Type.ASTSubtype) if + schema.asts(o.family).subtypes contains n => o + case (a:Type.Node, b:Type.Node) => + val sharedSuperTypes = + schema.nodesByName(a.nodeType).allSupertypes & + schema.nodesByName(b.nodeType).allSupertypes + if(sharedSuperTypes.isEmpty){ + assert(false, s"Node types $a and $b have nothing in common") + } else { + sharedSuperTypes.find { _.isInstanceOf[Type.ASTSubtype] } + .orElse { sharedSuperTypes.find { _.isInstanceOf[Type.AST] }} + .getOrElse { + assert(false, "A node can't inherit from another node") + } + } case (Type.Union(elems), _) if elems contains b => a case (_, Type.Union(elems)) if elems contains a => b case (_, Type.Any) => Type.Any