Merge commit '628e0aab6c2f7d31cf3b7d730f964d4fd9b340ee'

pull/481/head
Hugo van Rijswijk 2023-02-08 10:50:58 +07:00
commit 0a4c977311
31 changed files with 1149475 additions and 326848 deletions

@ -0,0 +1,2 @@
# Move queries to ./queries/scala sub-directory
889eb65aea8e0f9ce1694ab9ee528df1fa5f75d1

@ -0,0 +1,66 @@
name: Bug Report
description: Create an issue in tree-sitter-scala
body:
- type: input
id: commit
attributes:
label: Commit of tree-sitter-scala you tested this on
description: |
Make sure you're validating you're on the latest commit of
tree-sitter-scala. Note that depending on where you're experiencing the
issue there is a chance you're on an old commit.
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: A code sample showing the error
description: Try to minimize the code sample as much as possible
placeholder: |
```scala
val y = x match {
case A(_) if a => 1
}
```
validations:
required: true
- type: textarea
id: error
attributes:
label: Show the error node
description: Show us the output and where the ERROR node is appearing
placeholder: |
Check out [Obtaining an Error Reproduction](../../CONTRIBUTING.md#obtaining-an-error-reproduction)
in the CONTRIBUTING docs if you're unsure how to get this.
```
(compilation_unit [0, 0] - [4, 0]
(val_definition [0, 0] - [2, 1]
pattern: (identifier [0, 4] - [0, 5])
value: (match_expression [0, 8] - [2, 1]
value: (identifier [0, 8] - [0, 9])
body: (case_block [0, 16] - [2, 1]
(ERROR [1, 2] - [2, 0]
(case_class_pattern [1, 7] - [1, 11]
type: (type_identifier [1, 7] - [1, 8])
pattern: (wildcard [1, 9] - [1, 10]))
(lambda_expression [1, 15] - [2, 0]
(identifier [1, 15] - [1, 16])
(integer_literal [1, 20] - [1, 21])))))))
```
validations:
required: true
- type: textarea
id: expectation
attributes:
label: What do you expect the tree to look like
description: Show us what you expect the tree to look like
- type: input
id: where
attributes:
label: Where are you experiencing this error?
description: Let us know where you're seeing this error (nvim-treesitter etc)

@ -5,16 +5,115 @@ on:
branches:
- master
jobs:
changedfiles:
runs-on: ubuntu-20.04
outputs:
all: ${{ steps.changes.outputs.all}}
c: ${{ steps.changes.outputs.c }}
steps:
- name: checkout tree-sitter-scala
uses: actions/checkout@v3
with:
fetch-depth: 10
- name: Get changed files
id: changes
run: |
echo "all=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | xargs)" >> $GITHUB_OUTPUT
echo "c=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep '\.\(c\|h\)$' | xargs)" >> $GITHUB_OUTPUT
test:
runs-on: ${{ matrix.os }}
needs: changedfiles
strategy:
fail-fast: true
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
os: [ubuntu-20.04, macos-latest, windows-2019]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- name: checkout tree-sitter-scala
uses: actions/checkout@v3
with:
fetch-depth: 10
- name: checkout scala/scala
if: ${{ runner.os == 'Linux' }}
uses: actions/checkout@v3
with:
repository: scala/scala
ref: v2.13.10
path: scala_scala
- name: checkout lampepfl/dotty
if: ${{ runner.os == 'Linux' }}
uses: actions/checkout@v3
with:
repository: lampepfl/dotty
ref: 3.2.1
path: dotty
- name: checkout nvim-treesitter/nvim-treesitter
if: ${{ runner.os == 'Linux' }}
uses: actions/checkout@v3
with:
repository: nvim-treesitter/nvim-treesitter
path: nvim_treesitter
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Test C stack code
run: gcc test/test-stack.c -o a.out && ./a.out
- name: Generate parser from scratch and test it
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
npm install
npm run build
npm test
- name: Check parser
if: ${{ runner.os != 'Linux' && needs.changedfiles.outputs.c }}
shell: bash
run: |
# `git diff --quiet` doesn't seem to work on Github Actions
changes=$(git diff --name-only --diff-filter=ACMRT | xargs)
if [ ! -z "$changes" ]; then
echo "::error file=grammar.js::Generated $changes differs from the checked in version"
git diff --exit-code
exit 1
fi
- name: Parser tests
if: ${{ needs.changedfiles.outputs.c }}
shell: bash
run: npm install && npm test
- name: Smoke test
if: ${{ runner.os == 'Linux' }}
shell: bash
env:
SCALA_SCALA_DIR: scala_scala
DOTTY_DIR: dotty
run: script/smoke_test.sh
- name: copy nvim-treesitter highlight queries
if: ${{ runner.os == 'Linux' }}
shell: bash
run: cp ./nvim_treesitter/queries/scala/highlights.scm ./queries/scala/highlights.scm
- name: Check if highlight queries are out of sync with nvim-treesitter
if: ${{ runner.os == 'Linux' }}
uses: tj-actions/verify-changed-files@v13
id: verify-changed-files
with:
node-version: 14
- run: npm install
- run: npm test
files: |
queries/scala/highlights.scm
- name: Test quries if out of sync with nvim-treesitter
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |
echo "::warning file=queries/scala/highlights.scm::Queries in this repo are out of sync with nvim-treesitter"
git diff queries/scala/highlights.scm
npm run test

@ -0,0 +1,25 @@
name: Fuzz parser
# Run this workflow on changes to the external scanner
on:
workflow_dispatch:
push:
paths:
- src/scanner.c
- src/stack.h
pull_request:
paths:
- src/scanner.c
- src/stack.h
jobs:
test:
name: Parser fuzzing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: vigoux/tree-sitter-fuzz-action@v1
with:
language: scala
external-scanner: src/scanner.c
time: 60

@ -0,0 +1,54 @@
name: "Check changes and sync"
on:
workflow_dispatch:
schedule:
- cron: 0 5 * * *
jobs:
check-and-sync:
runs-on: ubuntu-20.04
outputs:
all: ${{ steps.changes.outputs.all}}
c: ${{ steps.changes.outputs.c }}
steps:
- name: checkout tree-sitter-scala
uses: actions/checkout@v3
with:
fetch-depth: 10
- name: Generate parser from scratch
run: |
npm install
npm run build
- name: Check for changes
uses: tj-actions/verify-changed-files@v13
id: verify-changed-files
with:
files: |
src/grammar.json
src/node-types.json
src/parser.c
src/tree_sitter/parser.h
- name: Commit changes if necessary
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |
git config user.name "GitHub"
git config user.email "noreply@github.com"
git add .
LAST_COMMIT=$(git log --format="%H" -n 1)
git commit -m "chore: generate and sync from $LAST_COMMIT"
git clean -xf
- name: Create Pull Request
if: steps.verify-changed-files.outputs.files_changed == 'true'
uses: peter-evans/create-pull-request@v4
with:
title: "chore: generate and sync latest changes"
branch: generation
base: ${{ github.head_ref }}
- name: No changes detected
if: steps.verify-changed-files.outputs.files_changed == 'false'
run: echo "No changes detected"

@ -7,3 +7,8 @@ package-lock.json
Cargo.lock
.scalafmt.conf
*worksheet.sc
*.out
.direnv
./scala
./dotty

@ -0,0 +1,109 @@
# Contributing to tree-sitter-scala
First off, thanks for being willing to contribute to making syntax highlighting
in Scala better! This document will hopefully help you get set up, understand
how to work on the codebase, or link to places to help you understand
tree-sitter.
## Requirements
- Node.js version 18.0 or greater
- C Compiler
Refer to the [tree-sitter
documentation](https://tree-sitter.github.io/tree-sitter/creating-parsers#dependencies)
for more details and specifics.
If you use nix you can enter a nix-shell (`nix-shell .`) which will install them
for you.
## Getting Started
To get started with contributing to the grammar you'll first need to install the
project's dependencies:
```sh
npm install
```
The general flow will often start with adding a test case to `./corpus`. You can
find details on how testing works with tree-sitter
[here](https://tree-sitter.github.io/tree-sitter/creating-parsers#command-test).
Once you've added your test case you'll want to then make the required changes
to `grammar.js`, regenerate and recompile the parser, and run the tests:
```sh
npm run build
npm test
```
Then adjust as necessary. Note that depending on your change you may also have
to touch the `/src/scanner.c` file if you need more advanced features like
look-ahead.
## Syntax highlighting
Right now the most common use-case for syntax highlight using tree-sitter is
[nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter), which
means much of our testing is in relation to it. You can find the syntax
highlighting tests in `test/highlight/*.scala`. You can read more about this
type of testing
[here](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#unit-testing). These test will be automatically ran with `npm run test`.
### tree-sitter highlight
Another way to test your syntax highlighting locally is to use the `tree-sitter
highlight` command. Note that you'll need to have `tree-sitter` installed
globally for this to work. Once you have it installed you'll want to follow the
instructions [here](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#per-user-configuration) to setup a local config that points towards this repo to be used as a parser. Once done you can then do the following:
```sh
tree-sitter highlight some/scala/file.scala
```
And see the syntax highlighting spit out. This is also the format used for
testing, so it provides an easy way to get the output we use for testing.
## Generation
In order to help not cause conflicts with multiple prs being open at the same
time, we don't check in the parser on each pr. Instead, just check in the
changes to the `grammar.js` and the CI will take care of the rest. Each night if
changes are detected it will automatically be generated.
## Smoke testing
You'll noticed that there is a part of CI that checks parsing against the Scala
2 and Scala 3 repositories to ensure we don't introduce unexpected regressions.
The script for this is in `script/smoke_test.sh`. If you're change is increasing
the coverage in either the library or compiler, please do update the expected
percentages at the top of the file.
## Obtaining an error reproduction
_With Neovim_
When creating an issue you'll want to ensure to include the `ERROR` node in it's
context. There's a couple ways to do this. If you're using Neovim and utilizing
treesitter, you can see the tree with `:lua vim.treesitter.show_tree()`. You can
copy it directly from the open panel.
_Manually_
A manual way to get this would be to ensure that when you're in the repo you
have the latest parser with
```sh
npm run build
```
Then you can copy your Scala code in a file and pass that file into `npm run
parse`:
```
npm run parse <path/to/your/file.scala>
```
Then the tree will be printed out for you to copy.

@ -1,7 +1,7 @@
[package]
name = "tree-sitter-scala"
description = "scala grammar for the tree-sitter parsing library"
version = "0.19.0"
version = "0.20.7"
keywords = ["incremental", "parsing", "scala"]
categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-javascript"
@ -20,7 +20,7 @@ include = [
path = "bindings/rust/lib.rs"
[dependencies]
tree-sitter = "0.19"
tree-sitter = "0.20.7"
[build-dependencies]
cc = "1.0"

@ -1,28 +1,21 @@
tree-sitter-scala
=================
# tree-sitter-scala
[![Test the grammar](https://github.com/tree-sitter/tree-sitter-scala/actions/workflows/ci.yml/badge.svg)](https://github.com/tree-sitter/tree-sitter-scala/actions/workflows/ci.yml)
Scala grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
Scala grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter)
covering both Scala 2 and 3.
References
## References
* [The Scala Language Specification](https://www.scala-lang.org/files/archive/spec/2.13/)
* [Scala Syntax Summary](https://www.scala-lang.org/files/archive/spec/2.13/13-syntax-summary.html)
_Scala 2_
- [The Scala 2 Language Specification](https://www.scala-lang.org/files/archive/spec/2.13/)
- [Scala 2 Syntax Summary](https://www.scala-lang.org/files/archive/spec/2.13/13-syntax-summary.html)
Development
-----------
_Scala 3_
First, install the project's dependencies:
- [Scala 3 Syntax Summary](https://docs.scala-lang.org/scala3/reference/syntax.html)
```sh
npm install
```
## Development and Contributing
Add a test case to `./corpus`, make the required changes to `grammar.js`,
regenerate and recompile the parser, and run the tests:
```sh
npm run build
npm test
```
Please refer to the [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on
getting set up.

@ -35,7 +35,7 @@ pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
// Uncomment these to include any queries that this grammar contains
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");

@ -1,3 +1,114 @@
=======================================
Identifiers
=======================================
def m = ???
def unary_! = true
def a_-> = ???
def __symtem = ???
def empty_? = ???
def ひらがな = ???
def a_^ = ???
---
(compilation_unit
(function_definition (identifier) (operator_identifier))
(function_definition (identifier) (boolean_literal))
(function_definition (identifier) (operator_identifier))
(function_definition (identifier) (operator_identifier))
(function_definition (identifier) (operator_identifier))
(function_definition (identifier) (operator_identifier))
(function_definition (identifier) (operator_identifier)))
=======================================
$ in identifier names
=======================================
class $A$B$ {
val b$, c$ : Int
val d$ : String
}
---
(compilation_unit
(class_definition
(identifier)
(template_body
(val_declaration
(identifier)
(identifier)
(type_identifier))
(val_declaration
(identifier)
(type_identifier)))))
===================================
Operator identifiers
===================================
type ::[+Ab] = scala.collection.immutable.::[Ab]
val :: = scala.collection.immutable.::
val +: = scala.collection.+:
val :+ = scala.collection.:+
def → = ???
val test = id.##
val x = y
/////////
// avoid matching slashes as operator
/////////
---
(compilation_unit
(type_definition
(type_identifier)
(type_parameters
(covariant_type_parameter
(identifier)))
(generic_type
(stable_type_identifier (stable_identifier (stable_identifier (identifier) (identifier)) (identifier))
(type_identifier))
(type_arguments (type_identifier))))
(val_definition
(operator_identifier)
(field_expression
(field_expression
(field_expression
(identifier)
(identifier))
(identifier))
(operator_identifier)))
(val_definition
(operator_identifier)
(field_expression
(field_expression
(identifier)
(identifier))
(operator_identifier)))
(val_definition
(operator_identifier)
(field_expression
(field_expression
(identifier)
(identifier))
(operator_identifier)))
(function_definition (operator_identifier) (operator_identifier))
(val_definition
(identifier)
(field_expression
(identifier)
(operator_identifier)))
(val_definition (identifier) (identifier))
(comment)
(comment)
(comment))
================================
Package
================================
@ -15,6 +126,41 @@ package c {
(template_body
(object_definition (identifier)))))
================================
Package with comma
================================
package a.b;
---
(compilation_unit
(package_clause (package_identifier (identifier) (identifier))))
================================
Package (Scala 3 syntax)
================================
package a.b
package c:
object A
package d:
object A
end d
---
(compilation_unit
(package_clause (package_identifier (identifier) (identifier)))
(package_clause (package_identifier (identifier))
(template_body
(object_definition (identifier))))
(package_clause (package_identifier (identifier))
(template_body
(object_definition (identifier)))))
================================
Package object
================================
@ -39,18 +185,23 @@ Imports
import PartialFunction.condOpt
import a.b, c.e
import reflect.io.{Directory, File, Path}
import a.{
b,
}
---
(compilation_unit
(import_declaration
(stable_identifier (identifier) (identifier)))
(identifier) (identifier))
(import_declaration
(stable_identifier (identifier) (identifier))
(stable_identifier (identifier) (identifier)))
(identifier)
(identifier)
(identifier) (identifier))
(import_declaration
(stable_identifier (identifier) (identifier))
(import_selectors (identifier) (identifier) (identifier))))
(identifier) (identifier)
(namespace_selectors (identifier) (identifier) (identifier)))
(import_declaration (identifier)
(namespace_selectors (identifier))))
================================
Imports: Wildcard
@ -62,21 +213,26 @@ import tools.nsc.classpath._
(compilation_unit
(import_declaration
(stable_identifier (stable_identifier (identifier) (identifier)) (identifier))
(import_wildcard)))
(identifier) (identifier) (identifier)
(namespace_wildcard)))
================================
Imports: Wildcard (Scala 3 syntax)
================================
import tools.nsc.classpath.*
import a.b.*, b.c, c.*
---
(compilation_unit
(import_declaration
(stable_identifier (stable_identifier (identifier) (identifier)) (identifier))
(import_wildcard)))
(identifier) (identifier) (identifier) (namespace_wildcard))
(import_declaration
(identifier) (identifier) (namespace_wildcard)
(identifier) (identifier)
(identifier) (namespace_wildcard)))
================================
Imports: Wildcard and wildcard givens (Scala 3 syntax)
@ -89,13 +245,13 @@ import tools.given
(compilation_unit
(import_declaration
(stable_identifier (stable_identifier (identifier) (identifier)) (identifier))
(import_selectors
(import_wildcard)
(import_wildcard)))
(identifier) (identifier) (identifier)
(namespace_selectors
(namespace_wildcard)
(namespace_wildcard)))
(import_declaration
(identifier)
(import_wildcard)))
(namespace_wildcard)))
================================
Imports: Givens by type (Scala 3 syntax)
@ -107,8 +263,8 @@ import tools.nsc.classpath.{given Test, given Test2, Test3}
(compilation_unit
(import_declaration
(stable_identifier (stable_identifier (identifier) (identifier)) (identifier))
(import_selectors
(identifier) (identifier) (identifier)
(namespace_selectors
(type_identifier)
(type_identifier)
(identifier))))
@ -118,26 +274,56 @@ Imports: Rename (Scala 3 Syntax)
================================
import lang.System.{lineSeparator as EOL}
import lang.System.lineSeparator as EOL
import lang.System.lineSeparator as _
---
(compilation_unit
(import_declaration
(stable_identifier (identifier) (identifier))
(import_selectors (renamed_identifier (identifier) (identifier)))))
(identifier)
(identifier)
(namespace_selectors
(as_renamed_identifier
(identifier)
(identifier))))
(import_declaration
(identifier)
(identifier)
(as_renamed_identifier
(identifier)
(identifier)))
(import_declaration
(identifier)
(identifier)
(as_renamed_identifier
(identifier)
(wildcard))))
================================
Imports: Rename
================================
import lang.System.{lineSeparator => EOL}
import lang.System.{lineSeparator => _}
---
(compilation_unit
(import_declaration
(stable_identifier (identifier) (identifier))
(import_selectors (renamed_identifier (identifier) (identifier)))))
(identifier)
(identifier)
(namespace_selectors
(arrow_renamed_identifier
(identifier)
(identifier))))
(import_declaration
(identifier)
(identifier)
(namespace_selectors
(arrow_renamed_identifier
(identifier)
(wildcard)))))
=================================
@ -166,7 +352,10 @@ object O3 extends A {
Class definitions
=======================================
class C[T, U] {
class C[
T,
U,
] {
}
---
@ -192,6 +381,10 @@ object C {
with C[T]
}
class D(c: String) extends E(c) with F
class MyClass extends Potato() with Tomato
---
(compilation_unit
@ -217,15 +410,48 @@ object C {
(identifier)
(template_body
(class_definition (identifier) (extends_clause
(compound_type
(generic_type (type_identifier) (type_arguments (type_identifier)))
(generic_type (type_identifier) (type_arguments (type_identifier)))))))))
(generic_type (type_identifier) (type_arguments (type_identifier)))
(generic_type (type_identifier) (type_arguments (type_identifier)))))))
(class_definition
(identifier)
(class_parameters
(class_parameter (identifier) (type_identifier)))
(extends_clause
(type_identifier) (arguments (identifier))
(type_identifier)))
(class_definition
(identifier)
(extends_clause
(type_identifier) (arguments)
(type_identifier))))
=======================================
Subclass definitions (Scala 3 syntax)
=======================================
class A extends B, C:
1
end A
---
(compilation_unit
(class_definition
(identifier)
(extends_clause (type_identifier) (type_identifier))
(template_body (integer_literal))))
=======================================
Class definitions with parameters
=======================================
class Point(val x: Int, val y: Int)(implicit coord: Coord)
class Point(
val x: Int,
val y: Int,
)(implicit coord: Coord)
// TODO: The last argument should become class_parameters
class A @Inject()(x: Int, y: Int)
---
@ -236,7 +462,70 @@ class Point(val x: Int, val y: Int)(implicit coord: Coord)
(class_parameter (identifier) (type_identifier))
(class_parameter (identifier) (type_identifier)))
(class_parameters
(class_parameter (identifier) (type_identifier)))))
(class_parameter (identifier) (type_identifier))))
(comment)
(class_definition
(identifier)
(annotation
(type_identifier)
(arguments)
(arguments
(ascription_expression (identifier) (type_identifier))
(ascription_expression (identifier) (type_identifier))))))
=======================================
Class definitions with parameters (Scala 3 syntax)
=======================================
class Point(val x: Int, val y: Int)(using coord: Coord)
// TODO: The last argument should become class_parameters
class A @ann (x: Int, y: Int)
// TODO: The last argument should become class_parameters
class A @ann(1) (x: Int, y: Int)
// TODO: The last argument should become class_parameters
class A @ann(1)(1) (x: Int, y: Int)
---
(compilation_unit
(class_definition
(identifier)
(class_parameters
(class_parameter (identifier) (type_identifier))
(class_parameter (identifier) (type_identifier)))
(class_parameters
(class_parameter (identifier) (type_identifier))))
(comment)
(class_definition
(identifier)
(annotation
(type_identifier)
(arguments
(ascription_expression (identifier) (type_identifier))
(ascription_expression (identifier) (type_identifier)))))
(comment)
(class_definition
(identifier)
(annotation
(type_identifier)
(arguments (integer_literal))
(arguments
(ascription_expression (identifier) (type_identifier))
(ascription_expression (identifier) (type_identifier)))))
(comment)
(class_definition
(identifier)
(annotation
(type_identifier)
(arguments (integer_literal))
(arguments (integer_literal))
(arguments
(ascription_expression (identifier) (type_identifier))
(ascription_expression (identifier) (type_identifier)))))
)
=======================================
Modifiers
@ -291,7 +580,7 @@ trait T[U] extends V.W[U] {
(extends_clause (type_identifier)))
(trait_definition
(identifier)
(extends_clause (compound_type (type_identifier) (type_identifier))))
(extends_clause (type_identifier) (type_identifier)))
(trait_definition
(identifier)
(type_parameters (identifier))
@ -315,6 +604,28 @@ class A {
---
(compilation_unit
(class_definition
(identifier)
(template_body
(val_declaration
(identifier)
(identifier)
(type_identifier))
(val_declaration
(identifier)
(type_identifier)))))
=======================================
Value declarations (Scala 3 syntax)
=======================================
class A:
val b, c : Int
val d : String
---
(compilation_unit
(class_definition
(identifier)
@ -392,6 +703,35 @@ class A {
(type_identifier)
(integer_literal)))))
=======================================
Variable definitions (Scala 3 syntax)
=======================================
class A:
var b: Int = 1
var c: Int =
val d = 2
d
---
(compilation_unit
(class_definition
(identifier)
(template_body
(var_definition
(identifier)
(type_identifier)
(integer_literal))
(var_definition
(identifier)
(type_identifier)
(indented_block
(val_definition
(identifier)
(integer_literal))
(identifier))))))
=======================================
Type definitions
=======================================
@ -399,6 +739,8 @@ Type definitions
class A {
type B = C
type D[E] = F
type Abs
type Beta[B]
}
---
@ -413,7 +755,13 @@ class A {
(type_definition
(type_identifier)
(type_parameters (identifier))
(type_identifier)))))
(type_identifier))
(type_definition
(type_identifier))
(type_definition
(type_identifier)
(type_parameters
(identifier))))))
=======================================
Function declarations
@ -445,11 +793,19 @@ Function definitions
=======================================
class A {
def b(c: D, e: F) = 1
def b(
c: D,
e: F,
) = 1
def g[H](i) {
j
}
def h(x: T)(implicit ev: Reads[T])
def l: Int =
1
def m = ()
}
---
@ -472,7 +828,153 @@ class A {
(function_declaration
(identifier)
(parameters (parameter (identifier) (type_identifier)))
(parameters (parameter (identifier) (generic_type (type_identifier) (type_arguments (type_identifier)))))))))
(parameters (parameter (identifier) (generic_type (type_identifier) (type_arguments (type_identifier))))))
(function_definition (identifier) (type_identifier) (indented_block (integer_literal)))
(function_definition (identifier) (unit)))))
=======================================
Function definitions (Scala 3 syntax)
=======================================
class A:
def foo(c: C): Int =
val x = 1
val y = 2
x + y
---
(compilation_unit
(class_definition
(identifier)
(template_body
(function_definition
(identifier)
(parameters
(parameter (identifier) (type_identifier)))
(type_identifier)
(indented_block
(val_definition (identifier) (integer_literal)) (val_definition (identifier) (integer_literal)) (infix_expression (identifier) (operator_identifier) (identifier)))))))
=======================================
Extension methods (Scala 3 syntax)
=======================================
object A:
extension (c: C)
def foo: Int = 1
extension [A1](d: D) def foo = "foo"
---
(compilation_unit
(object_definition
(identifier)
(template_body
(extension_definition
(parameters
(parameter (identifier) (type_identifier)))
(function_definition
(identifier)
(type_identifier)
(integer_literal)))
(extension_definition
(type_parameters (identifier))
(parameters
(parameter (identifier) (type_identifier)))
(function_definition
(identifier)
(string))))))
=======================================
Given instance definitions (Scala 3 syntax)
=======================================
object A:
given a: A = x
given intFoo: CanFoo[Int] with
def foo(x: Int): Int = 0
private given listFoo[A1](using ev: CanFoo[A1]): CanFoo[List[A1]] with
def foo(xs: List[A1]): Int = 0
given Context = ctx
---
(compilation_unit
(object_definition
(identifier)
(template_body
(given_definition
(identifier)
(type_identifier)
(identifier))
(given_definition
(identifier)
(generic_type
(type_identifier)
(type_arguments (type_identifier)))
(with_template_body
(function_definition
(identifier)
(parameters
(parameter (identifier) (type_identifier)))
(type_identifier)
(integer_literal)
)))
(given_definition
(modifiers (access_modifier))
(identifier)
(type_parameters (identifier))
(parameters
(parameter
(identifier)
(generic_type (type_identifier) (type_arguments (type_identifier)))))
(generic_type
(type_identifier)
(type_arguments
(generic_type (type_identifier) (type_arguments (type_identifier)))))
(with_template_body
(function_definition
(identifier)
(parameters
(parameter
(identifier)
(generic_type (type_identifier) (type_arguments (type_identifier)))))
(type_identifier)
(integer_literal))))
(given_definition
(type_identifier)
(identifier)))))
=======================================
Top-level Definitions (Scala 3 syntax)
=======================================
class A:
def a() =
()
def a() = 1
---
(compilation_unit
(class_definition
(identifier)
(template_body
(function_definition
(identifier)
(parameters)
(indented_block
(unit)))))
(function_definition
(identifier)
(parameters)
(integer_literal)))
=======================================
Initialization expressions
@ -510,3 +1012,257 @@ def mkLines(header: String, indented: Boolean = false, repeated: Long = 0L): Str
(parameter (identifier) (type_identifier) (boolean_literal))
(parameter (identifier) (type_identifier) (integer_literal)))
(type_identifier) (block)))
===================================
Enums (Scala 3)
===================================
enum Hello[Y] extends java.Enumeration derives Codec, Eq {
case World, You
case Test[A](bla: Int, yo: String) extends Hello[A]
case T extends Hello[String](25)
}
---
(compilation_unit
(enum_definition
(identifier)
(type_parameters (identifier))
(extends_clause (stable_type_identifier (identifier) (type_identifier)))
(derives_clause (type_identifier) (type_identifier))
(enum_body
(enum_case_definitions
(simple_enum_case (identifier))
(simple_enum_case (identifier)))
(enum_case_definitions
(full_enum_case
(identifier)
(type_parameters (identifier))
(class_parameters
(class_parameter
(identifier)
(type_identifier))
(class_parameter
(identifier)
(type_identifier)))
(extends_clause
(generic_type (type_identifier) (type_arguments (type_identifier)))))
)
(enum_case_definitions
(simple_enum_case
(identifier)
(extends_clause
(generic_type
(type_identifier)
(type_arguments
(type_identifier)))
(arguments
(integer_literal)))))
)
)
)
================================
Self types
================================
trait A {
self =>
def f: Int
}
class B {
self: Something[A] =>
case class Hello(a: Int)
}
---
(compilation_unit
(trait_definition
(identifier)
(template_body
(self_type
(identifier))
(function_declaration
(identifier)
(type_identifier))))
(class_definition
(identifier)
(template_body
(self_type
(identifier)
(generic_type
(type_identifier)
(type_arguments
(type_identifier))))
(class_definition
(identifier)
(class_parameters
(class_parameter
(identifier)
(type_identifier)))))))
=======================================
Inline methods and parameters (Scala 3)
=======================================
inline def mkLines(inline header: String, indented: Boolean = false): String = {}
---
(compilation_unit
(function_definition
(modifiers
(inline_modifier))
(identifier)
(parameters
(parameter (inline_modifier) (identifier) (type_identifier))
(parameter (identifier) (type_identifier) (boolean_literal)))
(type_identifier) (block)))
=======================================
Inline val (Scala 3)
=======================================
inline def test() =
inline val x = true
---
(compilation_unit
(function_definition
(modifiers
(inline_modifier))
(identifier)
(parameters)
(indented_block
(val_definition
(modifiers
(inline_modifier))
(identifier)
(boolean_literal)
)
)))
=======================================
Inline given (Scala 3)
=======================================
inline given Test =
new Test
---
(compilation_unit
(given_definition
(modifiers (inline_modifier))
(type_identifier)
(instance_expression (type_identifier))))
=======================================
Infix methods (Scala 3)
=======================================
object Test:
infix private def hello = 25
---
(compilation_unit
(object_definition
(identifier)
(template_body
(function_definition
(modifiers
(infix_modifier)
(access_modifier))
(identifier)
(integer_literal)))))
=======================================
Open classes (Scala 3)
=======================================
open class Test(a: Int):
def test = 25
end Test
---
(compilation_unit
(class_definition
(modifiers
(open_modifier))
(identifier)
(class_parameters
(class_parameter
(identifier)
(type_identifier)))
(template_body
(function_definition
(identifier)
(integer_literal)))))
=======================================
Transparent traits (Scala 3)
=======================================
transparent trait Kind:
def test = 1
---
(compilation_unit
(trait_definition
(modifiers
(transparent_modifier))
(identifier)
(template_body
(function_definition
(identifier)
(integer_literal)))))
================================
Exports (Scala 3)
================================
export scanUnit.scan
export printUnit.{status as _, *}
export printUnit.Test as Hello
export printUnit.Test as _
---
(compilation_unit
(export_declaration
(identifier)
(identifier))
(export_declaration
(identifier)
(namespace_selectors
(as_renamed_identifier
(identifier)
(wildcard))
(namespace_wildcard)))
(export_declaration
(identifier)
(as_renamed_identifier
(identifier)
(identifier)))
(export_declaration
(identifier)
(as_renamed_identifier
(identifier)
(wildcard)))
)

