tomokinakamaru/silverchain

View on GitHub
doc/ag-reference.md

Summary

Maintainability
Test Coverage
# AG Reference

AG is the language to write an input to Silverchain (i.e. to write the rules of fluent APIs). The following lines are AG code that defines the rules of a fluent builder of `java.time.LocalDateTime`:

```java
org.example.DateTimeBuilder {
  java.time.LocalDateTime year(int y) month(int m) day(int d);
}
```

AG code consists of one or more named blocks (`<NAME> { }`). The name of a block is the name of the _entrypoint class_ of a fluent API. Specifically, `org.example.DateTimeBuilder { … }` indicates that the fluent builder allows its users to start writing a method chain by instantiating `org.example.DateTimeBuilder`:

```java
import org.example.DateTimeBuilder;
new DateTimeBuilder().year(2021). … ;
```

The lines inside a block describe the rules on method chains. Each rule consists of

- the type of an object that is returned by the last method in a chain,
- a regular expression whose atomic element (or _alphabet_) is a method declaration,

and a semicolon. In the example AG code, the second line indicates that

- the API allows its users to chain `year(int)`, `month(int)`, and `day(int)` in this order, and
- the expression `year(…).month(…).day(…)` returns an instance of `java.time.LocalDateTime`.

## More on chain rules

### Types and methods in rule

One can write any valid Java type/method in a chain rule. One can write `void`, `int[]`, `String[][]`, etc., not only simple class/interface names; One can use `...` in method parameters; One can add `throws` to the method.

### Repeat operators

Common repeat operators such as `?` are supported. The example below indicates that the invocations of `month(int)` and `day(int)` are optional.

```java
org.example.DateTimeBuilder {
  java.time.LocalDateTime year(int y) month(int m)? day(int d)?;
}
```

The following lists all the operators allowed in AG code:

| operator     | semantics                                                |
| :----------- | :------------------------------------------------------- |
| `foo()?`     | zero or one invocation of `foo()`                        |
| `foo()*`     | zero or more invocations of `foo()`                      |
| `foo()+`     | one or more invocations of `foo()`                       |
| `foo()[n]`   | `n` invocations of `foo()`                               |
| `foo()[n,]`  | `n` or more invocations of `foo()`                       |
| `foo()[n,m]` | at least `n` but no more than `m` invocations of `foo()` |

### Unordered rules

Some API would provide a group of methods that can be invoked in any order, but each of them only once. Silverchain supports a special operator `{}`:

```java
import java.time.LocalDateTime;
import org.example.DateTimeBuilder;

DateTimeBuilder {
  // Invoke year, month, day in any order,
  //   but each of them only once
  LocalDateTime { year(int y), month(int m), day(int d) };
}
```

The above example means that the API allows all of the below:

- `year(2021).month(11).day(6)`
- `year(2021).day(6).month(11)`
- `month(11).year(2021).day(6)`
- `month(11).day(6).year(2021)`
- `day(6).year(2021).month(11)`
- `day(6).month(11).year(2021)`.

However, the API does not allow

- `year(2021).month(11).day(6).day(6)` (Invoking `day` twice)
- `month(11).day(6)` (Missing `year`)

## Sugars to simplify AG code

### Importing types

By importing a type like in Java, one can refer to that type by its simple name:

```java
import java.time.LocalDateTime;

org.example.DateTimeBuilder {
  LocalDateTime // = java.time.LocalDateTime
    year(int y) month(int m) day(int d);
}
```

One can use an import statement to use a simple name for classes to be generated by Silverchain:

```java
import org.example.DateTimeBuilder;

DateTimeBuilder { // = org.example.DateTimeBuilder
  java.time.LocalDateTime year(int y) month(int m) day(int d);
}
```

### Fragments

The repetition in rules can be removed by defining a fragment:

```java
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import org.example.DateTimeBuilder;

// Fragment definition ($FRAGMENT_NAME = …);
$YMD = year(int y) month(int m) day(int d);

DateTimeBuilder {
  // $YMD is expanded into `year(int y) month(int m) day(int d)`
  LocalDateTime $YMD local();
  ZonedDateTime $YMD timezone(ZoneId z);
}
```

## Type parameters

There are three kinds of type parameters:

1. Ones listed in a type declaration

   1. **Ones listed before `;` or without `;`**. These type parameters are shared in a chain expression and are included in the generated type declaration. For example, if you give an input like `Foo<T> { … }`, Silverchain generates `Foo<T>`.

   2. **Ones listed after `;`**. These type parameters are shared in a chain expression but are **not** included in the generated type declaration. For example, If you give an input like `Foo<;T> { … }`, Silverchain generates `Foo` (without the type parameter `T`). The examples [mapbuilder.ag](https://github.com/tomokinakamaru/silverchain/blob/master/src/test/resources/mapbuilder.ag) and [listutil.ag](https://github.com/tomokinakamaru/silverchain/blob/fix/issue39/src/test/resources/listutil.ag) are their usecases.

2. **Ones listed in a method declaration**. These type parameters are **not** shared in a chain. They are only used in that method.

## Comments

One can write comments in AG code. Everything after `//` in a line is treated as a comment. `/* */` to write a block comment.

To add comments to generated files, use the `--javadoc` option (See [here](./javadoc.md)).