Exit Full View

Feather

A statically typed language which compiles to Java Byte Code, designed to be used as a scripting language within other applications. It includes a sandbox which restricts which classes are available. The syntax is inspired by Kotlin's (but not as rich).

Take a look at Feather's documentation for example code.

Feather is also nice to use as a powerful command scripting language, due to the optional shell features (See below).

Feather is statically typed, which may seem weird for a scripting language, but I am of the firm belief that dynamically typed languages hinder productivity. Variables can be declared without explicitly stating their type, when it can be inferred by the compiler.

For example, the type of "foo" is an "int", without having to explicitly declare it :

val foo = 1 + someFunctionWhichReturnsAnInt()

Feather's typing is also lax. You can pass an int to a method expecting a double, and it will be silently converted.

Together these make the type system nice to use.

Feather uses ANTLR to parse the code, and ASM to help generate the Java Byte Code.

A BIG Thank You to the ANTLR and ASM folks. You guys are great.

Building from Source

git clone https://gitlab.com/nickthecoder/feather
cd feather
./gradlew

Now test by compiling and running an example script :

 ./build/install/feather/bin/feather examples/Hello.feather 

You should see "Hello" on the terminal.

Adding Feather dependencies to your project

Feather's maven artifacts are hosted on gitlab.com. To add this repository to a gradle build.gradle.kts script :

repositories {
    maven {
        name = "feather"
        url = uri("https://gitlab.com/api/v4/projects/16531834/packages/maven")
    }
}

Then add the dependencies :

val featherVersion = "0.5"
dependencies {
    implementation("uk.co.nickthecoder:feather-core:$featherVersion")
    implementation("uk.co.nickthecoder:feather-runtime:$featherVersion")
}

Command line usage :

Assuming the "feather" executable in build/install/feather/bin is on your path...

feather FEATHER_SCRIPT...

Where FEATHER_SCRIPT is one or more script files (usually with a ".feather" extension).

For more details on the command line options :

feather --help

Usage from within an Application

Using Kotlin style pseudocode here, but Feather can be used from any JVM language, including Java :

val compiler = FeatherCompiler()

val byteCode = compiler.compile( myFile )

If you want to compile scripts from a string, and not a file :

val byteCode = compiler.compileFromStrings( "class Foo() ..." )

The result is a map of compiled JVM byte code, keyed by their fully qualified class names.

That's not useful on its own, you will need a ClassLoader. Here, we use Feather's built-in classloader, but you may use one of your own instead :

val classLoader = FeatherClassLoader(
    compiler.compile( myFile )
)

You can then instantiate classes :

classLoader.loadClass( fullyQualifiedClassName ).newInstance()

Look at CompilerConfiguration for options.

Status

Working, but there are still some annoying quirks.

I've been using Feather as a scripting language in two of my applications for some time now. Tickle and FooCAD

I also use it as a general-purpose language for one-off scripts.

The most annoying issues for me are the error messages. There are lots of cases where an error message is misleading.

I have automated test cases, but not nearly enough of them. I could write 10 times as many test cases, and still not cover all the edge cases.

Missing Features

I've completed all the features that I care about, except for Lambdas.

I'm in no rush at all to implement the remainder!

  • All fields/methods are public - there are no protected/private/package fields or methods.
  • Cannot define generic classes (but you can use generic classes from external libraries).
  • No Secondary constructors.
  • No Lambdas
  • No Distinction between nullable and non-nullable types. Java folks may not care, but Kotlin folks will see this as a big backwards step.
  • No Inner classes.
  • No Interface default methods.
  • No default values for methods/constructors parameters.
  • Cannot use named arguments in method calls (e.g. foo( isGood = true, isTransparent = false )
  • No bitwise shifts, bitwise and/or/xor.

Speed

The speed of the compiled code is similar, if not identical to Java in most cases. However, there are cases where Feather is slightly less optimal. There are unit tests which check that simple scripts produces identical bytecode to an equivalent Java program.

If you look at generated bytecode, it is clear that there are many cases where optimisations could be made. But that would make compilation slower, and quite frankly, runtime speed isn't as important to me as compilation speed. (Despite the fact that I write games with Feather!)

For my use cases, Feather's runtime speed is fine (and I write games with it).

As for compilation speed, I'm pleased with the performance, but I would like it to be faster.

I have an application which compiles dozens of feather extensions at startup, which takes 4 seconds - longer than I'd like.