@ -5,7 +5,11 @@ Call expressions
class C {
def main() {
a()
a(123)
a(
123,
234,
)
::(123)
a(b, c)
a.map(x => x + 1)
a { 123 }
@ -26,7 +30,8 @@ class C {
(parameters)
(block
(call_expression (identifier) (arguments))
(call_expression (identifier) (arguments (integer_literal)))
(call_expression (identifier) (arguments (integer_literal) (integer_literal)))
(call_expression (operator_identifier) (arguments (integer_literal)))
(call_expression (identifier) (arguments (identifier) (identifier)))
(call_expression (field_expression (identifier) (identifier)) (arguments
(lambda_expression (identifier)
@ -39,13 +44,91 @@ class C {
(call_expression (field_expression (identifier) (identifier)) (case_block (case_clause
(tuple_pattern (tuple_pattern (identifier) (wildcard))) (identifier)))))))))
===============================
SIP-44 Fewer braces (Scala 3 syntax)
===============================
class C:
xs.map: x =>
x + 1
xs.map:
x =>
x + 1
xs:
println("")
xs `++`:
val x = 1
List(x)
xs.map:
case x => x
// This is ascription
xs: Int
// This is call_expression
xs:
Int
---
(compilation_unit
(class_definition
(identifier)
(template_body
(call_expression
(field_expression (identifier) (identifier))
(colon_argument
(identifier)
(indented_block (infix_expression (identifier) (operator_identifier) (integer_literal)))))
(call_expression
(field_expression (identifier) (identifier))
(colon_argument
(indented_block (lambda_expression (identifier)
(infix_expression (identifier) (operator_identifier) (integer_literal))))))
(call_expression
(identifier)
(colon_argument
(indented_block (call_expression
(identifier)
(arguments (string))))))
(infix_expression
(identifier)
(identifier)
(colon_argument (indented_block
(val_definition
(identifier)
(integer_literal))
(call_expression (identifier) (arguments (identifier))))))
(call_expression
(field_expression (identifier) (identifier))
(colon_argument
(indented_cases (case_clause
(identifier)
(identifier)))))
(comment)
(ascription_expression (identifier) (type_identifier))
(comment)
(call_expression (identifier) (colon_argument (indented_block (identifier)))))))
===============================
Generic functions
===============================
class C {
def main() {
a[T]()
a[
A1,
A2,
]()
List[Traversable[ClassPath]]()
}
}
@ -63,7 +146,7 @@ class C {
(call_expression
(generic_function
(identifier)
(type_arguments (type_identifier)))
(type_arguments (type_identifier) (type_identifier)))
(arguments))
(call_expression
(generic_function
@ -155,6 +238,51 @@ def other() {
(block (identifier))
(call_expression (identifier) (arguments))))))
===============================
If expressions (Scala 3 syntax)
===============================
class C:
def main() =
if a then b()
end if
if
val c = false
c
then
d()
1
else if f then
g()
else
h()
---
(compilation_unit
(class_definition
(identifier)
(template_body
(function_definition
(identifier)
(parameters)
(indented_block
(if_expression
(identifier)
(call_expression (identifier) (arguments)))
(if_expression
(indented_block
(val_definition (identifier) (boolean_literal))
(identifier))
(indented_block
(call_expression (identifier) (arguments))
(integer_literal))
(if_expression
(identifier)
(indented_block (call_expression (identifier) (arguments)))
(indented_block (call_expression (identifier) (arguments))))))))))
===============================
Try expressions
===============================
@ -203,7 +331,7 @@ def main() {
(case_class_pattern
(type_identifier)
(identifier))
(throw_expression (instance_expression (call_expression (identifier) (arguments (identifier))))
(throw_expression (instance_expression (type_identifier) (arguments (identifier)))
)
)
)
@ -220,6 +348,64 @@ def main() {
(try_expression (call_expression (identifier) (arguments))
(finally_clause (call_expression (identifier) (arguments)))))))
===============================
Try expressions (Scala 3 syntax)
===============================
def main(): Unit =
try a() finally depth -= 1
try
a()
b()
catch
case e: SomeException => prinlnt(e.message)
case NonFatal(ex) => throw new SomeException(ex)
finally
tryAnotherThing()
tryAnotherThing2()
---
(compilation_unit
(function_definition
(identifier)
(parameters)
(type_identifier)
(indented_block
(try_expression
(call_expression (identifier) (arguments))
(finally_clause (infix_expression (identifier) (operator_identifier) (integer_literal))))
(try_expression
(indented_block
(call_expression (identifier) (arguments))
(call_expression (identifier) (arguments)))
(catch_clause
(indented_cases
(case_clause
(typed_pattern
(identifier)
(type_identifier))
(call_expression
(identifier)
(arguments (field_expression
(identifier)
(identifier)))))
(case_clause
(case_class_pattern
(type_identifier)
(identifier))
(throw_expression (instance_expression (type_identifier) (arguments (identifier)))
)
)
)
)
(finally_clause
(indented_block
(call_expression (identifier) (arguments))
(call_expression (identifier) (arguments)))))
)))
===============================
Match expressions
===============================
@ -231,7 +417,11 @@ def matchTest(x: Int): String = x match {
case 3 => {
"3"
}
case A if a == 1 =>
case A if a == 2 => 2
case ((i, _)) => i
case s"$l1 -- $l2" =>
l1 + l2
case _ =>
val x = "many"
"more"
@ -249,9 +439,60 @@ def matchTest(x: Int): String = x match {
(case_clause (integer_literal) (string) (string))
(case_clause (integer_literal) (string))
(case_clause (integer_literal) (block (string)))
(case_clause (identifier) (guard (infix_expression (identifier) (operator_identifier) (integer_literal))))
(case_clause (identifier) (guard (infix_expression (identifier) (operator_identifier) (integer_literal))) (integer_literal))
(case_clause (tuple_pattern (tuple_pattern (identifier) (wildcard))) (identifier))
(case_clause
(interpolated_string_expression
(identifier)
(interpolated_string
(interpolation
(identifier))
(interpolation
(identifier))))
(infix_expression
(identifier)
(operator_identifier)
(identifier)))
(case_clause (wildcard) (val_definition (identifier) (string)) (string))))))
===============================
Match expressions (Scala 3 syntax)
===============================
def matchTest(x: Int): String =
x match
case 0 =>
case 1 => "one"; "uno"
case 2 => "two"
case x if x == 3 =>
val x = "3"
x
case ((i, _)) => i
case _ =>
val x = "many"
"more"
---
(compilation_unit
(function_definition
(identifier)
(parameters (parameter (identifier) (type_identifier)))
(type_identifier)
(indented_block
(match_expression (identifier) (indented_cases
(case_clause (integer_literal))
(case_clause (integer_literal) (string) (string))
(case_clause (integer_literal) (string))
(case_clause
(identifier)
(guard (infix_expression (identifier) (operator_identifier) (integer_literal)))
(val_definition (identifier) (string))
(identifier))
(case_clause (tuple_pattern (tuple_pattern (identifier) (wildcard))) (identifier))
(case_clause (wildcard) (val_definition (identifier) (string)) (string)))))))
===============================
Field expressions
===============================
@ -286,8 +527,17 @@ Instance expressions
class C {
def main() {
a = new B
c = new D(e, f)
val a = new B
val c = new D(e, f)
val e = new E:
def app = 1
val f = new:
def app = 1
val g = new G {}
val i = new {
"ok"
}
new Array: Array
}
}
@ -301,8 +551,29 @@ class C {
(identifier)
(parameters)
(block
(assignment_expression (identifier) (instance_expression (identifier)))
(assignment_expression (identifier) (instance_expression (call_expression (identifier) (arguments (identifier) (identifier))))))))))
(val_definition
(identifier)
(instance_expression (type_identifier)))
(val_definition
(identifier)
(instance_expression (type_identifier) (arguments (identifier) (identifier))))
(val_definition
(identifier)
(instance_expression (type_identifier) (template_body
(function_definition (identifier) (integer_literal)))))
(val_definition
(identifier)
(instance_expression (template_body
(function_definition (identifier) (integer_literal)))))
(val_definition
(identifier)
(instance_expression (type_identifier) (template_body)))
(val_definition
(identifier)
(instance_expression (template_body (string))))
(ascription_expression
(instance_expression (type_identifier))
(type_identifier)))))))
===============================
Infix expressions
@ -340,7 +611,6 @@ Prefix expressions
class C {
def main() {
!a
!!a
+a + b
}
}
@ -356,7 +626,6 @@ class C {
(parameters)
(block
(prefix_expression (identifier))
(prefix_expression (prefix_expression (identifier)))
(infix_expression
(prefix_expression (identifier))
(operator_identifier)
@ -400,6 +669,7 @@ object O {
val x = 2: Byte
Nil: List[String]
3: _*
x: @unchecked
}
---
@ -418,7 +688,9 @@ object O {
(generic_type (type_identifier)
(type_arguments (type_identifier))))
(ascription_expression (integer_literal)
(repeated_parameter_type (type_identifier))))))
(repeated_parameter_type (wildcard)))
(ascription_expression (identifier)
(annotation (type_identifier))))))
================================================================================
Lambda Expression
@ -436,7 +708,8 @@ object O {
(compilation_unit
(object_definition (identifier)
(template_body (val_definition
(template_body
(val_definition
(identifier) (lambda_expression
(identifier) (infix_expression
(identifier) (operator_identifier) (integer_literal))))
@ -447,7 +720,7 @@ object O {
(block (infix_expression
(identifier) (operator_identifier) (identifier)))))
(val_definition (identifier)
(lambda_expression (identifier) (integer_literal)))
(lambda_expression (wildcard) (integer_literal)))
(lambda_expression
(bindings
(binding (identifier))
@ -507,8 +780,8 @@ While loops
===============================
def f = {
while(a) g()
while(b < c) {
while (a) g()
while (b < c) {
d
}
}
@ -526,6 +799,36 @@ def f = {
(parenthesized_expression (infix_expression (identifier) (operator_identifier) (identifier)))
(block (identifier))))))
===============================
While loops (Scala 3 syntax)
===============================
def f: Unit =
while a do g()
while
val x = 1
b < x
do
()
---
(compilation_unit
(function_definition
(identifier)
(type_identifier)
(indented_block
(while_expression
(identifier)
(call_expression (identifier) (arguments)))
(while_expression
(indented_block
(val_definition (identifier) (integer_literal))
(infix_expression (identifier) (operator_identifier) (identifier)))
(indented_block
(unit))))))
===============================
Do-while loops
===============================
@ -594,6 +897,48 @@ def f() = {
(enumerator (tuple_pattern (identifier) (identifier)) (identifier)))
(block (call_expression (identifier) (arguments (identifier) (identifier))))))))
===============================
For comprehensions (Scala 3 syntax)
===============================
def f(): Unit =
for n <- nums yield n + 1
for
x <- xs
y <- ys
yield
g(x, y)
for
annot <- annotations
if shouldEmitAnnotation(annot)
do
()
---
(compilation_unit
(function_definition
(identifier)
(parameters)
(type_identifier)
(indented_block
(for_expression
(enumerators (enumerator (identifier) (identifier)))
(infix_expression (identifier) (operator_identifier) (integer_literal)))
(for_expression
(enumerators
(enumerator (identifier) (identifier))
(enumerator (identifier) (identifier)))
(indented_block
(call_expression (identifier) (arguments (identifier) (identifier)))))
(for_expression
(enumerators
(enumerator (identifier) (identifier))
(enumerator (guard (call_expression (identifier) (arguments (identifier))))))
(indented_block (unit))))))
===============================
Chained expressions
===============================
@ -623,9 +968,118 @@ def main() {
(call_expression
(field_expression (identifier) (identifier))
(arguments (infix_expression
(identifier) (operator_identifier) (integer_literal))))
(wildcard) (operator_identifier) (integer_literal))))
(call_expression
(field_expression (identifier) (identifier))
(arguments (infix_expression
(identifier) (operator_identifier) (integer_literal))))
(wildcard) (operator_identifier) (integer_literal))))
(field_expression (identifier) (identifier)))))
=======================================
Macros (Scala 3 syntax)
=======================================
class A:
inline def inspect(inline a: Any): Any =
${ inspectCode('a) }
def foo =
'{ $b }
---
(compilation_unit
(class_definition
(identifier)
(template_body
(function_definition
(modifiers (inline_modifier))
(identifier)
(parameters
(parameter (inline_modifier) (identifier) (type_identifier)))
(type_identifier)
(indented_block
(splice_expression
(call_expression
(identifier)
(arguments (quote_expression (identifier)))))))
(function_definition
(identifier)
(indented_block
(quote_expression
(identifier))))
)))
=======================================
Inline matches (Scala 3)
=======================================
def hello =
inline c match {
case 1 =>
}
---
(compilation_unit
(function_definition
(identifier)
(indented_block
(match_expression
(inline_modifier)
(identifier)
(case_block
(case_clause
(integer_literal)))))))
=======================================
Inline if (Scala 3)
=======================================
def hello =
inline if (x) {} else {}
---
(compilation_unit
(function_definition
(identifier)
(indented_block
(if_expression
(inline_modifier)
(parenthesized_expression
(identifier))
(block)
(block)))))
=======================================
Nested inline if and match (Scala 3)
=======================================
def hello =
inline if (x) {
inline x match {
}
}
---
(compilation_unit
(function_definition
(identifier)
(indented_block
(if_expression
(inline_modifier)
(parenthesized_expression
(identifier))
(block
(match_expression
(inline_modifier)
(identifier)
(case_block)))))))

@ -152,24 +152,26 @@ def foo(a: Char = 'c') = a + 'd'
(character_literal))))
==========================
Symbol literals
Null
==========================
val mySymbol = 'c
val myOtherSymbol = 'thing
lazy val nullObject: String = null
---
(compilation_unit
(val_definition (identifier) (symbol_literal))
(val_definition (identifier) (symbol_literal)))
(compilation_unit (val_definition (modifiers) (identifier) (type_identifier) (null_literal)))
==========================
Null
Tuple literals
==========================
lazy val nullObject: String = null
val x = (
1,
2,
3,
)
---
(compilation_unit (val_definition (modifiers) (identifier) (type_identifier) (null_literal)))
(compilation_unit
(val_definition (identifier)
(tuple_expression (integer_literal) (integer_literal) (integer_literal))))

