Skip to content

Commit

Permalink
Scala.js bindings (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubukoz authored Oct 29, 2022
1 parent 14381b9 commit 8c8ea67
Show file tree
Hide file tree
Showing 24 changed files with 750 additions and 25 deletions.
42 changes: 39 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
scala: [2.12.17, 2.13.10, 3.2.0]
java: [temurin@8]
project: [rootJVM]
project: [rootJS, rootJVM]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
Expand Down Expand Up @@ -69,10 +69,16 @@ jobs:
- name: Check that workflows are up to date
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' 'project /' githubWorkflowCheck

- run: yarn

- name: Check headers and formatting
if: matrix.java == 'temurin@8'
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck

- name: scalaJSLink
if: matrix.project == 'rootJS'
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' Test/scalaJSLinkerResult

- name: Test
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' test

Expand All @@ -86,11 +92,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p target .js/target bindingsPython/.jvm/target bindingsScala/.jvm/target core/.jvm/target .jvm/target .native/target tests/.jvm/target project/target
run: mkdir -p bindingsPython/.js/target target .js/target core/.js/target bindingsScala/.js/target bindingsPython/.jvm/target bindingsScala/.jvm/target core/.jvm/target tests/.js/target .jvm/target .native/target tests/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar target .js/target bindingsPython/.jvm/target bindingsScala/.jvm/target core/.jvm/target .jvm/target .native/target tests/.jvm/target project/target
run: tar cf targets.tar bindingsPython/.js/target target .js/target core/.js/target bindingsScala/.js/target bindingsPython/.jvm/target bindingsScala/.jvm/target core/.jvm/target tests/.js/target .jvm/target .native/target tests/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down Expand Up @@ -143,6 +149,16 @@ jobs:
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Download target directories (2.12.17, rootJS)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.17-rootJS

- name: Inflate target directories (2.12.17, rootJS)
run: |
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.12.17, rootJVM)
uses: actions/download-artifact@v2
with:
Expand All @@ -153,6 +169,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.13.10, rootJS)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.10-rootJS

- name: Inflate target directories (2.13.10, rootJS)
run: |
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.13.10, rootJVM)
uses: actions/download-artifact@v2
with:
Expand All @@ -163,6 +189,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.2.0, rootJS)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-3.2.0-rootJS

- name: Inflate target directories (3.2.0, rootJS)
run: |
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.2.0, rootJVM)
uses: actions/download-artifact@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .sbtopts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-J-Xmx8G
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2022 Polyvariant
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polyvariant.treesitter4s.bindings.python

import org.polyvariant.treesitter4s.Language
import scalajs.js
import js.annotation.JSImport

