From 3390152d7011db1a8dbd30cc49fc5dacec232bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 2 Nov 2024 03:17:17 +0100 Subject: [PATCH] Generating some tree-sitter code --- build.sbt | 17 +- .../playground/parsergen/ParserGen.scala | 208 ++++++++++++++++++ .../src/main/smithy/treesitter.smithy | 58 +++++ .../libtree-sitter-smithyql.dylib | Bin 34384 -> 34384 bytes .../libtree-sitter-smithyql.dylib | Bin 16456 -> 16456 bytes .../linux-aarch64/libtree-sitter-smithyql.so | Bin 67368 -> 67368 bytes .../linux-x86-64/libtree-sitter-smithyql.so | Bin 18304 -> 18304 bytes .../playground/generated/nodes/Binding.scala | 20 ++ .../playground/generated/nodes/Bindings.scala | 19 ++ .../playground/generated/nodes/Boolean_.scala | 19 ++ .../playground/generated/nodes/Comment.scala | 19 ++ .../generated/nodes/Identifier.scala | 19 ++ .../generated/nodes/Let_binding.scala | 19 ++ .../playground/generated/nodes/List_.scala | 19 ++ .../generated/nodes/List_fields.scala | 19 ++ .../playground/generated/nodes/Null_.scala | 19 ++ .../playground/generated/nodes/Number.scala | 19 ++ .../generated/nodes/Operation_call.scala | 20 ++ .../generated/nodes/Operation_name.scala | 22 ++ .../nodes/Qualified_identifier.scala | 23 ++ .../generated/nodes/Source_file.scala | 20 ++ .../playground/generated/nodes/String_.scala | 19 ++ .../playground/generated/nodes/Struct.scala | 19 ++ .../generated/nodes/Top_level_statement.scala | 19 ++ .../generated/nodes/Use_clause.scala | 19 ++ .../generated/nodes/Whitespace.scala | 19 ++ .../generated/nodes/_Input_node.scala | 37 ++++ .../parser/ParserTreeSitterDemo.scala | 34 ++- .../smithyql/parser/demo.worksheet.sc | 35 +++ tree-sitter-smithyql/grammar.js | 7 +- 30 files changed, 742 insertions(+), 25 deletions(-) create mode 100644 modules/parser-gen/src/main/scala/playground/parsergen/ParserGen.scala create mode 100644 modules/parser-gen/src/main/smithy/treesitter.smithy create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Binding.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Bindings.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Boolean_.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Comment.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Identifier.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Let_binding.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/List_.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/List_fields.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Null_.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Number.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Operation_call.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Operation_name.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Qualified_identifier.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Source_file.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/String_.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Struct.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Top_level_statement.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Use_clause.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/Whitespace.scala create mode 100644 modules/parser/src/main/scala/playground/generated/nodes/_Input_node.scala create mode 100644 modules/parser/src/main/scala/playground/smithyql/parser/demo.worksheet.sc 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 fd69ecf89012885df0c3206413b5c41348d83ab1..8681f5e8cbcf10c600e245d6781d9e4986cd0b0c 100755 GIT binary patch delta 811 zcmcc6!*rpCX+nqK9wi1a;NgPMdnV4b7d@c^;baIxXb_)ivLdteWDZ6JK?6I8Xe}dz zHrQ;*XwD>X&S2Wb$H&d{_9jGDT&|2@*xbNuBhRR?`JmDpVP*zK#?2ed{5TlnC;zkC z#KgcbdA+?XFUWEepdlc9a`H`kf5{gX5HoA^A@m=R6cE%*c61Q8ykiRC@t8sA2q=95 zN(-4o#6_TV50pMJd7^{AWeL=5nEOqj{;+`3R#4gwO1nX6kI9^l=JoMVUJ8`Xg3^Uh zx&lhqLg`j0JpoG3fzlhG^d2aE8cJV-(yyWPFDT7s5B5_%122TZAOxkQp|moT)`!yO zP&x!kr$T8+&@jODSHjdmEvkd^Tc9+|+zv1wm;zuf>xC+q4y6}CX_!xz+i%{;A|SW< z01G3CC}o_~pv|_&6=?sS$%YLt6|O248~V%N_4>Hv%{}h`#~+pRpEcc_dTv37-1FGq zd<_#OTQw>q+)rkC!u4Rv%zcg?2hL^Na{ delta 811 zcmcc6!*rpCX+nqK0VM`7;NgPM2PV$67d@i`;baIxXb_)ivLdteWDZ6JK@&TOXbmHT zHrZ^+XwD?yVl6Z6rm3y`gtsAocbyjbxw(PaMt%|_>*l>mZ-gf=FyjQ_&AZL)IVS(M z+cbHDy$mnNauc8-AbfW6O?!XIHx>{x8}uPGgDI3h+0jAV@&S}5UG$c60vCC1pQ`6MVsUeCt!yImO8J|6BP2C{uDp3XE2^ZKpKqGj zVUri^AD1!Zq<(8SZsxYLu6pOKKZj;svE8%JMDU2~>~A5tt?Trb%H9Y)_0XvB@x*=e zugba{oxH0tLE)qJiYW!xnj0gyOjJI~w7>q>FTv<-=y_H$uYHP?#00U;R!w3o0LkXG A+yDRo 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 04116657e3ac2ad8fd6094eb8eba1ed76cb90dff..395c5791f38cbe389bba8f2db895ff45cc3942cc 100755 GIT binary patch delta 275 zcmX@nz<8p8af1OPW9?)^Mt9LQ`V3$Y0HGK_e5T2Y%+iw`m{>NiVLZYp@bK8F^4Io; zkJrzgyzj%&RQ}Bk%paAQ85kKim#8&yFvd^*rniZSfnoA;{m@MY7Z@Fxphj{+X+9_| z4yEOxv^JDBhSC90I&rd~k$56ZS1C+Z58}cqD8B(p!%S;|@}EQ1bwc@*q4Yc`eH<#k M7-Y-l1x71)0V7v0fB*mh delta 271 zcmX@nz<8p8af1OPW6fkkMt9K_`V3$Y0HGK_e5T2Y%+iw`m{>NiVLZYpkS(jsvhbIk zc8AOA>`gC@s%>sy{-`uLUQK9ovsy97JHbDuIdy=s{dp3FX&AX_#rvQ2ukMx(+CR5|o|`rH@197lCZq JyufG$F94Y2E?NKp 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 45c2640ef63ea843a64e8002bd558ada542073c0..5b4f52e15e081f7f112756496022e9958956e976 100755 GIT binary patch delta 73 zcmZ3{$Fic2WkWc#f<W?_1=L3(z1a$Zt;Mv5bbb8E=n2LPzx81Mi9 delta 73 zcmZ3{$Fic2WkWc#f>DM=c0o~Lc3x7Zc~WwwaaodyfpL*ZZen(Fc15nG$>wtADy7Ny dRfRS)tNmr<&de()Es4*|Pf2Ci%&j4N9{|pr8b<&C 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 744547263e582b4304a5512d104431b16cea5961..22c312457734d87bc7591a72b4d3d6573b097742 100755 GIT binary patch delta 191 zcmZqZXKd(aT)?7`Qk89PRGgVuY*CeKS(cWUSXE|GWty3jXknC>Rhe11S%JlaO>(Ls z0~oCEh0q)jieb*=My`0qg_9q0iA%18s=0xt=GbIGZgIxzn>D##s4z1yGHw3P-bXRWMGkISdmz4mRppYT$r7_S%JlaO>&AM z0~oCEh0q)jiedKTMy`0q1(P3giA%14s=0xt=ICTWZgIwIn>D##s7(H^FSJ?NAdPWy rmtoT8*M?iUIblZ6p1jglUUC7 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], }); //