@ -169,3 +169,26 @@ val x = y match {
(tuple_pattern (alternative_pattern (identifier) (identifier))))
(operator_identifier) (identifier)) (operator_identifier) (string))
(integer_literal))))))
============================
Quoted patterns (Scala 3 syntax)
============================
def foo =
x match
case '{ $boolExpr } => Some(true)
case _ => None
---
(compilation_unit
(function_definition (identifier)
(indented_block
(match_expression (identifier)
(indented_cases
(case_clause
(quote_expression (identifier))
(call_expression (identifier) (arguments (boolean_literal))))
(case_clause (wildcard)
(identifier)))))))

@ -5,6 +5,7 @@ Stable type identifiers
object Main {
type A = B.C
type D = E.F.G
type H = __root__.scala.Int
}
---
@ -14,6 +15,9 @@ object Main {
(type_definition
(type_identifier)
(stable_type_identifier (identifier) (type_identifier)))
(type_definition
(type_identifier)
(stable_type_identifier (stable_identifier (identifier) (identifier)) (type_identifier)))
(type_definition
(type_identifier)
(stable_type_identifier (stable_identifier (identifier) (identifier)) (type_identifier))))))
@ -23,7 +27,10 @@ Generic types
===================================
object Main {
type A = B[C, D]
type A = B[
C,
D,
]
type E = F.G[H]
}
@ -48,12 +55,21 @@ Tuple types
object Main {
type A = (B, C)
type A = (
B,
C,
)
}
---
(compilation_unit
(object_definition (identifier) (template_body
(type_definition
(type_identifier)
(tuple_type
(type_identifier)
(type_identifier)))
(type_definition
(type_identifier)
(tuple_type
@ -92,6 +108,20 @@ object Main {
(parameter_types (type_identifier))
(tuple_type (type_identifier) (type_identifier)))))))
===================================
Context function types (Scala 3 syntax)
===================================
type Executable[A] = ExecutionContext ?=> A
---
(compilation_unit
(type_definition (type_identifier) (type_parameters (identifier))
(function_type
(parameter_types (type_identifier))
(type_identifier))))
==================================
Compound types
==================================
@ -110,7 +140,7 @@ class F extends Cloneable with Resetable with Serializable {}
(type_identifier) (block))
(class_definition (identifier)
(extends_clause
(compound_type (type_identifier) (type_identifier) (type_identifier)))
(type_identifier) (type_identifier) (type_identifier))
(template_body)))
==================================
@ -250,3 +280,144 @@ type A = B with B1 with B2 ! C with C1
(compound_type (type_identifier) (type_identifier) (type_identifier))
(operator_identifier)
(compound_type (type_identifier) (type_identifier)))))
==================================
Literal type aliases (Scala 2.13+)
==================================
type A = "hello"
type B = 25
type C = false
type D = 0.5f
class Test(d: "hello", b: 25)
---
(compilation_unit
(type_definition
(type_identifier) (literal_type (string)))
(type_definition
(type_identifier) (literal_type (integer_literal)))
(type_definition
(type_identifier) (literal_type (boolean_literal)))
(type_definition
(type_identifier) (literal_type (floating_point_literal)))
(class_definition
(identifier)
(class_parameters
(class_parameter
(identifier)
(literal_type
(string)))
(class_parameter
(identifier)
(literal_type
(integer_literal))))))
==================================
Singleton Types
==================================
class A:
def foobar: this.type = this
type X = A.B.type
---
(compilation_unit
(class_definition
(identifier)
(template_body
(function_definition (identifier) (singleton_type (identifier)) (identifier))
(type_definition (type_identifier) (singleton_type (stable_identifier (identifier) (identifier)))))))
==================================
Opaque type aliases (Scala 3)
==================================
opaque type A = Int
private opaque type B = String
opaque type B <: Test >: Help = String
---
(compilation_unit
(type_definition
(opaque_modifier) (type_identifier) (type_identifier))
(type_definition
(modifiers (access_modifier))
(opaque_modifier)
(type_identifier)
(type_identifier))
(type_definition
(opaque_modifier)
(type_identifier)
(upper_bound (type_identifier))
(lower_bound (type_identifier))
(type_identifier))
)
==================================
Structural type
==================================
type A = { def fly(): Unit }
---
(compilation_unit
(type_definition (type_identifier)
(structural_type
(function_declaration
(identifier)
(parameters)
(type_identifier)
)
)
)
)
=========================================
Anonymous structural type with projection
=========================================
type A = B[({ type f[x] = M[S, x] })#f]
---
(compilation_unit
(type_definition (type_identifier)
(generic_type
(type_identifier)
(type_arguments
(projected_type
(tuple_type
(structural_type
(type_definition
(type_identifier)
(type_parameters
(identifier)
)
(generic_type
(type_identifier)
(type_arguments
(type_identifier)
(type_identifier)
)
)
)
)
)
(type_identifier)
)
)
)
)
)

@ -0,0 +1,23 @@
let
nixpkgs = fetchTarball {
name = "nixpkgs";
url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz";
sha256 = "1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36";
};
pkgs = import nixpkgs { };
in
pkgs.mkShell {
name = "env";
buildInputs = with pkgs; [
nodejs
gcc
clang
libiconv
];
shellHook = ''
PATH=./node_modules/.bin:$PATH
command -v tree-sitter >/dev/null 2>&1 || npm install
'';
}

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "tree-sitter-scala",
"version": "0.19.0",
"version": "0.20.7",
"description": "Scala grammar for tree-sitter",
"main": "bindings/node",
"keywords": [
@ -13,7 +13,7 @@
"nan": "^2.14.1"
},
"devDependencies": {
"tree-sitter-cli": "^0.19.5"
"tree-sitter-cli": "^0.20.7"
},
"scripts": {
"build": "tree-sitter generate && node-gyp build",
@ -24,7 +24,8 @@
"scope": "source.scala",
"file-types": [
"scala"
]
],
"highlights": "queries/scala/highlights.scm"
}
]
}

