diff --git a/build.sbt b/build.sbt index cd758399..bc5d558a 100644 --- a/build.sbt +++ b/build.sbt @@ -73,7 +73,7 @@ val commonSettings = Seq( Nil }, Test / scalacOptions += "-Wconf:cat=deprecation:silent,msg=Specify both message and version:silent", - scalacOptions ++= Seq("-release", "11"), + scalacOptions += "-release:11", mimaFailOnNoPrevious := false, resolvers += "Sonatype S01 snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots", ) @@ -117,7 +117,7 @@ lazy val parser = module("parser") "io.circe" %% "circe-generic" % "0.14.10" % Test, "io.circe" %% "circe-parser" % "0.14.10" % Test, "co.fs2" %% "fs2-io" % "3.11.0" % Test, - "org.polyvariant.treesitter4s" %% "core" % "0.3-e777c6d-SNAPSHOT", + "org.polyvariant.treesitter4s" %% "core" % "0.3-9edd0ef-SNAPSHOT", ) ) .dependsOn( @@ -125,6 +125,18 @@ lazy val parser = module("parser") source % "test->test;compile->compile", ) +lazy val parsergen = module("parser-gen") + .settings( + libraryDependencies ++= Seq( + "dev.optics" %% "monocle-core" % "3.3.0", + "com.disneystreaming.smithy4s" %% "smithy4s-json" % smithy4sVersion.value, + ("org.scalameta" %% "scalameta" % "4.11.0").cross(CrossVersion.for3Use2_13), + "org.polyvariant.treesitter4s" %% "core" % "0.3-9edd0ef-SNAPSHOT", + ), + scalacOptions -= "-release:11", + ) + .enablePlugins(Smithy4sCodegenPlugin) + // Formatter for the SmithyQL language constructs lazy val formatter = module("formatter") .settings( @@ -250,6 +262,7 @@ lazy val root = project core, examples, parser, + parsergen, formatter, languageSupport, lsp, diff --git a/modules/parser-gen/src/main/scala/playground/parsergen/ParserGen.scala b/modules/parser-gen/src/main/scala/playground/parsergen/ParserGen.scala new file mode 100644 index 00000000..27440cc8 --- /dev/null +++ b/modules/parser-gen/src/main/scala/playground/parsergen/ParserGen.scala @@ -0,0 +1,208 @@ +package playground.parsergen + +import cats.syntax.all.* +import monocle.syntax.all.* +import org.polyvariant.treesitter4s.Node +import smithy4s.Blob +import smithy4s.Document +import smithy4s.json.Json +import treesittersmithy.NodeType +import treesittersmithy.NodeTypes +import treesittersmithy.TypeName +import util.chaining.* + +import java.nio.file.Files +import java.nio.file.Paths +import scala.jdk.CollectionConverters.* +import scala.meta.Dialect + +val debug = false + +def debugDump(s: String): String = + if debug then s + else + "" + +extension (tn: TypeName) { + def render: String = tn.value.smartCapitalize.ident + def asEnumCase: TypeName = TypeName(tn.value + "Case") +} + +extension (tpe: NodeType) { + + def render: String = + if tpe.subtypes.nonEmpty then renderAdt(tpe) + else + renderClass(tpe) + +} + +def renderAdt(tpe: NodeType) = { + val name = tpe.tpe.render + + val enumCases = tpe.subtypes.map { nodeType => + show"""case ${nodeType.tpe.asEnumCase.render}(value: ${nodeType.tpe.render})""" + } + + show"""// Generated code! Do not modify by hand. + |package playground.generated.nodes + | + |import ${classOf[Node].getName()} + | + |enum $name { + |${enumCases.mkString_("\n").indentTrim(2)} + | + | def asNode: Node = this match { + |${tpe + .subtypes + .map { nodeType => + show"""case ${nodeType.tpe.asEnumCase.render}(value) => value.node""" + } + .mkString_("\n") + .indentTrim(4)} + | } + |} + | + |object $name { + | def apply(node: Node): $name = node match { + |${tpe + .subtypes + .map { nodeType => + show"""case node @ ${nodeType + .tpe + .render}() => ${nodeType.tpe.asEnumCase.render}(${nodeType.tpe.render}(node))""" + } + .mkString_("\n") + .indentTrim(4)} + | } + |} + | + |/* + |${debugDump(Json.writeDocumentAsPrettyString(Document.encode(tpe)).trimLines)} + |*/ + |""".stripMargin +} + +def renderClass(tpe: NodeType) = { + val name = tpe.tpe.render + + val fieldGetters = tpe + .fields + .toList + .map { (k, fieldType) => + val singleFieldType = fieldType + .types + .map(tpe => show"${tpe.tpe.render}") + .reduceLeft(_ + " | " + _) + + val fieldTypeAnnotation = singleFieldType.pipe { + case s if fieldType.multiple => show"List[$s]" + case s => s + } + + val allFields = show"""node.fields(${k.value.literal})""" + + val cases = fieldType.types.map { typeInfo => + show"""case node @ ${typeInfo.tpe.render}() => ${typeInfo.tpe.render}(node)""" + } + val fieldValue = + if fieldType.multiple then show"""$allFields.toList.collect { + |${cases.mkString("\n").indentTrim(2)} + |}""".stripMargin + else + show"""${singleFieldType}($allFields.head)""" + + show"""def ${k.value}: ${fieldTypeAnnotation} = $fieldValue""" + } + + show"""// Generated code! Do not modify by hand. + |package playground.generated.nodes + | + |import ${classOf[Node].getName()} + | + |case class $name /* private */(node: Node) extends Node { + + |${fieldGetters + .mkString_("\n") + .indentTrim(2)} + | + | export node.* + |} + | + |object $name { + | def unapply(node: Node): scala.Boolean = node.tpe == ${tpe.tpe.value.literal} + |} + | + |/* + |${debugDump(Json.writeDocumentAsPrettyString(Document.encode(tpe)).trimLines)} + |*/ + |""".stripMargin + +} + +@main def parserGen = { + val types = + Json + .read[NodeTypes]( + Blob(Files.readString(Paths.get("tree-sitter-smithyql/src/node-types.json"))) + ) + .toTry + .get + .value + + val base = Paths.get(s"modules/parser/src/main/scala/playground/generated/nodes") + + Files.walk(base).iterator().asScala.filter(Files.isRegularFile(_)).foreach(Files.delete) + + types + .filter(_.named) + .map(_.focus(_.fields.each.types).modify(_.filter(_.named))) + .fproduct( + _.render + ) + .foreach { (tpe, code) => + Files.writeString( + base.resolve(s"${tpe.tpe.render}.scala"), + code, + ) + } +} + +extension (s: String) { + + def indentTrim(n: Int): String = s + .linesIterator + .map { + case line if line.nonEmpty => " " * n + line + case line => line + } + .mkString("\n") + + def trimLines: String = s.linesIterator.map(_.stripTrailing()).mkString("\n") + + def literal: String = scala.meta.Lit.String(s).printSyntaxFor(scala.meta.dialects.Scala3) + + def ident: String = { + // etc. + val reserved = Set("List", "String", "Boolean", "Null") + if reserved(s) then s + "_" + else + scala.meta.Name(s).printSyntaxFor(scala.meta.dialects.Scala3) + } + + def smartCapitalize: String = { + val (before, after) = s.span(!_.isLetter) + before + after.capitalize + } + +} + +extension [A](l: List[A]) { + + def requireOnly: A = + l match { + case a :: Nil => a + case _ => throw new IllegalArgumentException(s"Expected exactly one element, got $l") + } + +} diff --git a/modules/parser-gen/src/main/smithy/treesitter.smithy b/modules/parser-gen/src/main/smithy/treesitter.smithy new file mode 100644 index 00000000..3c7c687e --- /dev/null +++ b/modules/parser-gen/src/main/smithy/treesitter.smithy @@ -0,0 +1,58 @@ +$version: "2" + +namespace treesittersmithy + +list NodeTypes { + member: NodeType +} + +structure NodeType { + @required + @jsonName("type") + tpe: TypeName + + @required + named: Boolean + + @required + fields: NodeFields = {} + + children: FieldInfo + + @required + subtypes: NodeTypes = [] +} + +string TypeName + +map NodeFields { + key: FieldName + value: FieldInfo +} + +string FieldName + +structure FieldInfo { + @required + multiple: Boolean + + @required + required: Boolean + + @required + types: TypeList +} + +list TypeList { + member: TypeInfo +} + +// https://github.com/disneystreaming/smithy4s/issues/1618 +structure TypeInfo { + @required + @jsonName("type") + tpe: TypeName + + @required + named: Boolean +} diff --git a/modules/parser/src/main/resources/darwin-aarch64/libtree-sitter-smithyql.dylib b/modules/parser/src/main/resources/darwin-aarch64/libtree-sitter-smithyql.dylib index fd69ecf8..8681f5e8 100755 Binary files a/modules/parser/src/main/resources/darwin-aarch64/libtree-sitter-smithyql.dylib and b/modules/parser/src/main/resources/darwin-aarch64/libtree-sitter-smithyql.dylib differ diff --git a/modules/parser/src/main/resources/darwin-x86-64/libtree-sitter-smithyql.dylib b/modules/parser/src/main/resources/darwin-x86-64/libtree-sitter-smithyql.dylib index 04116657..395c5791 100755 Binary files a/modules/parser/src/main/resources/darwin-x86-64/libtree-sitter-smithyql.dylib and b/modules/parser/src/main/resources/darwin-x86-64/libtree-sitter-smithyql.dylib differ diff --git a/modules/parser/src/main/resources/linux-aarch64/libtree-sitter-smithyql.so b/modules/parser/src/main/resources/linux-aarch64/libtree-sitter-smithyql.so index 45c2640e..5b4f52e1 100755 Binary files a/modules/parser/src/main/resources/linux-aarch64/libtree-sitter-smithyql.so and b/modules/parser/src/main/resources/linux-aarch64/libtree-sitter-smithyql.so differ diff --git a/modules/parser/src/main/resources/linux-x86-64/libtree-sitter-smithyql.so b/modules/parser/src/main/resources/linux-x86-64/libtree-sitter-smithyql.so index 74454726..22c31245 100755 Binary files a/modules/parser/src/main/resources/linux-x86-64/libtree-sitter-smithyql.so and b/modules/parser/src/main/resources/linux-x86-64/libtree-sitter-smithyql.so differ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Binding.scala b/modules/parser/src/main/scala/playground/generated/nodes/Binding.scala new file mode 100644 index 00000000..cb267b13 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Binding.scala @@ -0,0 +1,20 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Binding /* private */(node: Node) extends Node { + + def key: Identifier = Identifier(node.fields("key").head) + def value: _Input_node = _Input_node(node.fields("value").head) + + export node.* +} + +object Binding { + def unapply(node: Node): scala.Boolean = node.tpe == "binding" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Bindings.scala b/modules/parser/src/main/scala/playground/generated/nodes/Bindings.scala new file mode 100644 index 00000000..77ca1e46 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Bindings.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Bindings /* private */(node: Node) extends Node { + + + + export node.* +} + +object Bindings { + def unapply(node: Node): scala.Boolean = node.tpe == "bindings" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Boolean_.scala b/modules/parser/src/main/scala/playground/generated/nodes/Boolean_.scala new file mode 100644 index 00000000..8fc52ea7 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Boolean_.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Boolean_ /* private */(node: Node) extends Node { + + + + export node.* +} + +object Boolean_ { + def unapply(node: Node): scala.Boolean = node.tpe == "boolean" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Comment.scala b/modules/parser/src/main/scala/playground/generated/nodes/Comment.scala new file mode 100644 index 00000000..062e860a --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Comment.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Comment /* private */(node: Node) extends Node { + + + + export node.* +} + +object Comment { + def unapply(node: Node): scala.Boolean = node.tpe == "comment" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Identifier.scala b/modules/parser/src/main/scala/playground/generated/nodes/Identifier.scala new file mode 100644 index 00000000..06e1cf9e --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Identifier.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Identifier /* private */(node: Node) extends Node { + + + + export node.* +} + +object Identifier { + def unapply(node: Node): scala.Boolean = node.tpe == "identifier" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Let_binding.scala b/modules/parser/src/main/scala/playground/generated/nodes/Let_binding.scala new file mode 100644 index 00000000..e18a249f --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Let_binding.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Let_binding /* private */(node: Node) extends Node { + + + + export node.* +} + +object Let_binding { + def unapply(node: Node): scala.Boolean = node.tpe == "let_binding" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/List_.scala b/modules/parser/src/main/scala/playground/generated/nodes/List_.scala new file mode 100644 index 00000000..2a31ea3f --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/List_.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class List_ /* private */(node: Node) extends Node { + + def list_fields: List_fields = List_fields(node.fields("list_fields").head) + + export node.* +} + +object List_ { + def unapply(node: Node): scala.Boolean = node.tpe == "list" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/List_fields.scala b/modules/parser/src/main/scala/playground/generated/nodes/List_fields.scala new file mode 100644 index 00000000..7923e68a --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/List_fields.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class List_fields /* private */(node: Node) extends Node { + + + + export node.* +} + +object List_fields { + def unapply(node: Node): scala.Boolean = node.tpe == "list_fields" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Null_.scala b/modules/parser/src/main/scala/playground/generated/nodes/Null_.scala new file mode 100644 index 00000000..1ab7527c --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Null_.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Null_ /* private */(node: Node) extends Node { + + + + export node.* +} + +object Null_ { + def unapply(node: Node): scala.Boolean = node.tpe == "null" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Number.scala b/modules/parser/src/main/scala/playground/generated/nodes/Number.scala new file mode 100644 index 00000000..4713a0a8 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Number.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Number /* private */(node: Node) extends Node { + + + + export node.* +} + +object Number { + def unapply(node: Node): scala.Boolean = node.tpe == "number" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Operation_call.scala b/modules/parser/src/main/scala/playground/generated/nodes/Operation_call.scala new file mode 100644 index 00000000..57b4f82a --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Operation_call.scala @@ -0,0 +1,20 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Operation_call /* private */(node: Node) extends Node { + + def input: Struct = Struct(node.fields("input").head) + def operation_name: Operation_name = Operation_name(node.fields("operation_name").head) + + export node.* +} + +object Operation_call { + def unapply(node: Node): scala.Boolean = node.tpe == "operation_call" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Operation_name.scala b/modules/parser/src/main/scala/playground/generated/nodes/Operation_name.scala new file mode 100644 index 00000000..9a58e09d --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Operation_name.scala @@ -0,0 +1,22 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Operation_name /* private */(node: Node) extends Node { + + def identifier: List[Qualified_identifier] = node.fields("identifier").toList.collect { + case node @ Qualified_identifier() => Qualified_identifier(node) + } + def name: Identifier = Identifier(node.fields("name").head) + + export node.* +} + +object Operation_name { + def unapply(node: Node): scala.Boolean = node.tpe == "operation_name" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Qualified_identifier.scala b/modules/parser/src/main/scala/playground/generated/nodes/Qualified_identifier.scala new file mode 100644 index 00000000..057b5360 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Qualified_identifier.scala @@ -0,0 +1,23 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Qualified_identifier /* private */(node: Node) extends Node { + + def head: Identifier = Identifier(node.fields("head").head) + def selection: Identifier = Identifier(node.fields("selection").head) + def tail: List[Identifier] = node.fields("tail").toList.collect { + case node @ Identifier() => Identifier(node) + } + + export node.* +} + +object Qualified_identifier { + def unapply(node: Node): scala.Boolean = node.tpe == "qualified_identifier" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Source_file.scala b/modules/parser/src/main/scala/playground/generated/nodes/Source_file.scala new file mode 100644 index 00000000..19e95f7c --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Source_file.scala @@ -0,0 +1,20 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Source_file /* private */(node: Node) extends Node { + + def statements: Top_level_statement = Top_level_statement(node.fields("statements").head) + def use_clause: Use_clause = Use_clause(node.fields("use_clause").head) + + export node.* +} + +object Source_file { + def unapply(node: Node): scala.Boolean = node.tpe == "source_file" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/String_.scala b/modules/parser/src/main/scala/playground/generated/nodes/String_.scala new file mode 100644 index 00000000..69198bcf --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/String_.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class String_ /* private */(node: Node) extends Node { + + + + export node.* +} + +object String_ { + def unapply(node: Node): scala.Boolean = node.tpe == "string" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Struct.scala b/modules/parser/src/main/scala/playground/generated/nodes/Struct.scala new file mode 100644 index 00000000..f2d92645 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Struct.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Struct /* private */(node: Node) extends Node { + + def bindings: Bindings = Bindings(node.fields("bindings").head) + + export node.* +} + +object Struct { + def unapply(node: Node): scala.Boolean = node.tpe == "struct" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Top_level_statement.scala b/modules/parser/src/main/scala/playground/generated/nodes/Top_level_statement.scala new file mode 100644 index 00000000..5ed000f8 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Top_level_statement.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Top_level_statement /* private */(node: Node) extends Node { + + + + export node.* +} + +object Top_level_statement { + def unapply(node: Node): scala.Boolean = node.tpe == "top_level_statement" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Use_clause.scala b/modules/parser/src/main/scala/playground/generated/nodes/Use_clause.scala new file mode 100644 index 00000000..b6db1017 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Use_clause.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Use_clause /* private */(node: Node) extends Node { + + def identifier: Qualified_identifier = Qualified_identifier(node.fields("identifier").head) + + export node.* +} + +object Use_clause { + def unapply(node: Node): scala.Boolean = node.tpe == "use_clause" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/Whitespace.scala b/modules/parser/src/main/scala/playground/generated/nodes/Whitespace.scala new file mode 100644 index 00000000..6c758fe9 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/Whitespace.scala @@ -0,0 +1,19 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +case class Whitespace /* private */(node: Node) extends Node { + + + + export node.* +} + +object Whitespace { + def unapply(node: Node): scala.Boolean = node.tpe == "whitespace" +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/generated/nodes/_Input_node.scala b/modules/parser/src/main/scala/playground/generated/nodes/_Input_node.scala new file mode 100644 index 00000000..96350ad4 --- /dev/null +++ b/modules/parser/src/main/scala/playground/generated/nodes/_Input_node.scala @@ -0,0 +1,37 @@ +// Generated code! Do not modify by hand. +package playground.generated.nodes + +import org.polyvariant.treesitter4s.Node + +enum _Input_node { + case BooleanCase(value: Boolean_) + case ListCase(value: List_) + case NullCase(value: Null_) + case NumberCase(value: Number) + case StringCase(value: String_) + case StructCase(value: Struct) + + def asNode: Node = this match { + case BooleanCase(value) => value.node + case ListCase(value) => value.node + case NullCase(value) => value.node + case NumberCase(value) => value.node + case StringCase(value) => value.node + case StructCase(value) => value.node + } +} + +object _Input_node { + def apply(node: Node): _Input_node = node match { + case node @ Boolean_() => BooleanCase(Boolean_(node)) + case node @ List_() => ListCase(List_(node)) + case node @ Null_() => NullCase(Null_(node)) + case node @ Number() => NumberCase(Number(node)) + case node @ String_() => StringCase(String_(node)) + case node @ Struct() => StructCase(Struct(node)) + } +} + +/* + +*/ diff --git a/modules/parser/src/main/scala/playground/smithyql/parser/ParserTreeSitterDemo.scala b/modules/parser/src/main/scala/playground/smithyql/parser/ParserTreeSitterDemo.scala index 57061ba6..52deb153 100644 --- a/modules/parser/src/main/scala/playground/smithyql/parser/ParserTreeSitterDemo.scala +++ b/modules/parser/src/main/scala/playground/smithyql/parser/ParserTreeSitterDemo.scala @@ -2,31 +2,25 @@ package playground.smithyql import org.polyvariant.treesitter4s.Node import org.polyvariant.treesitter4s.TreeSitterAPI +import playground.generated.nodes.* object ParserTreeSitterDemo extends App { - def parse(s: String) = { - val p = TreeSitterAPI.make("smithyql") - val tree = p.parse(s) - println( - tree - .rootNode - .get - .fold[LazyList[Node]](_ #:: _.to(LazyList).flatten) - .filter(_.fields.nonEmpty) - .find(_.source == "Bax") - .get - .parents - .map(p => p.text) - .mkString("\n\n") - ) - } - - parse( + val tree = TreeSitterAPI.make("smithyql").parse { """ - use service foo.bar#Baz + use service foo.bar.bax.qux#Baz Bax { x = 42 } """.stripMargin - ) + } + + val bind = + Operation_call(Source_file(tree.rootNode.get).statements.children.head) + .input + .bindings + .children + .collect { case b @ Binding() => Binding(b) } + .head + + println(bind.key.source + ": " + bind.value.asNode.source) } diff --git a/modules/parser/src/main/scala/playground/smithyql/parser/demo.worksheet.sc b/modules/parser/src/main/scala/playground/smithyql/parser/demo.worksheet.sc new file mode 100644 index 00000000..b3e8859d --- /dev/null +++ b/modules/parser/src/main/scala/playground/smithyql/parser/demo.worksheet.sc @@ -0,0 +1,35 @@ +import org.polyvariant.treesitter4s.Node +import org.polyvariant.treesitter4s.TreeSitterAPI +import playground.generated.nodes.* + +val s = + """ + use service foo.bar.baz.bax#Baz + + Bax { x = 42 , y = 50} + """.stripMargin + +val p = TreeSitterAPI.make("smithyql") + +val tree = p.parse(s) + +Source_file(tree.rootNode.get).use_clause.identifier.head.node.source +// +Source_file(tree.rootNode.get).use_clause.identifier.selection.node.source + +Source_file(tree.rootNode.get) + .use_clause + .identifier + .tail + +val bind = + Operation_call(Source_file(tree.rootNode.get).statements.children.head) + .input + .bindings + .children + .collect { case b @ Binding() => Binding(b) } + .head + +bind.key.source + +bind.value diff --git a/tree-sitter-smithyql/grammar.js b/tree-sitter-smithyql/grammar.js index 2e395bb5..393a439c 100644 --- a/tree-sitter-smithyql/grammar.js +++ b/tree-sitter-smithyql/grammar.js @@ -50,7 +50,7 @@ module.exports = grammar({ ) ), - input_node: ($) => + _input_node: ($) => choice($.struct, $.list, $.number, $.string, $.boolean, $.null), struct: ($) => seq("{", field("bindings", optional($.bindings)), "}"), @@ -59,9 +59,9 @@ module.exports = grammar({ bindings: ($) => comma_separated_trailing($.binding), binding: ($) => - seq(field("key", $.identifier), "=", field("value", $.input_node)), + seq(field("key", $.identifier), "=", field("value", $._input_node)), - list_fields: ($) => comma_separated_trailing($.input_node), + list_fields: ($) => comma_separated_trailing($._input_node), identifier: ($) => /[a-zA-Z_][a-zA-Z0-9_]*/, @@ -74,5 +74,6 @@ module.exports = grammar({ comment: ($) => token(seq("//", /.*/)), whitespace: ($) => /\s+/, }, + supertypes: ($) => [$._input_node], }); //