Object
Object is a generic object type. These objects can be used as parameters for the Context/next function, like {{next .}}.
.Typeof
.Typeof returns the type name of the object. The type name is a string that represents the type of the object. Except for objects under Common, the type names of other objects are lowercase names separated by dots. For example, the type name of a EnumMember object is enum.member, and the type name of a Enum object is enum. These objects can be customized for code generation by defining templates.
Example:
package demo;
enum Color {
Red = 1;
Green = 2;
Blue = 3;
}
{{- define "go/enum.member" -}}
const {{render "enum.member:name" .Name}} = {{next .Value}}
{{- end}}
{{- define "go/enum.member:name" -}}
{{.Decl.Name}}_{{.}}
{{- end}}
Output:
package demo
type Color int
const Color_Red = 1
const Color_Green = 2
const Color_Blue = 3
These two definitions will override the built-in template functions next/go/enum.member and next/go/enum.member:name.
ArrayType
ArrayType represents an fixed-size array Type.
.ElemType
.ElemType represents the element Type of the array.
.N
.N represents the number of elements in the array.
Comment
Comment represents a line comment or a comment group in Next source code. Use this in templates to access and format comments.
.String
.String returns the full original comment text, including delimiters.
Example:
const x = 1; // This is a comment.
{{.Comment.String}}
Output:
// This is a comment.
.Text
.Text returns the content of the comment without comment delimiters.
Example:
const x = 1; // This is a comment.
{{.Comment.Text}}
Output:
This is a comment.
Common
Common contains some general types, including a generic type. Unless specifically stated, these objects cannot be directly called using the Context/next function. The Value object represents a value, which can be either a constant value or an enum member's value. The object type for the former is const.value, and for the latter is enum.member.value.
Annotation
Annotation represents an annotation by name => value.
Annotation is a map that stores the parameters of a single annotation. It allows for flexible parameter types, including strings, numbers, booleans and Types.
Example:
Next code:
@json(omitempty)
@event(name="Login")
@message(name="Login", type=100)
struct Login {}
@next(type=int8)
enum Color {
Red = 1;
Green = 2;
Blue = 3;
}
Will be represented as:
{{- define "go/struct" -}}
{{.Annotations.json.omitempty}}
{{.Annotations.event.name}}
{{.Annotations.message.name}}
{{.Annotations.message.type}}
{{- end}}
{{- define "go/enum" -}}
{{.Annotations.next.type}}
{{- end}}
Output:
true
Login
Login
100
int8
The next annotation is used to pass information to the next compiler. It's a reserved annotation and should not be used for other purposes. The next annotation can be annotated to package statements, const declarations, enum declarations, struct declarations, field declarations, interface declarations, method declarations, and parameter declarations.
Annotation name MUST NOT be "Has", it's a reserved method for checking whether the annotation contains the given parameter. Parameter name MUST NOT be start with "_" and uppercase letter (A-Z).
@message(type=100) // OK
@message(_type=100)
// invalid parameter name "_type": must not start with an underscore (_)
@next(Pos=100)
// invalid parameter name "Pos": must not start with an uppercase letter (A-Z)
.Has
.Has reports whether the annotation contains the given parameter.
Example:
@json(omitempty)
struct User {/*...*/}
{{if .Annotations.json.Has "omitempty"}}
{{/* do something */}}
{{end}}
If you want to check whether the annotation has a non-empty value, you can use the parameter name directly.
{{if .Annotations.json.omitempty}}
{{/* do something */}}
{{end}}
.Len
.Len returns the number of parameters in the annotation.
.NamePos
.NamePos returns the position of the annotation name in the source code. It's useful to provide a better error message when needed.
Example:
package demo;
@message(type=100)
struct Login {/*...*/}
{{error "%s: Something went wrong" (.Annotations.message.NamePos "type")}}
Output:
example.next:3:10: Something went wrong
.Node
.Node returns the node that the annotation is linked to.
.Pos
.Pos returns the position of the annotation in the source code. It's useful to provide a better error message when needed.
Example:
package demo;
@message(type=100)
struct Login {/*...*/}
{{error "%s: Something went wrong" .Annotations.message.Pos}}
Output:
example.next:3:1: Something went wrong
.ValuePos
.ValuePos returns the position of the annotation value in the source code. It's useful to provide a better error message when needed.
Example:
package demo;
@message(type=100)
struct Login {/*...*/}
{{error "%s: Something went wrong" (.Annotations.message.ValuePos "type")}}
Output:
example.next:3:15: Something went wrong
decl
.available
The @next(available="expression") annotation for file, const, enum, struct, field, interface, method availability of the declaration. The expression is a boolean expression that can be used to control the availability of the declaration in the target language. Supported operators are &, |, !, (, ), and true, false.
Example:
@next(available="c|cpp|java|go|csharp")
struct Point {
int x;
int y;
@next(available="c | cpp | go")
int z;
@next(available="!java & !c")
int w;
}
enum
The next annotation for enum declarations used to control the enum behavior.
.type
.type specifies the underlying type of the enum.
Example:
@next(type=int8)
enum Color {
Red = 1;
Green = 2;
Blue = 3;
}
Output in Go:
type Color int8
const (
ColorRed Color = 1
ColorGreen Color = 2
ColorBlue Color = 3
)
Output in C++:
enum class Color : int8_t {
Red = 1,
Green = 2,
Blue = 3,
};
interface
The next annotation for interface declarations used to control the interface behavior. L_alias is a alias for the interface name in language L. It's used to reference an external type in the target language.
Example:
@next(
available="go|java",
go_alias="net/http.Handler",
java_alias="java.util.function.Function<com.sun.net.httpserver.HttpExchange, String>",
)
interface HTTPHandler {}
@next(available="go|java")
interface HTTPServer {
@next(error)
Handle(string path, HTTPHandler handler);
}
method
The next annotation for method declarations used to control the method behavior.
.error
The @next(error) annotation used to indicate that the method returns an error or throws an exception.
Example:
interface Parser {
@next(error)
parse(string s) int;
}
Output in Go:
type Parser interface {
Parse(s string) (int, error)
}
Output in C++:
class Parser {
public:
int parse(const std::string& s) const;
};
Output in Java:
interface Parser {
int parse(String s) throws Exception;
}
.mut
The @next(mut) annotation used to indicate that the method is a mutable method, which means it can modify the object's state.
Example:
interface Writer {
@next(error, mut)
write(string data);
}
Output in Go:
type Writer interface {
Write(data string) error
}
Output in C++:
class Writer {
public:
void write(const std::string& data);
};
package
The next annotation for package statements used to control the package behavior for specific languages. The next annotation can be used to set the package name, package path, and some other package-related information.
For any language L, the next annotation for package statements is defined as @next(L_package="package_info").
Example:
@next(
c_package="DEMO_",
cpp_package="demo",
java_package="com.exmaple.demo",
go_package="github.com/next/demo",
csharp_package="demo",
)
{{.Package.Annotations.next.c_package}}
{{.Package.Annotations.next.cpp_package}}
{{.Package.Annotations.next.java_package}}
{{.Package.Annotations.next.go_package}}
{{.Package.Annotations.next.csharp_package}}
There are some reserved keys for the next annotation for package statements.
.go_imports
.go_imports represents a list of import paths for Go packages, separated by commas: @next(go_imports="fmt.Printf,*io.Reader").
* is required to import types.
Example:
@next(go_imports="fmt.Printf,*io.Reader")
package demo;
param
The next annotation for parameter declarations used to control the parameter behavior.
.mut
The @next(mut) annotation used to indicate that the parameter is mutable.
Example:
interface Reader {
@next(error);
read(@next(mut) string data);
}
Output in Go:
type Reader interface {
Read(data string) error
}
Output in C++:
class Reader {
public:
void read(std::string& data);
};
struct
The next annotation for struct declarations used to control the struct behavior. L_alias is a alias for the struct name in language L. It's used to reference an external type in the target language.
Example:
@next(rust_alias="u128")
struct uint128 {
int64 low;
int64 high;
}
@next(go_alias="complex128")
struct Complex {
float64 real;
float64 imag;
}
struct Contract {
uint128 address;
Complex complex;
}
This will don't generate the uint128 struct in the rust language, but use u128 instead. And in the go language, it will use complex128 instead of Complex.
Annotations
Annotations represents a group of annotations by name => Annotation.
Annotations is a map that stores multiple annotations for a given entity. The key is the annotation name (string), and the value is the corresponding Annotation object.
.Has
.Has reports whether the annotations contain the given annotation.
Example:
@json(omitempty)
struct User {/*...*/}
{{if .Annotations.Has "json"}}
{{/* do something */}}
{{end}}
Decl
Decl represents a top-level declaration Node in a file.
Currently, the following declarations are supported:
.UsedKinds
.UsedKinds returns the used kinds in the declaration. Returns 0 if the declaration does not use any kinds. Otherwise, returns the OR of all used kinds.
Example:
struct User {
int64 id;
string name;
vector<string> emails;
map<int, bool> flags;
}
The used kinds in the User struct are: (1<<KindInt64) | (1<<KindString) | (1<<KindVector) | (1<<KindMap) | (1<<KindInt) | (1<<KindBool).
Fields
Fields<D, F> represents a list of fields of a declaration where D is the declaration Node and F is the field object Node.
.Decl
.Decl is the declaration Node that contains the fields.
Currently, it is one of following types:
.List
.List is the slice of fields: [Object].
Currently, the field object is one of following types:
.Lookup
.Lookup looks up a field by name and returns the field object.
List
List<T> represents a slice of objects: [T: Object].
.List
.List represents the slice of Objects. It is used to provide a uniform way to access.
LocatedObject
LocatedObject represents an Object with a location in a file.
.File
.File represents the file containing the object.
.Package
.Package represents the package containing the object.
.Pos
.Pos represents the Position of the object.
Example:
package demo;
const Name = "hei hei";
{{- define "meta/this" -}}const{{- end -}}
{{this.Pos}}
{{this.Value.Pos}}
Output:
demo.next:2:1
demo.next:2:14
Node
Node represents a LocatedObject that is a node in a file.
Currently, the following nodes are supported:
.Annotations
.Annotations represents the Annotations for the node.
Example:
package demo;
@next(type=int8)
@custom
enum Color {
Red = 1;
Green = 2;
Blue = 3;
}
{{.Annotations.next.type}}
{{.Annotations.next.Pos}}
{{.Annotations.Has "custom"}}
Output:
int8
demo.next:2:1
true
.Doc
.Doc represents the documentation comment for the node. The documentation comment is a comment that appears before the node declaration.
Example:
// This is a documentation comment for the node.
// It can be multiple lines.
const x = 1;
{{.Doc.Text}}
Output:
This is a documentation comment for the node.
It can be multiple lines.
.Name
.Name represents the name of the node.
Example:
const x = 1;
{{.Name}}
Output:
x
.NamePos
.NamePos represents the position of the node name.
Example:
package demo;
const x = 1;
{{.NamePos}}
Output:
demo.next:2:7
Position
Position returns the string representation of the position, e.g., demo.next:10:2.
.Column
.Column represents the column number of the position starting from 1.
.Filename
.Filename represents the filename of the position.
.IsValid
.IsValid reports whether the position is valid.
.Line
.Line represents the line number of the position starting from 1.
Symbol
Symbol represents a Next symbol. There are two types of symbols:
- Value symbol: such as a constant or an enum member.
- Type symbol: such as an enum, a struct, or an interface.
Type
Type represents a Next type Object.
Currently, the following types are supported:
.Actual
.Actual represents the actual type. You need to use the Actual method to get the actual type to access the specific fields of the type.
For example, if you have a type ArrayType:
{{.Type.Actual.ElemType}} {{/* Good */}}
{{.Type.Actual.N}} {{/* Good */}}
{{.Type.ElemType}} {{/* Error */}}
{{.Type.N}} {{/* Error */}}
Example:
package demo;
struct User {
int64 id;
string name;
array<int, 3> codes;
}
{{- define "c/struct.field" -}}
{{- if .Type.Kind.IsArray -}}
{{next .Type.Actual.ElemType}} {{.Name}}[{{.Type.Actual.N}}];
{{- else -}}
{{next .Type}} {{.Name}};
{{- end}}
{{- end}}
Output:
typedef struct User {
int64_t id;
char* name;
int codes[3];
} User;
In the example above, the codes field is an single-dimensional array of integers with a length of 3. If we want to process the multi-dimensional array, we need to fix the template to recursively process the array type.
Example:
package demo;
struct User {
int64 id;
string name;
array<array<int, 3>, 2> codes;
}
{{- define "c/struct.field" -}}
{{next .Doc}}{{render "dict:struct.field.decl" (dict "type" .Type "name" (render "struct.field:name" .))}};{{next .Comment}}
{{- end}}
{{- define "c/dict:struct.field.decl" -}}
{{- $type := .type -}}
{{- $name := .name -}}
{{- if $type.Kind.IsArray -}}
{{render "dict:struct.field.decl" (dict "type" $type.Actual.ElemType "name" (printf "%s[%d]" $name $type.Actual.N))}}
{{- else -}}
{{next $type}} {{$name}}
{{- end}}
{{- end}}
Output:
typedef struct User {
int64_t id;
char* name;
int codes[2][3];
} User;
.Decl
.Decl represents the Decl of the type. If the type is a built-in type, it returns a special declaration. Otherwise, it returns the declaration of the type: Enum, Struct, or Interface.
.Kind
.Kind returns the Kind of the type.
Example:
package demo;
enum Color {
Red = 1;
Green = 2;
Blue = 3;
}
{{- define "cpp/enum" -}}
{{.Type.Kind}}
{{.MemberType.Kind}}
{{end}}
Output:
Enum
Int32
.String
.String represents the string representation of the type.
.UsedKinds
.UsedKinds returns the used kinds in the type.
Example:
package demo;
struct User {
int64 id;
string name;
vector<string> emails;
map<int, bool> flags;
}
The used kinds in the User struct are: (1<<KindStruct) | (1<<KindInt64) | (1<<KindString) | (1<<KindVector) | (1<<KindMap) | (1<<KindInt) | (1<<KindBool).
{{.UsedKinds.Has "struct"}}
{{.UsedKinds.Has "int64"}}
{{.UsedKinds.Has "string"}}
{{.UsedKinds.Has "vector"}}
{{.UsedKinds.Has "map"}}
{{.UsedKinds.Has "int"}}
{{.UsedKinds.Has "bool"}}
{{.UsedKinds.Has "float32"}}
Output:
true
true
true
true
true
true
true
false
Kind
Kind represents the type kind. Currently, the following kinds are supported:
- bool: true or false
- int: integer
- int8: 8-bit integer
- int16: 16-bit integer
- int32: 32-bit integer
- int64: 64-bit integer
- float32: 32-bit floating point
- float64: 64-bit floating point
- byte: byte
- bytes: byte slice
- string: string
- time: time
- duration: duration
- any: any object
- map: dictionary
- vector: vector of elements
- array: array of elements
- enum: enumeration
- struct: structure
- interface: interface
.Bits
.Bits returns the number of bits for the type. If the type has unknown bits, it returns 0 (for example, any, string, bytes).
.Compatible
.Compatible returns the compatible type kind between two kinds. If the kinds are not compatible, it returns KindInvalid. If the kinds are the same, it returns the kind. If the kinds are both numeric, it returns the kind with the most bits.
.IsAny
.IsAny reports whether the type is any.
.IsArray
.IsArray reports whether the type is an array.
.IsBool
.IsBool reports whether the type is a boolean.
.IsByte
.IsByte reports whether the type is a byte.
.IsBytes
.IsBytes reports whether the type is a byte slice.
.IsDuration
.IsDuration reports whether the type is a duration.
.IsEnum
.IsEnum reports whether the type is an enumeration.
.IsFloat
.IsFloat reports whether the type is a floating point. It includes float32 and float64.
.IsInteger
.IsInteger reports whether the type is an integer. It includes int, int8, int16, int32, int64, and byte.
.IsInterface
.IsInterface reports whether the type is an interface.
.IsMap
.IsMap reports whether the type is a map.
.IsNumeric
.IsNumeric reports whether the type is a numeric type. It includes integer and floating point types.
.IsPrimitive
.IsPrimitive reports whether the type is a PrimitiveType.
.IsString
.IsString reports whether the type is a string.
.IsStruct
.IsStruct reports whether the type is a structure.