@ -1,74 +0,0 @@
;; Annotations
"@" @operator
;; Types
(class_definition
name: (identifier) @type)
(trait_definition
name: (identifier) @type)
(object_definition
name: (identifier) @type)
(type_identifier) @type
;; Variables
((identifier) @constant
(#match? @constant "^_*[A-Z][A-Z\\d_]+$"))
(identifier) @variable
;; Literals
[
(integer_literal)
(floating_point_literal)
] @number
[
(character_literal)
(string)
] @string
[
(boolean_literal)
(null_literal)
] @constant.builtin
(comment) @comment
;; Keywords
[
"abstract"
"case"
"catch"
"class"
"def"
"do"
"else"
"extends"
"final"
"finally"
"for"
"if"
"implicit"
"import"
"lazy"
"match"
"new"
"object"
"override"
"package"
"private"
"protected"
"return"
"sealed"
"throw"
"trait"
"try"
"while"
"with"
] @keyword

@ -0,0 +1,251 @@
; CREDITS @stumash (stuart.mashaal@gmail.com)
(class_definition
name: (identifier) @type)
(enum_definition
name: (identifier) @type)
(object_definition
name: (identifier) @type)
(trait_definition
name: (identifier) @type)
(full_enum_case
name: (identifier) @type)
(simple_enum_case
name: (identifier) @type)
;; variables
(class_parameter
name: (identifier) @parameter)
(interpolation) @none
;; types
(type_definition
name: (type_identifier) @type.definition)
(type_identifier) @type
;; val/var definitions/declarations
(val_definition
pattern: (identifier) @variable)
(var_definition
pattern: (identifier) @variable)
(val_declaration
name: (identifier) @variable)
(var_declaration
name: (identifier) @variable)
; method definition
(function_declaration
name: (identifier) @method)
(function_definition
name: (identifier) @method)
; imports/exports
(import_declaration
path: (identifier) @namespace)
((stable_identifier (identifier) @namespace))
((import_declaration
path: (identifier) @type) (#lua-match? @type "^[A-Z]"))
((stable_identifier (identifier) @type) (#lua-match? @type "^[A-Z]"))
(export_declaration
path: (identifier) @namespace)
((stable_identifier (identifier) @namespace))
((export_declaration
path: (identifier) @type) (#lua-match? @type "^[A-Z]"))
((stable_identifier (identifier) @type) (#lua-match? @type "^[A-Z]"))
((namespace_selectors (identifier) @type) (#lua-match? @type "^[A-Z]"))
; method invocation
(call_expression
function: (identifier) @function.call)
(call_expression
function: (operator_identifier) @function.call)
(call_expression
function: (field_expression
field: (identifier) @method.call))
((call_expression
function: (identifier) @constructor)
(#lua-match? @constructor "^[A-Z]"))
(generic_function
function: (identifier) @function.call)
(interpolated_string_expression
interpolator: (identifier) @function.call)
; function definitions
(function_definition
name: (identifier) @function)
(parameter
name: (identifier) @parameter)
(binding
name: (identifier) @parameter)
; expressions
(field_expression field: (identifier) @property)
(field_expression value: (identifier) @type
(#lua-match? @type "^[A-Z]"))
(infix_expression operator: (identifier) @operator)
(infix_expression operator: (operator_identifier) @operator)
(infix_type operator: (operator_identifier) @operator)
(infix_type operator: (operator_identifier) @operator)
; literals
(boolean_literal) @boolean
(integer_literal) @number
(floating_point_literal) @float
[
(symbol_literal)
(string)
(character_literal)
(interpolated_string_expression)
] @string
(interpolation "$" @punctuation.special)
;; keywords
(opaque_modifier) @type.qualifier
(infix_modifier) @keyword
(transparent_modifier) @type.qualifier
(open_modifier) @type.qualifier
[
"case"
"class"
"enum"
"extends"
"derives"
"finally"
;; `forSome` existential types not implemented yet
;; `macro` not implemented yet
"object"
"override"
"package"
"trait"
"type"
"val"
"var"
"with"
"given"
"using"
"end"
"implicit"
"extension"
"with"
] @keyword
[
"abstract"
"final"
"lazy"
"sealed"
"private"
"protected"
] @type.qualifier
(inline_modifier) @storageclass
(null_literal) @constant.builtin
(wildcard) @parameter
(annotation) @attribute
;; special keywords
"new" @keyword.operator
[
"else"
"if"
"match"
"then"
] @conditional
[
"("
")"
"["
"]"
"{"
"}"
] @punctuation.bracket
[
"."
","
] @punctuation.delimiter
[
"do"
"for"
"while"
"yield"
] @repeat
"def" @keyword.function
[
"=>"
"<-"
"@"
] @operator
["import" "export"] @include
[
"try"
"catch"
"throw"
] @exception
"return" @keyword.return
(comment) @comment @spell
;; `case` is a conditional keyword in case_block
(case_block
(case_clause ("case") @conditional))
(operator_identifier) @operator
((identifier) @type (#lua-match? @type "^[A-Z]"))
((identifier) @variable.builtin
(#lua-match? @variable.builtin "^this$"))
(
(identifier) @function.builtin
(#lua-match? @function.builtin "^super$")
)

@ -0,0 +1,53 @@
#!/bin/bash -e
# This is an integration test to generally check the quality of parsing.
SCALA_SCALA_LIBRARY_EXPECTED=100
SCALA_SCALA_COMPILER_EXPECTED=68
DOTTY_COMPILER_EXPECTED=65
if [ ! -d "$SCALA_SCALA_DIR" ]; then
echo "\$SCALA_SCALA_DIR must be set"
exit 1
fi
if [ ! -d "$DOTTY_DIR" ]; then
echo "\$DOTTY_DIR must be set"
exit 1
fi
failed=0
run_tree_sitter () {
local source_dir=$1
local expected=$2
local name=$3
cmd="npm exec -c 'tree-sitter parse $source_dir/**/*.scala --quiet --stat' | sort | sed 's%$source_dir%%g'"
echo
echo "Parse $source_dir: $cmd"
out=$((eval $cmd) || true)
if [ ! -e "$PRODUCE_REPORTS" ]; then
local report_file="report-$name.txt"
echo "$out" | sed G | sed -E 's/([0-9]+) ms//' | grep -v 'success percentage' > "report-$name.txt"
echo "Report written to $report_file"
fi
actual=$(echo "$out" | grep 'success percentage:' | rev | cut -d' ' -f1 | rev | sed 's/%//g' )
echo $actual
if (( $(echo "$actual >= $expected" |bc -l) )); then
# See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error
echo -e "::notice file=grammar.js,line=1::ok, ${source_dir}: ${actual}%, expected at least $expected%"
else
echo -e "::error file=grammar.js,line=1::${source_dir}: expected ${expected}, but got ${actual} instead"
failed=$((failed + 1))
fi
}
run_tree_sitter $SCALA_SCALA_DIR/src/library/ $SCALA_SCALA_LIBRARY_EXPECTED scala2-library
run_tree_sitter $SCALA_SCALA_DIR/src/compiler/ $SCALA_SCALA_COMPILER_EXPECTED scala2-compiler
run_tree_sitter $DOTTY_DIR/compiler/ $DOTTY_COMPILER_EXPECTED dotty-compiler
if (( $failed > 0 )); then
exit 1
fi

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,14 +1,19 @@
#include <tree_sitter/parser.h>
#include "stack.h"
#include "tree_sitter/parser.h"
#include <stdio.h>
#include <string.h>
#include <wctype.h>
enum TokenType {
AUTOMATIC_SEMICOLON,
SIMPLE_STRING,
SIMPLE_MULTILINE_STRING,
INDENT,
INTERPOLATED_STRING_MIDDLE,
INTERPOLATED_STRING_END,
INTERPOLATED_MULTILINE_STRING_MIDDLE,
INTERPOLATED_MULTILINE_STRING_END,
OUTDENT,
SIMPLE_MULTILINE_STRING,
SIMPLE_STRING,
ELSE,
CATCH,
FINALLY,
@ -16,11 +21,26 @@ enum TokenType {
WITH,
};
void *tree_sitter_scala_external_scanner_create() { return NULL; }
void tree_sitter_scala_external_scanner_destroy(void *p) {}
void tree_sitter_scala_external_scanner_reset(void *p) {}
unsigned tree_sitter_scala_external_scanner_serialize(void *p, char *buffer) { return 0; }
void tree_sitter_scala_external_scanner_deserialize(void *p, const char *b, unsigned n) {}
void *tree_sitter_scala_external_scanner_create() {
return createStack();
}
void tree_sitter_scala_external_scanner_destroy(void *p) {
free(p);
}
void tree_sitter_scala_external_scanner_reset(void *p) {
resetStack(p);
}
unsigned tree_sitter_scala_external_scanner_serialize(void *p, char *buffer) {
return serialiseStack(p, buffer);
}
void tree_sitter_scala_external_scanner_deserialize(void *p, const char *b,
unsigned n) {
deserialiseStack(p, b, n);
}
static void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
@ -70,11 +90,80 @@ static bool scan_string_content(TSLexer *lexer, bool is_multiline, bool has_inte
bool tree_sitter_scala_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
unsigned newline_count = 0;
ScannerStack *stack = (ScannerStack *)payload;
int prev = peekStack(stack);
int newline_count = 0;
int indentation_size = 0;
LOG("scanner was called at column: %d\n", lexer->get_column(lexer));
// Before advancing the lexer, check if we can double outdent
if (valid_symbols[OUTDENT] &&
(lexer->lookahead == 0 || (
stack->last_indentation_size != -1 &&
prev != -1 &&
stack->last_indentation_size < prev))) {
popStack(stack);
LOG(" pop\n");
LOG(" OUTDENT\n");
lexer->result_symbol = OUTDENT;
return true;
}
stack->last_indentation_size = -1;
while (iswspace(lexer->lookahead)) {
if (lexer->lookahead == '\n') newline_count++;
if (lexer->lookahead == '\n') {
newline_count++;
indentation_size = 0;
}
else
indentation_size++;
lexer->advance(lexer, true);
}
printStack(stack, " before");
if (valid_symbols[INDENT] &&
newline_count > 0 &&
(isEmptyStack(stack) ||
indentation_size > peekStack(stack))) {
pushStack(stack, indentation_size);
lexer->result_symbol = INDENT;
LOG(" INDENT\n");
return true;
}
// This saves the indentation_size and newline_count so it can be used
// in subsequent calls for multiple outdent or autosemicolon.
if (valid_symbols[OUTDENT] &&
(lexer->lookahead == 0 || (
newline_count > 0 &&
prev != -1 &&
indentation_size < prev))) {
popStack(stack);
LOG(" pop\n");
LOG(" OUTDENT\n");
lexer->result_symbol = OUTDENT;
stack->last_indentation_size = indentation_size;
stack->last_newline_count = newline_count;
if (lexer->eof(lexer)) {
stack->last_column = -1;
} else {
stack->last_column = lexer->get_column(lexer);
}
return true;
}
// Recover newline_count from the outdent reset
if (stack->last_newline_count > 0 &&
((lexer->eof(lexer) && stack->last_column == -1)
|| lexer->get_column(lexer) == stack->last_column)) {
newline_count += stack->last_newline_count;
}
stack->last_newline_count = 0;
printStack(stack, " after");
LOG(" indentation_size: %d, newline_count: %d, column: %d, indent_is_valid: %d, dedent_is_valid: %d\n", indentation_size,
newline_count, lexer->get_column(lexer), valid_symbols[INDENT], valid_symbols[OUTDENT]);
if (valid_symbols[AUTOMATIC_SEMICOLON] && newline_count > 0) {
// NOTE: When there's a dot after a new line it could be a multi-line field

@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#else
#define LOG(...)
#endif
// Total payload size is 1024 bytes max
#define STACK_SIZE 100
typedef struct ScannerStack {
int stack[STACK_SIZE];
int top;
int last_indentation_size;
int last_newline_count;
int last_column;
} ScannerStack;
ScannerStack* createStack() {
ScannerStack* ptr = (ScannerStack*) malloc(sizeof(ScannerStack));
ptr -> top = 0;
ptr -> last_indentation_size = -1;
ptr -> last_newline_count = 0;
ptr -> last_column = -1;
memset(ptr -> stack, STACK_SIZE, (0));
return ptr;
}
bool isEmptyStack(ScannerStack *stack) { return stack->top == 0; }
int peekStack(ScannerStack *stack) {
return isEmptyStack(stack) ? -1 : stack->stack[stack->top - 1];
}
void pushStack(ScannerStack *stack, unsigned int value) {
stack->top++;
stack->stack[stack->top - 1] = value;
}
int popStack(ScannerStack *stack) {
if (isEmptyStack(stack))
return -1;
else {
int result = peekStack(stack);
stack->top--;
return result;
}
}
void printStack(ScannerStack *stack, char *msg) {
LOG("%s Stack[top = %d; ", msg, stack->top);
for (int i = 0; i < stack->top; i++) {
LOG("%d | ", stack->stack[i]);
}
LOG("]\n");
}
unsigned serialiseStack(ScannerStack *stack, char *buf) {
int elements = isEmptyStack(stack) ? 0 : stack->top;
if (elements < 0) {
elements = 0;
}
unsigned result_length = (elements + 3) * sizeof(int);
int *placement = (int *)buf;
memcpy(placement, stack->stack, elements * sizeof(int));
placement[elements] = stack->last_indentation_size;
placement[elements + 1] = stack->last_newline_count;
placement[elements + 2] = stack->last_column;
return result_length;
}
void deserialiseStack(ScannerStack* stack, const char* buf, unsigned n) {
if (n != 0) {
int *intBuf = (int *)buf;
unsigned elements = n / sizeof(int) - 3;
stack->top = elements;
memcpy(stack->stack, intBuf, elements * sizeof(int));
stack->last_indentation_size = intBuf[elements];
stack->last_newline_count = intBuf[elements + 1];
stack->last_column = intBuf[elements + 2];
}
}
void resetStack(ScannerStack *p) {
p->top = 0;
p->last_indentation_size = -1;
p->last_newline_count = 0;
p->last_column = -1;
}

@ -123,6 +123,7 @@ struct TSLanguage {
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
const TSStateId *primary_state_ids;
};
/*

@ -0,0 +1,77 @@
import java.time.Instant
//^include
// ^namespace
// ^type
object Hello {
// ^ keyword
// ^ type
val x = if (true) (25 * 1.0) else "hello"
// ^keyword
// ^variable
// ^conditional
// ^boolean
// ^number
// ^float
// ^string
// ^conditional
val y = new lower_case_intentionally
// ^keyword.operator
// ^type
var mutation = "mutant"
// ^keyword
// ^variable
trait Test {
// ^ keyword
// ^ type
def meth(i: Int)(implicit x: Boolean) = ???
// ^keyword.function
// ^keyword
// ^type
// ^method
// ^parameter
val anonFun: Int => Int = (a: Int) => a
// ^variable
// ^type
// ^operator
// ^type
// ^parameter
}
protected abstract class Bla(test: String)
// ^type.qualifier
// ^keyword
// ^type.qualifier
// ^parameter
// ^type
type Hello = "25"
// ^keyword
// ^type.definition
// ^string
class A {
// ^ keyword
// ^ type
self: X =>
// ^type
// ^type
}
type A = { def fly(): Unit }
// ^keyword.function
// ^method
// ^type
type A = B[({ type f[x] = M[S, x] })#f]
// ^keyword
// ^type.definition
val hello = c"some $stuff"
// ^function.call
// ^punctuation.special
}

@ -0,0 +1,149 @@
// Optional braces syntax
class C:
// ^keyword
// ^type
def test(aaaa: A): Int =
//^keyword.function
// ^method
// no curly braces, but this is still in test method
val bbb = 1
//^keyword
// ^variable
val ccc = 2
//^keyword
// ^variable
1
object O1:
//^keyword
// ^type
def test: Unit = ()
//^keyword.function
// ^method
// SIP-44
class C:
// ^keyword
fooooo.map: x =>
// ^type
// ^method.call
x + 1
xs.map:
param1 =>
param1 + 1
foooo:
// ^function.call
println("")
foooo `++`:
// ^operator
val x = 1
List(x)
// This is an ascription
val y = x: Int
// ^type
// This is SIP-44
val y = x:
Int
//^type
// Ascription expression
class C:
foooo: Int
// ^type
enum Simple:
//^keyword
// ^type
case A, B, C
// ^type
enum Test(a: Int) derives Codec:
// ^keyword
// ^type
// ^type
// ^keyword
// ^type
// ^type
// ^parameter
case Test(b: String)
// ^keyword
// ^type
// ^type
// ^parameter
case Hello, Bla
// ^type
// ^type
case Bla extends Test(256)
// ^keyword
opaque type Blow <: Int = 25
// ^type.qualifier
// ^keyword
// ^type
// ^type.definition
inline given Test = new Test {
// ^ storageclass
inline def hello(inline x: Boolean) =
// ^ storageclass
// ^ storageclass
inline if x then "hi" else "bye"
// ^storageclass
// ^conditional
inline x match
// ^storageclass
case true => 25
case false => 26
}
object A:
// ^ keyword
// ^type
::(123)
//^function.call
// ^number
object bla:
open class Hello(A: Int)
// ^ type.qualifier
transparent trait Hello
// ^ type.qualifier
infix def hello = 25
// ^ keyword
extension (s: String)
// ^keyword
def test = 25
def test2 = 25
val extension = "hello"
// ^variable - to test "soft" keywords
given Test with
// ^keyword
// ^type
// ^keyword
def test = 1
// ^keyword.function
val a = "hello"
class Copier:
private val printUnit = new Printer { type PrinterType = InkJet }
private val scanUnit = new Scanner
export scanUnit.scan
// ^ include
// ^namespace
export printUnit.{status as _, *}
// ^ include
// ^namespace
def status: List[String] = printUnit.status ++ scanUnit.status

@ -0,0 +1,50 @@
#define DEBUG
#include "../src/stack.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
ScannerStack *stack = createStack();
printStack(stack, "hello");
assert(isEmptyStack(stack));
pushStack(stack, 27);
assert(!isEmptyStack(stack));
assert(peekStack(stack) == 27);
pushStack(stack, 42);
assert(!isEmptyStack(stack));
assert(peekStack(stack) == 42);
assert(popStack(stack) == 42);
assert(peekStack(stack) == 27);
assert(popStack(stack) == 27);
assert(peekStack(stack) == -1);
assert(isEmptyStack(stack));
char *buf = malloc(1024);
for (int i = 0; i < 100; i++) {
pushStack(stack, i);
}
assert(serialiseStack(stack, buf) == sizeof(int) * 103);
ScannerStack *newStack = createStack();
deserialiseStack(newStack, buf, sizeof(int) * 103);
assert(newStack -> top == 100);
assert(popStack(newStack) == 99);
resetStack(newStack);
assert(isEmptyStack(newStack));
printStack(stack, "hello");
printStack(newStack, "hello");
return 0;
}