object PythonLanguageBindings {

@js.native
@js.annotation.JSImport("tree-sitter-python", JSImport.Namespace)
def Python: Language = js.native

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2022 Polyvariant
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polyvariant.treesitter4s.bindings.scala

import org.polyvariant.treesitter4s.Language
import scalajs.js
import js.annotation.JSImport

object ScalaLanguageBindings {

@js.native
@js.annotation.JSImport("tree-sitter-scala", JSImport.Namespace)
def Scala: Language = js.native

}
32 changes: 24 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ThisBuild / developers := List(tlGitHubDev("kubukoz", "Jakub Kozłowski"))
ThisBuild / tlSonatypeUseLegacyHost := false
ThisBuild / githubWorkflowOSes := Seq("ubuntu-latest", "macos-latest")

ThisBuild / githubWorkflowBuild ~= (WorkflowStep.Run(commands = List("yarn")) +: _)

def crossPlugin(x: sbt.librarymanagement.ModuleID) = compilerPlugin(x.cross(CrossVersion.full))

val compilerPlugins = List(
Expand All @@ -27,9 +29,9 @@ ThisBuild / tlFatalWarningsInCi := false

val commonSettings = Seq(
libraryDependencies ++= compilerPlugins ++ Seq(
"com.disneystreaming" %%% "weaver-cats" % "0.7.15" % Test,
"com.disneystreaming" %%% "weaver-discipline" % "0.7.15" % Test,
"com.disneystreaming" %%% "weaver-scalacheck" % "0.7.15" % Test,
"com.disneystreaming" %%% "weaver-cats" % "0.8.0" % Test,
"com.disneystreaming" %%% "weaver-discipline" % "0.8.0" % Test,
"com.disneystreaming" %%% "weaver-scalacheck" % "0.8.0" % Test,
),
testFrameworks += new TestFramework("weaver.framework.CatsEffect"),
scalacOptions ++= {
Expand All @@ -44,11 +46,21 @@ val jvmTargetOptions = Seq("-source", "8", "-target", "8")

val commonJVMSettings = Seq(
javacOptions ++= jvmTargetOptions,
doc / javacOptions --= jvmTargetOptions.:+("-Xlint:all"),
doc / javacOptions --= (jvmTargetOptions :+ "-Xlint:all"),
Test / fork := true,
scalacOptions ++= {
if (scalaVersion.value.startsWith("2.13"))
Seq("-Wnonunit-statement")
else
Nil
},
)

val commonJSSettings = Seq(
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
)

lazy val core = crossProject(JVMPlatform)
lazy val core = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.settings(
commonSettings
Expand All @@ -59,32 +71,36 @@ lazy val core = crossProject(JVMPlatform)
"net.java.dev.jna" % "jna" % "5.12.1"
),
)
.jsSettings(commonJSSettings)

lazy val bindingsScala = crossProject(JVMPlatform)
lazy val bindingsScala = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.settings(
name := "language-scala",
commonSettings,
)
.dependsOn(core)
.jvmSettings(commonJVMSettings)
.jsSettings(commonJSSettings)

lazy val bindingsPython = crossProject(JVMPlatform)
lazy val bindingsPython = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.settings(
name := "language-python",
commonSettings,
)
.dependsOn(core)
.jvmSettings(commonJVMSettings)
.jsSettings(commonJSSettings)

lazy val tests = crossProject(JVMPlatform)
lazy val tests = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.settings(
commonSettings
)
.dependsOn(bindingsScala, bindingsPython)
.jvmSettings(commonJVMSettings)
.jsSettings(commonJSSettings)
.enablePlugins(NoPublishPlugin)

lazy val root = tlCrossRootProject
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2022 Polyvariant
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polyvariant.treesitter4s

@scalajs.js.native
trait Language extends scalajs.js.Any
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2022 Polyvariant
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polyvariant.treesitter4s

import scala.scalajs.js
import scala.scalajs.js.UndefOr
import scala.scalajs.js.annotation.JSImport

@js.native
@JSImport("tree-sitter", JSImport.Namespace)
private class Parser extends js.Object {

def setLanguage(language: js.Any): Unit = js.native
def parse(s: String): TSTree = js.native
}

@js.native
trait TSTree extends js.Object {
def rootNode: SyntaxNode = js.native
}

@js.native
trait TSCursor extends js.Object {
def gotoFirstChild(): Boolean = js.native
def gotoNextSibling(): Boolean = js.native
def currentFieldName: UndefOr[String] = js.native
}

@js.native
trait SyntaxNode extends js.Object {
def children: js.Array[SyntaxNode] = js.native
def `type`: String = js.native
def startIndex: Int = js.native
def endIndex: Int = js.native
def text: String = js.native

def walk(): TSCursor
}

protected trait TreeSitterPlatform {

def make(language: Language): TreeSitter =
new TreeSitter {

def parse(source: String): Tree = {
val p = new Parser()
p.setLanguage(language)

val t = p.parse(source)
new Tree {
val rootNode: Option[Node] = Some(unwrapNode(t.rootNode))
}
}

private def unwrapNode(nativeNode: SyntaxNode): Node =
new Node {

val source: String = nativeNode.text

val text: String = nativeNode.toString()

val tpe: String = nativeNode.`type`

val children: List[Node] =
nativeNode
.children
.map(unwrapNode(_))
.toList

val fields: Map[String, Node] = {
// Workaround for the node interface not having proper field names:
// we walk the node's children and gather the field names that way.
val fieldMap = Map.newBuilder[String, Node]
val c = nativeNode.walk()
c.gotoFirstChild()

var i = 0

while ({
c.currentFieldName.toOption match {
case Some(fn) => fieldMap += (fn -> children(i))
case None => ()
}
i += 1
c.gotoNextSibling()
}) {}

fieldMap.result()
}

val startByte: Int = nativeNode.startIndex

val endByte: Int = nativeNode.endIndex

}

}

}
Loading

0 comments on commit 8c8ea67

Please sign in to comment.