Skip to content

Repository files navigation

AnnotationScript

logo

AnnotationScript is a functional, dynamically typed language that starts numbering at zero, and is contained within annotations placed on a Java class.

Read why I built this ridiculous thing on my blog!

Running the demos from the command line

The easiest way to run the demos if from the IDE, but that would be no fun. Using Java 22 or higher, you can do this instead:

  • Run mvn dependency:copy-dependencies -DoutputDirectory=target/lib -DincludeScope=runtime
    This copies the dependencies into target/lib folder.
  • Run java -cp "target/lib/*" src/main/java/nl/jqno/annotationscript/demo/<demofile>.java
    This compiles and runs the demo.

A brief tutorial

A top-level expression may look like this:

@Zero("+")@Zero("x")@Zero("1")
classExample {}

Each element of the expression is placed within its own @Zero annotation. The function to be called comes first; its parameters follow. This expression calculates x + 1.

You can create more complex expressions by using lists, in which the name of the annotation is incremented by one, like this:

@Zero("+")@Zero(list={@One("*"), @One("x"), @One("2")})@Zero("1")
classExample {}

This calculates (x * 2) + 1.

You can define constants as follows:

@Zero("begin")
@Zero(list={@One("define"), @One("TWO"), @One("2")})
@Zero(list={
@One("+"),
@One(list={@Two("*"), @Two("x"), @Two("TWO")}),
@One("1")})
classExample {}

This assigns the value 2 to the identifier TWO, and then calculates (x * TWO) + 1. Since this program now consists of two expressions (the define and the calculation itself), we need to wrap the expressions in a begin expression, which accepts an arbitrary number of expressions, evaluates each one in turn, and returns the result of the last expression.

We can also define functions:

@Zero("begin")
@Zero(list={
@One("define"),
@One("twice"),
@One(list={
@Two("lambda"),
@Two(list={@Three("x")}),
@Two(list={@Three("*"), @Three("x"), @Three("2")})})})
@Zero(list={
@One("+"),
@One(list={@Two("twice"), @Two("x")}),
@One("1")})
classExample {}

As you can see, defining a function is the same as assigning a lambda-expression to an identifier. In this case, we define the lambda x -> x * 2 to the identifier twice. Then we calculate the result of twice(x) + 1.

AnnotationScript also supports namespaces:

@Zero("define"),
@Zero("twice"),
@Zero(list={
@One("lambda"),
@One(list={@Two("x")}),
@One(list={@Two("*"), @Two("x"), @Two("2")})})})
classTwice {}
@Zero("begin")
@Zero(include=Twice.class)
@Zero(list={
@One("+"),
@One(list={@Two("twice"), @Two("x")}),
@One("1")})
classExample {}

Here, we've moved the twice function to the class Twice, which we imported in Example by using @Zero(include=Twice.class).

We can also group namespaces together. Lets assume we have classes Twice, Thrice and Quadruple:

@Zero(export={
Twice.class,
Thrice.class,
Quadruple.class})
classModule {}
@Zero("begin")
@Zero(include=Module.class)
@Zero(list={
@One("+"),
@One(list={@Two("twice"), @Two("x")}),
@One("1")})
classExample {}

Example now has access to Double, Thrice and Quadruple through the Module class.

AnnotationScript supports branching using if and cond:

@Zero("if"),
@Zero(list={@One(">"), @One("x"), @One("0")}),
@Zero("positive")
@Zero("negative")
classIf {}
@Zero("cond"),
@Zero(list={@One(">"), @One("x"), @One("0")}),
@Zero("positive")
@Zero(list={@One("&lt;"), @One("x"), @One("0")})
@Zero("negative")
@Zero("else")
@Zero("zero")
classCond {}

There are no loops; instead, AnnotationScript uses recursion to achieve the same effect.

AnnotationScript has a lot of built-in functions to do arithmetic, manipulate strings, lists and maps, print to the console, and more. For an exhaustive list, I'll just refer to the global environment.

Finally, to run an AnnotationScript program, you pass the class that holds the main expression to the AnnotationScript.run function:

importnl.jqno.annotationscript.AnnotationScript;
importnl.jqno.annotationscript.Annotations.*;
@Zero("+")@Zero("1")@Zero("1")
publicclassExample {
publicstaticvoidmain(String[] args) {
varoutput = AnnotationScript.run(Example.class);
System.out.println(output);
}
}

This program outputs 2.

You can also pass parameters. Note that you need a VavrHashMap to do so:

importio.vavr.collection.HashMap;
importnl.jqno.annotationscript.AnnotationScript;
importnl.jqno.annotationscript.Annotations.*;
@Zero("+")@Zero("x")@Zero("1")
publicclassExample {
publicstaticvoidmain(String[] args) {
varparams = HashMap.of("x", 2)
varoutput = AnnotationScript.run(Example.class, params);
System.out.println(output);
}
}

This program prints 3.

About

Java annotations with a lisp

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages