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
:
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
:
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)
, andday(int)
in this order, and - the expression
year(…).month(…).day(…)
returns an instance ofjava.time.LocalDateTime
.
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.
Common repeat operators such as ?
are supported. The example below indicates that the invocations of month(int)
and day(int)
are optional.
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() |
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 {}
:
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)
(Invokingday
twice)month(11).day(6)
(Missingyear
)
By importing a type like in Java, one can refer to that type by its simple name:
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:
import org.example.DateTimeBuilder;
DateTimeBuilder { // = org.example.DateTimeBuilder
java.time.LocalDateTime year(int y) month(int m) day(int d);
}
The repetition in rules can be removed by defining a fragment:
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);
}
There are three kinds of type parameters:
-
Ones listed in a type declaration
-
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 likeFoo<T> { … }
, Silverchain generatesFoo<T>
. -
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 likeFoo<;T> { … }
, Silverchain generatesFoo
(without the type parameterT
). The examples mapbuilder.ag and listutil.ag are their usecases.
-
-
Ones listed in a method declaration. These type parameters are not shared in a chain. They are only used in that method.
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).