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.