• Stars
    star
    225
  • Rank 176,545 (Top 4 %)
  • Language
    Kotlin
  • License
    MIT License
  • Created about 6 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple Kotlin Source AST and Syntax Parsing, Editing, and Writing

Kastree

Kastree is a simple library to manipulate Kotlin source code as a set of AST objects. Features:

  • Simple, immutable, hierarchical set of data classes representing Kotlin AST
  • Simple writer implementation (some advanced features not yet supported)
  • Support for regular and mutable visitors
  • Basic support for blank-line and comment map (some advanced use cases not yet supported)
  • Support for parsing (via Kotlin compiler's parser) and converting to the AST

The project is a simple one and probably will not support a lot of features. It was created to facilitate advanced Kotlin code generation beyond the string-based versions that exist.

Note

Kastree is currently not being actively developed.

Another kotlin AST parsing library is kotlinx.ast

Usage

Getting

This project has three libraries that are deployed to Maven Central. To simply build the AST from a Kotlin JVM or Java project, add the following dependency in Gradle:

compile 'com.github.cretz.kastree:kastree-ast-jvm:0.4.0'

That does not include the parser. To include the parser (which transitively includes the entire Kotlin compiler), instead use:

compile 'com.github.cretz.kastree:kastree-ast-psi:0.4.0'

While the parser only works from JVM projects, the AST itself (and writers/visitors) can be used from other multiplatform projects. In the shared/common project, include the common lib:

compile 'com.github.cretz.kastree:kastree-ast-common:0.4.0'

Examples

Examples below are simple Kotlin scripts.

Parse code

In this example, we use the wrapper around the Kotlin compiler's parser:

import kastree.ast.psi.Parser

val code = """
    package foo

    fun bar() {
        // Print hello
        println("Hello, World!")
    }

    fun baz() = println("Hello, again!")
""".trimIndent()
// Call the parser with the code
val file = Parser.parseFile(code)
// The file var is now a kastree.ast.Node.File that is used in future examples...

The file variable has the full AST. Note, if you want to parse with blank line and comment information, you can create a converter that holds the extras:

// ...

val extrasMap = Converter.WithExtras()
val file = Parser(extrasMap).parseFile(code)
// extrasMap is an instance of kastree.ast.ExtrasMap

Write code

To write the code created above, simply use the writer

import kastree.ast.Writer
// ...

println(Writer.write(file))

This outputs a string of the written code from the AST file object. To include the extra blank line and comment info from the previous parse example, pass in the extras map:

// ...

println(Writer.write(file, extrasMap))

This outputs the code with the comments.

View nodes of a type

This will get all strings:

import kastree.ast.Node
import kastree.ast.Visitor
// ...

var strings = emptyList<String>()
Visitor.visit(file) { v, _ ->
    if (v is Node.Expr.StringTmpl.Elem.Regular) strings += v.str
}
// Prints [Hello, World!, Hello, again!]
println(strings)

The first parameter of the lambda is the nullable node and the second parameter is the parent. There is a tag var on each node that can be used to store per-node state if desired.

Modify nodes

This will change "Hello, World!" and "Hello, again!" to "Howdy, World!" and "Howdy, again":

import kastree.ast.MutableVisitor
// ...

val newFile = MutableVisitor.preVisit(file) { v, _ ->
    if (v !is Node.Expr.StringTmpl.Elem.Regular) v
    else v.copy(str = v.str.replace("Hello", "Howdy"))
}

Now newFile is a transformed version of file. As before, the first parameter of the lambda is the nullable node and the second parameter is the parent. The difference between preVisit (as used here) and postVisit is that preVisit is called before children are traversed/mutated whereas postVisit is called afterwards.

Note, since extraMap support relies on object identities and this creates entirely new objects in the immutable tree, the extra map becomes invalid on this step. This is not solved in the library yet, but could be done fairly easily.

Running tests

The tests rely on a checked out version of the Kotlin source repository since it uses the test data there to build a corpus to test against. The path to the base of the repo needs to be set as the KOTLIN_REPO environment variable. Once set, run:

path/to/gradle :ast-psi:test

This will ignore all Kotlin files with expected parse errors and only test against the ones that are valid (178 as of this writing). The test parses the Kotlin code into this AST, then re-writes this AST, then re-parses what was just written and confirms it matches the original AST field for field.

More Repositories

1

bine

Go library for accessing and embedding Tor clients and servers
Go
719
star
2

asmble

Compile WebAssembly to JVM and other WASM tools
Kotlin
599
star
3

doogie

A Chromium-based web browser with tree-style pages
C++
278
star
4

pb-and-k

Kotlin Code Generator and Runtime for Protocol Buffers
Kotlin
139
star
5

stackparam

JVM agent to add method parameters to Java stack traces
Rust
116
star
6

node-tds

Pure JS implementation of TDS protocol for Microsoft SQL Server
CoffeeScript
104
star
7

gwt-node

GWT implementation of standard the node.js library
Java
87
star
8

software-ideas

85
star
9

myscreen.live

P2P Screen Sharing with WebRTC
TypeScript
84
star
10

go-scrap

Go library to capture screen pixels for screenshots or screen recording
Go
76
star
11

tor-static

Helpers to build Tor statically
Go
74
star
12

dust-php

Powerful PHP templating engine based off of Dust JS
PHP
67
star
13

pgnio

Asynchronous PostgreSQL client for Java and the JVM
Java
62
star
14

gopaque

Go implementation of OPAQUE (hidden password user registration and auth)
Go
58
star
15

prost-twirp

Code generator and library for calling/serving Twirp services in Rust using prost and hyper
Rust
57
star
16

webrtc-ipfs-signaling

Tech demo using JS-IPFS to do signaling for WebRTC
HTML
56
star
17

javan-warty-pig

AFL-like fuzzer for the Java Virtual Machine
Java
49
star
18

scala-web-ideal

Akka HTTP + Scala.js + ScalaTags + ScalaCSS + Autowire + Prickle + akka-sse + Gradle + etc... (outdated/stale)
Scala
49
star
19

tor-dht-poc

Anonymous DHT Accessible from Executable or Tor-Enabled Browser
Go
48
star
20

pratphall

A typed language targeting PHP (abandoned)
JavaScript
45
star
21

qt_cef_poc

Proof of concept of simple browser w/ CEF and Qt
Go
26
star
22

kotlin-native-sample-agent

Sample JVMTI Agent Using Kotlin Native
Kotlin
23
star
23

chrome-screen-rec-poc

C++
21
star
24

go-sqleet

Encrypted SQLite for Go using go-sqlite3 and sqleet
C
17
star
25

msplit

Simple algorithm to split a JVM ASM method into two
Java
16
star
26

goahead

A JVM AOT
Scala
16
star
27

temporal-sdk-go-advanced

Go
15
star
28

go-mental-poker

Mental Poker Demonstration in Go
Go
12
star
29

go-wasm-bake

Experimenting with eager evaluation of Go WASM code
Kotlin
12
star
30

rtsw-poc

Rust + Tor (embedded) + Static (compile) + Windows + Proof of Concept
Rust
12
star
31

esgopeta

Go implementation of the Gun distributed graph database
Go
11
star
32

owncast-old

Go
11
star
33

superpose

Go library for compile-time code transformation
Go
10
star
34

SqlChop

Read the Transaction Log from SQL Server
C#
10
star
35

temporal-debug-go

Go
10
star
36

safeclient.js

JS Client to the SAFE Launcher API
TypeScript
9
star
37

imedge

Imedge - Image Manipulation in Edge Service Workers
Rust
8
star
38

rust-qt_cef_poc

Rust
8
star
39

temporal-protoc-gen-go-activity

Go
7
star
40

takecast

Go
7
star
41

go2k

Kotlin
5
star
42

rwtxt-crypt

The minimalist rwtxt CMS using encrypted SQLite and Tor
Go
5
star
43

rust-qthello

Rust
5
star
44

go-safeclient

CLI and Go library for accessing SAFE network
Go
4
star
45

statmantis

Java based baseball stat analyzer/parser
Java
4
star
46

gulliver

Scala
4
star
47

capnp-scala

Scala
3
star
48

ffembedpoc

Go
3
star
49

nexus-poc-old

Go
3
star
50

javan-warty-pig-kotlin

Kotlin
2
star
51

temporal-wasm

Go
2
star
52

teleworker

Go
2
star
53

temporal-determinist

Go
2
star
54

systrument

Go
2
star
55

badads

JavaScript
2
star
56

safe-poc-browser

Simple Proof-of-Concept Browser for SAFE Network
JavaScript
2
star
57

forsook

Java
2
star
58

smail

Scala
2
star
59

go-dump

Go
1
star
60

irnie

1
star
61

mlbdash

HTML, client-side only live MLB dashboard
Java
1
star
62

clicknkick

Go
1
star
63

shrewd-old

TypeScript
1
star
64

gwtson

GWT emulation layer for Gson
Java
1
star
65

monitoral

Go
1
star
66

distinct

JS diffing utility
CoffeeScript
1
star
67

yocalist

Java
1
star
68

fusty

Network Device Backup Tool/System
Go
1
star
69

narrow

1
star
70

gat-cdc

Trigger based Change Data Capture (CDC)
C#
1
star
71

yocalist-coffee

CoffeeScript
1
star
72

one-left

Go
1
star
73

sbn-stat

Generate posting statistics for SBN sites
Java
1
star
74

ripoata

1
star
75

elixoral

Elixir
1
star
76

rusty-zipper

Bindings and wrapper for libzip (incomplete)
Rust
1
star
77

caddy-tlsconsul

Go
1
star