Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tree-sitter #102

Draft
wants to merge 108 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
84b5a77
WIP: tree-sitter grammar
kubukoz Aug 14, 2022
df716b7
Add tree-sitter submodule
kubukoz Aug 14, 2022
3a881b0
Add grammar
kubukoz Aug 15, 2022
be256cb
Improve grammar, start working on adaptation
kubukoz Aug 16, 2022
4b32252
wip
kubukoz Aug 16, 2022
bf09246
reintroduce NodeDecoder
kubukoz Aug 16, 2022
a81baa9
wip field extraction
kubukoz Aug 16, 2022
036e46d
Merge branch 'main' into tree-sitter
kubukoz Aug 22, 2022
bfbe4ee
Merge branch 'main' into tree-sitter
kubukoz Sep 11, 2022
9a38c2c
Merge branch 'main' into tree-sitter
kubukoz Sep 11, 2022
1380ee8
Support raw service references
kubukoz Sep 11, 2022
b073597
WIP POC more syntax
kubukoz Sep 14, 2022
b801085
Merge branch 'main' into tree-sitter
kubukoz Oct 13, 2022
fe16844
Add treesitter4s
kubukoz Oct 13, 2022
02943aa
rename, add flag
kubukoz Oct 13, 2022
d3a34b4
Add binaries, remove codegen'd code
kubukoz Oct 13, 2022
cd67e5d
remove even more generated stuff
kubukoz Oct 13, 2022
539124b
bump t4s
kubukoz Oct 13, 2022
2dc5dc7
Remove gitmodules
kubukoz Oct 13, 2022
5a04629
Remove boilerplate
kubukoz Oct 13, 2022
4997910
Add s01 resolver
kubukoz Oct 13, 2022
5417ece
Merge branch 'main' into tree-sitter
kubukoz Oct 13, 2022
83e1136
Merge branch 'main' into tree-sitter
kubukoz Oct 15, 2022
6aebdd2
Merge branch 'main' into tree-sitter
kubukoz Oct 18, 2022
bfbd5ef
Less boilerplate in grammar farm build
kubukoz Oct 30, 2022
2808a7f
Update ts4s
kubukoz Oct 30, 2022
23741a5
reusable library packaging
kubukoz Oct 30, 2022
af7d5cc
cleanup
kubukoz Oct 30, 2022
438c12b
Full blown grammar build
kubukoz Oct 31, 2022
eae7d8b
Move library code to ts4s
kubukoz Oct 31, 2022
aa6a324
More work in nix, update things
kubukoz Oct 31, 2022
dc64ccf
cleanup
kubukoz Oct 31, 2022
a67db72
Merge branch 'main' into tree-sitter
kubukoz Oct 29, 2024
7cc3713
use an existing snapshot instead
kubukoz Oct 29, 2024
3b5af71
new age of treesitter4s
kubukoz Oct 30, 2024
88d410f
setup sbt
kubukoz Oct 30, 2024
0949bf1
use adoptium 21
kubukoz Oct 30, 2024
7ea8ce3
updates
kubukoz Oct 30, 2024
bf9f6d4
update build settings
kubukoz Nov 1, 2024
1cd0977
rebuild linux binaries
kubukoz Nov 1, 2024
906d50d
move corpus to new location
kubukoz Nov 1, 2024
a0fb6e1
downgrade ts so that we have ui support
kubukoz Nov 1, 2024
79ff5a9
Merge branch 'main' into tree-sitter
kubukoz Nov 1, 2024
3390152
Generating some tree-sitter code
kubukoz Nov 2, 2024
6f16fd5
Improvements for more coverage
kubukoz Nov 2, 2024
5473186
Render typedChildren
kubukoz Nov 3, 2024
2c619ee
Simpler whitespace
kubukoz Nov 3, 2024
53f77df
get rid of underscores
kubukoz Nov 3, 2024
30fe370
Add precise typed children
kubukoz Nov 3, 2024
6a9083c
Generate projections for ADTs
kubukoz Nov 3, 2024
0652614
Mark unsafe pieces
kubukoz Nov 3, 2024
17d7538
Private nodes + safe projections
kubukoz Nov 3, 2024
6393892
Account for field/child optionality
kubukoz Nov 3, 2024
e469698
update dep, demo
kubukoz Nov 3, 2024
908f08c
something something everything is optional now, old man
kubukoz Nov 4, 2024
2945a42
More progress on error safety
kubukoz Nov 4, 2024
c2757f3
Generate opaque types
kubukoz Nov 4, 2024
b9ca07e
Add a couple tests
kubukoz Nov 5, 2024
567bb2a
Backport things from tree-sitter branch
kubukoz Nov 5, 2024
9871fc1
ignore deprecations in examples
kubukoz Nov 5, 2024
81dd169
Merge branch 'ts-backports' into tree-sitter
kubukoz Nov 5, 2024
1c2446f
Use nixpkgs 24.05
kubukoz Nov 5, 2024
58e7f8f
oops
kubukoz Nov 5, 2024
53dae2d
update gitignore
kubukoz Nov 5, 2024
f65eba4
Use nixpkgs-unstable
kubukoz Nov 5, 2024
385df93
Use nixpkgs-unstable
kubukoz Nov 5, 2024
cf86d3d
Merge branch 'ts-backports' into tree-sitter
kubukoz Nov 5, 2024
68e3f62
never mind
kubukoz Nov 5, 2024
05a6d23
Merge branch 'main' into tree-sitter
kubukoz Nov 5, 2024
d250c74
Involve tree-sitter in parser suites
kubukoz Nov 5, 2024
4713b31
eh
kubukoz Nov 5, 2024
e0793b4
Revamp grammar a bit for more coverage
kubukoz Nov 7, 2024
b83f791
And just like that, we have parity?
kubukoz Nov 7, 2024
c249e1a
Make e2e tests detect snapshot issues
kubukoz Nov 9, 2024
6653084
Merge branch 'main' into tree-sitter
kubukoz Nov 9, 2024
12101f6
Move treesitter sources to another module
kubukoz Nov 9, 2024
75e5b2a
Implement RangeIndex
kubukoz Nov 10, 2024
4177ade
Use tree-sitter in completions
kubukoz Nov 10, 2024
ef1fe2a
Add POC of selector DSL
kubukoz Nov 10, 2024
737e8cd
Remove debugDump comment
kubukoz Nov 10, 2024
dedb2be
Introduce IR for ADTs
kubukoz Nov 11, 2024
fd95b3d
some privacy
kubukoz Nov 11, 2024
4ec6124
first generate, then delete
kubukoz Nov 11, 2024
7629f98
Add IR (which doesn't help much but hey)
kubukoz Nov 11, 2024
c4bce3a
expand selector demo to account for unions
kubukoz Nov 11, 2024
d156026
ADT -> Union
kubukoz Nov 11, 2024
abe7c7e
refactor for consistency
kubukoz Nov 11, 2024
9eca761
Add typed selectors for real
kubukoz Nov 11, 2024
170bebb
Remove selector demo
kubukoz Nov 11, 2024
163325b
Add union unsafeApply and some space
kubukoz Nov 11, 2024
c833e17
Add typed apply on unions
kubukoz Nov 11, 2024
e5946c3
format
kubukoz Nov 11, 2024
d790595
Remove some replaced code, start using selectors
kubukoz Nov 11, 2024
e2809c8
renames
kubukoz Nov 11, 2024
bcc6a66
remove monoid
kubukoz Nov 11, 2024
40455a3
update ci
kubukoz Nov 11, 2024
5458c9b
use ts4s 0.4.0
kubukoz Nov 11, 2024
08fbbd6
More powerful select
kubukoz Nov 11, 2024
a8a2b90
Fix e2e
kubukoz Nov 11, 2024
d3fc988
Small grammar refactor
kubukoz Nov 11, 2024
88ce227
tiny grammar cleanup
kubukoz Nov 12, 2024
9c24e99
Generate typed visitors on unions
kubukoz Nov 12, 2024
6a39e2c
Mark generated files as such
kubukoz Nov 12, 2024
c86bc7a
remove demos
kubukoz Nov 13, 2024
6a57268
Remove a duplicate ContextRange
kubukoz Nov 13, 2024
51184d6
well it is pretty now...
kubukoz Nov 13, 2024
b3b7d01
Merge branch 'main' into tree-sitter
kubukoz Dec 2, 2024
63b40eb
Merge branch 'main' into tree-sitter
kubukoz Dec 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
modules/treesitter/src/main/scala/playground/generated/** linguist-generated
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,22 @@ jobs:
run: nix develop --command bash -c 'cd vscode-extension && yarn && SERVER_VERSION=$(cat ../.version) xvfb-run --auto-servernum yarn test'

- name: Show extension test logs
if: job.status == 'failure'
if: always() && job.status == 'failure'
run: cat vscode-extension/fixture/smithyql-log.txt | tail --lines 1000

build-parser:
name: "Build parser"
strategy:
matrix:
os: [ubuntu-latest, macos-latest, macos-13]
runs-on: ${{matrix.os}}
timeout-minutes: 10
steps:
- uses: actions/[email protected]
- uses: coursier/setup-action@v1
with:
apps: sbt
jvm: adoptium:1.21
- name: Parser tests
# intentionally not setting up nix
run: sbt treesitter/test
48 changes: 41 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ val commonSettings = Seq(
Test / scalacOptions += "-Wconf:cat=deprecation:silent,msg=Specify both message and version:silent",
scalacOptions += "-release:11",
mimaFailOnNoPrevious := false,
resolvers += "Sonatype S01 snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots",
)

def module(
Expand Down Expand Up @@ -133,8 +134,28 @@ lazy val parser = module("parser")
.dependsOn(
ast % "test->test;compile->compile",
source % "test->test;compile->compile",
treesitter % "test->compile",
)

lazy val treesitter = module("treesitter")
.settings(
libraryDependencies ++= Seq(
"org.polyvariant.treesitter4s" %% "core" % "0.4.0"
)
)

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.4.0",
),
scalacOptions -= "-release:11",
)
.enablePlugins(Smithy4sCodegenPlugin)

// Formatter for the SmithyQL language constructs
lazy val formatter = module("formatter")
.settings(
Expand Down Expand Up @@ -189,6 +210,7 @@ lazy val core = module("core")
examples % "test->compile",
pluginCore,
ast,
treesitter,
source % "test->test;compile->compile",
parser % "test->compile;test->test",
formatter,
Expand Down Expand Up @@ -228,17 +250,27 @@ lazy val e2e = module("e2e")
buildInfoKeys ++=
Seq[BuildInfoKey.Entry[_]]( // do you know how to simplify this? let me know please!
Def
.task((lsp / Compile / fullClasspath).value.map(_.data).map(_.toString))
.taskValue
.named("lspClassPath"),
Def
.task(
(lsp / Compile / mainClass).value.getOrElse(sys.error("didn't find main class in lsp"))
.task {
s"""${(lsp / organization).value}::${(lsp / moduleName).value}:${(lsp / version).value}"""
}
// todo: replace with a full publishLocal before e2e in particular gets run (but not before tests run normally)
.dependsOn(
lsp / publishLocal,
languageSupport / publishLocal,
core / publishLocal,
parser / publishLocal,
pluginCore / publishLocal,
source / publishLocal,
treesitter / publishLocal,
ast / publishLocal,
formatter / publishLocal,
protocol4s / publishLocal,
)
.taskValue
.named("lspMainClass"),
.named("lspArtifact")
),
publish / skip := true,
Test / fork := true,
)
.dependsOn(lsp)

Expand All @@ -260,11 +292,13 @@ lazy val root = project
core,
examples,
parser,
parsergen,
formatter,
languageSupport,
lsp,
protocol4s,
pluginCore,
pluginSample,
e2e,
treesitter,
)
14 changes: 7 additions & 7 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 32 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { nixpkgs, flake-utils, ... }:
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
Expand All @@ -18,10 +18,40 @@
pkgs.sbt
pkgs.jless
pkgs.gnupg
(pkgs.tree-sitter.override { webUISupport = true; })
# temporary, while we don't download coursier ourselves
pkgs.coursier
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.xvfb-run ];
};
packages.tree-sitter-smithyql =
pkgs.stdenv.mkDerivation {
name = "tree-sitter-smithyql";
src = ./tree-sitter-smithyql;
buildInputs = [pkgs.tree-sitter pkgs.nodejs];
buildPhase = ''
tree-sitter generate
make
'';
installPhase = if system == "x86_64-darwin" || system == "aarch64-darwin" then ''
cp libtree-sitter-smithyql.dylib $out
'' else ''
cp libtree-sitter-smithyql.so $out
'';
};
packages.tree-sitter-smithyql-all =
pkgs.stdenv.mkDerivation {
name = "tree-sitter-smithyql-all";
src = ./tree-sitter-smithyql;
dontBuild=true;
installPhase = ''
mkdir $out
cd $out
mkdir darwin-aarch64 && cp ${self.packages.aarch64-darwin.tree-sitter-smithyql} darwin-aarch64/libtree-sitter-smithyql.dylib
mkdir darwin-x86-64 && cp ${self.packages.x86_64-darwin.tree-sitter-smithyql} darwin-x86-64/libtree-sitter-smithyql.dylib
mkdir linux-aarch64 && cp ${self.packages.aarch64-linux.tree-sitter-smithyql} linux-aarch64/libtree-sitter-smithyql.so
mkdir linux-x86-64 && cp ${self.packages.x86_64-linux.tree-sitter-smithyql} linux-x86-64/libtree-sitter-smithyql.so
'';
};
}
);
}
11 changes: 6 additions & 5 deletions modules/ast/src/main/scala/playground/smithyql/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cats.Applicative
import cats.Functor
import cats.Id
import cats.Show
import cats.arrow.FunctionK
import cats.data.NonEmptyList
import cats.kernel.Eq
import cats.kernel.Order
Expand Down Expand Up @@ -87,7 +88,7 @@ final case class SourceFile[F[_]](

def mapK[G[_]: Functor](
fk: F ~> G
): AST[G] = SourceFile(
): SourceFile[G] = SourceFile(
prelude = prelude.mapK(fk),
statements = fk(statements).map(_.map(_.mapK(fk))),
)
Expand Down Expand Up @@ -132,9 +133,9 @@ final case class OperationName[F[_]](
text: String
) extends AST[F] {

def mapK[G[_]: Functor](
fk: F ~> G
): OperationName[G] = copy()
def mapK[G[_]: Functor](fk: FunctionK[F, G]): OperationName[G] = retag[G]

def retag[G[_]]: OperationName[G] = copy()

}

Expand Down Expand Up @@ -189,7 +190,7 @@ final case class QueryOperationName[F[_]](
fk: F ~> G
): QueryOperationName[G] = QueryOperationName(
identifier.map(fk(_)),
fk(operationName).map(_.mapK(fk)),
fk(operationName).map(_.retag[G]),
)

}
Expand Down
2 changes: 1 addition & 1 deletion modules/ast/src/test/scala/playground/Assertions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object Assertions extends Expectations.Helpers {
val stringWithResets = d.show()(conf).linesWithSeparators.map(Console.RESET + _).mkString

failure(
s"Diff failed:\n${Console.RESET}(${conf.left("expected")}, ${conf.right("actual")})\n\n" + stringWithResets
s"Diff failed:\n${Console.RESET}(${conf.left("actual")}, ${conf.right("expected")})\n\n" + stringWithResets
)
}

Expand Down
12 changes: 12 additions & 0 deletions modules/core/src/main/scala/playground/ASTAdapter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package playground

import cats.syntax.all.*
import playground.smithyql.QualifiedIdentifier

object ASTAdapter {

def decodeQI(qi: playground.generated.nodes.QualifiedIdentifier): Option[QualifiedIdentifier] =
(qi.namespace.map(_.source).toNel, qi.selection.map(_.source))
.mapN(QualifiedIdentifier.apply)

}
94 changes: 94 additions & 0 deletions modules/core/src/main/scala/playground/MultiServiceResolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import playground.smithyql.UseClause
import playground.smithyql.WithSource

object MultiServiceResolver {
import playground.smithyql.tsutils.*

/** Determines which service should be used for a query. The rules are:
* - If the operation name has a service identifier, there MUST be a service with that name
Expand Down Expand Up @@ -37,6 +38,40 @@ object MultiServiceResolver {
case None => resolveImplicit(queryOperationName.operationName, serviceIndex, useClauses)
}

/** Determines which service should be used for a query. The rules are:
* - If the operation name has a service identifier, there MUST be a service with that name
* that contains the given operation.
* - If there's no service identifier, find all matching services that are included in the use
* clauses. MUST find exactly one entry.
*
* In other cases, such as when we can't find a unique entry, or the explicitly referenced
* service doesn't have an operation with a matching name, we fail. The latter might eventually
* be refactored to a separate piece of code.
*
* **Important**!
*
* This method assumes that all of the use clauses match the available service set. It does NOT
* perform a check on that. For the actual check, see PreludeCompiler.
*/
def resolveServiceTs(
queryOperationName: playground.generated.nodes.QueryOperationName,
serviceIndex: ServiceIndex,
useClauses: List[playground.generated.nodes.UseClause],
): EitherNel[CompilationError, QualifiedIdentifier] =
queryOperationName.name match {
case Some(opName) =>
// todo: this should be an Option in codegen. might be a bad grammar
queryOperationName.identifier.headOption match {
case Some(explicitRef) => resolveExplicitTs(serviceIndex, explicitRef, opName)

case None => resolveImplicitTs(opName, serviceIndex, useClauses)
}
case None =>
// TODO: operation name is invalid or something like that
???

}

private def resolveExplicit(
index: ServiceIndex,
explicitRef: WithSource[QualifiedIdentifier],
Expand Down Expand Up @@ -66,6 +101,41 @@ object MultiServiceResolver {
case Some(_) => explicitRef.value.asRight
}

private def resolveExplicitTs(
index: ServiceIndex,
explicitRef: playground.generated.nodes.QualifiedIdentifier,
operationName: playground.generated.nodes.OperationName,
): EitherNel[CompilationError, QualifiedIdentifier] =
ASTAdapter.decodeQI(explicitRef) match {
case None => ??? /* todo - I don't really know xD */
// explicit reference exists, but doesn't parse
case Some(ref) =>
index.getService(ref) match {
// explicit reference exists, but the service doesn't
case None =>
CompilationError
.error(
CompilationErrorDetails.UnknownService(index.serviceIds.toList),
explicitRef.range,
)
.leftNel

// the service exists, but doesn't have the requested operation
case Some(service)
if !service.operationNames.contains_(OperationName(operationName.source)) =>
CompilationError
.error(
CompilationErrorDetails.OperationMissing(service.operationNames.toList),
operationName.range,
)
.leftNel

// all good
case Some(_) => ref.asRight
}

}

private def resolveImplicit(
operationName: WithSource[OperationName[WithSource]],
index: ServiceIndex,
Expand All @@ -90,4 +160,28 @@ object MultiServiceResolver {
}
}

private def resolveImplicitTs(
operationName: playground.generated.nodes.OperationName,
index: ServiceIndex,
useClauses: List[playground.generated.nodes.UseClause],
): EitherNel[CompilationError, QualifiedIdentifier] = {
val matchingServices = index
.getServices(useClauses.flatMap(_.identifier).flatMap(ASTAdapter.decodeQI).toSet)
.filter(_.hasOperation(OperationName(operationName.source)))

matchingServices match {
case one :: Nil => one.id.asRight
case _ =>
CompilationError
.error(
CompilationErrorDetails
.AmbiguousService(
workspaceServices = index.serviceIds.toList
),
operationName.range,
)
.leftNel
}
}

}
Loading
Loading