Fory Definition Language (FDL) Compiler
The FDL compiler generates cross-language serialization code from schema definitions. It enables type-safe cross-language data exchange by generating native data structures with Fory serialization support for multiple programming languages.
Features
- Multi-language code generation: Java, Python, Go, Rust, C++, C#, JavaScript, Swift, Dart, Scala, and Kotlin
- Rich type system: Primitives, enums, messages, lists, dense arrays, maps
- Cross-language serialization: Generated code works seamlessly with Apache Fory
- Type ID and namespace support: Both numeric IDs and name-based type registration
- Field modifiers: Optional fields, reference tracking, list fields, scalar encoding modifiers
- File imports: Modular schemas with import support
- gRPC service generation: Native gRPC stubs and service bases for Java, Python, Go, Rust, C#, JavaScript, Dart, Kotlin, and Scala
Documentation
For comprehensive documentation, see the FDL Schema Guide:
- FDL Syntax Reference - Complete language syntax and grammar
- Type System - Primitive types, collections, and language mappings
- Compiler Guide - CLI options and build integration
- Generated Code - Output format for each target language
- Protocol Buffers vs FDL - Feature comparison and porting guide
Installation
cd compiler
pip install -e .
Quick Start
1. Define Your Schema
Create a .fdl file:
package demo;
enum Color [id=101] {
GREEN = 0;
RED = 1;
BLUE = 2;
}
message Dog [id=102] {
optional string name = 1;
int32 age = 2;
}
message Cat [id=103] {
ref Dog friend = 1;
optional string name = 2;
list<string> tags = 3;
map<string, int32> scores = 4;
int32 lives = 5;
}
2. Compile
# Generate for all languages
foryc schema.fdl --output ./generated
# Generate for specific languages
foryc schema.fdl --lang java,python,csharp,javascript,scala --output ./generated
# Language-specific output directories (protoc-style)
foryc schema.fdl --java_out=./src/main/java --python_out=./python/src --csharp_out=./csharp/src/Generated --javascript_out=./javascript --scala_out=./scala/src/main/scala
# Combine with other options
foryc schema.fdl --java_out=./gen --go_out=./gen/go --csharp_out=./gen/csharp --javascript_out=./gen/js --scala_out=./gen/scala -I ./proto
# Also generate gRPC service stubs
foryc schema.fdl --lang java,python,go --grpc --output ./generated
3. Use Generated Code
Java:
import demo.*;
import org.apache.fory.Fory;
Fory fory = Fory.builder()
.withXlang(true)
.withRefTracking(true)
.withModule(DemoForyModule.INSTANCE)
.build();
Cat cat = new Cat();
cat.setName("Whiskers");
cat.setLives(9);
byte[] bytes = fory.serialize(cat);
Python:
import pyfory
from demo import Cat, register_demo_types
fory = pyfory.Fory(xlang=True)
register_demo_types(fory)
cat = Cat(name="Whiskers", lives=9)
data = fory.serialize(cat)
FDL Syntax
Package Declaration
package com.example.models;
Imports
Import types from other FDL files:
import "common/types.fdl";
import "models/address.fdl";
Imports are resolved relative to the importing file. All types from imported files become available for use in the current file.
Example:
// common.fdl
package common;
message Address [id=100] {
string street = 1;
string city = 2;
}
// user.fdl
package user;
import "common.fdl";
message User [id=101] {
string name = 1;
Address address = 2; // Uses imported type
}
Enum Definition
enum Status [id=100] {
PENDING = 0;
ACTIVE = 1;
INACTIVE = 2;
}
Message Definition
message User [id=101] {
string name = 1;
int32 age = 2;
optional string email = 3;
}
Type Options
Types can have options specified in brackets after the name:
message User [id=101] { ... } // Registered with type ID 101
message User [id=101, deprecated=true] { ... } // Multiple options
Types without [id=...] use name-based registration:
message Config { ... } // Registered as "package.Config"
Primitive Types
| FDL Type | Java | Python | Go | Rust | C++ | C# | JavaScript |
|---|---|---|---|---|---|---|---|
bool |
boolean |
bool |
bool |
bool |
bool |
bool |
boolean |
int8 |
byte |
pyfory.Int8 |
int8 |
i8 |
int8_t |
sbyte |
number |
int16 |
short |
pyfory.Int16 |
int16 |
i16 |
int16_t |
short |
number |
int32 |
int |
pyfory.Int32 |
int32 |
i32 |
int32_t |
int |
number |
int64 |
long |
pyfory.Int64 |
int64 |
i64 |
int64_t |
long |
bigint | number |
float16 |
Float16 |
pyfory.Float16 |
float16 |
Float16 |
fory::float16_t |
Half |
number |
bfloat16 |
BFloat16 |
pyfory.BFloat16 |
bfloat16 |
BFloat16 |
fory::bfloat16_t |
BFloat16 |
number |
float32 |
float |
pyfory.Float32 |
float32 |
f32 |
float |
float |
number |
float64 |
double |
pyfory.Float64 |
float64 |
f64 |
double |
double |
number |
string |
String |
str |
string |
String |
std::string |
string |
string |
bytes |
byte[] |
bytes |
[]byte |
Vec<u8> |
std::vector<uint8_t> |
byte[] |
Uint8Array |
date |
LocalDate |
datetime.date |
time.Time |
fory::Date |
fory::Date |
DateOnly |
Date |
timestamp |
Instant |
datetime.datetime |
time.Time |
fory::Timestamp |
fory::Timestamp |
DateTimeOffset |
Date |
Collection Types
list<string> tags = 1; // List<String>
array<int32> dense_numbers = 2; // Packed dense int32 array
map<string, fixed int32> scores = 3; // Map<String, fixed-width Integer>
Field Modifiers
optional: Field can be null/Noneref: Enable reference tracking for shared/circular referenceslist<T>: Ordered collection schema (alias:repeated T)array<T>: Dense numeric/vector schema
message Example {
optional string nullable_field = 1;
ref OtherMessage shared_ref = 2;
list<int32> numbers = 3;
list<fixed int32> offsets = 4;
array<float32> embedding = 5;
}
Service Definition
Define gRPC services alongside message types in the same FDL file:
package demo.greeter;
message HelloRequest {
string name = 1;
}
message HelloReply {
string reply = 1;
}
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc StreamReplies (HelloRequest) returns (stream HelloReply);
rpc CollectRequests (stream HelloRequest) returns (HelloReply);
rpc Chat (stream HelloRequest) returns (stream HelloReply);
}
Each rpc declaration supports four streaming modes:
| Mode | Syntax |
|---|---|
| Unary | rpc Method (Req) returns (Res) |
| Server streaming | rpc Method (Req) returns (stream Res) |
| Client streaming | rpc Method (stream Req) returns (Res) |
| Bidirectional | rpc Method (stream Req) returns (stream Res) |
Fory Options
FDL uses plain option keys without a (fory) prefix:
File-level options:
option use_record_for_java_message = true;
option polymorphism = true;
option enable_auto_type_id = true;
enable_auto_type_id defaults to true. Set it to false to keep name-based registration
for types that omit explicit IDs.
Message/Enum options:
message MyMessage [id=100] {
option evolving = false;
option use_record_for_java = true;
string name = 1;
}
enum Status [id=101] {
UNKNOWN = 0;
ACTIVE = 1;
}
Field options:
message Example {
ref MyType friend = 1;
string nickname = 2 [nullable=true];
ref MyType data = 3 [nullable=true];
ref(weak=true) MyType parent = 4;
}
Architecture
fory_compiler/
├── __init__.py # Package exports
├── __main__.py # Module entry point
├── cli.py # Command-line interface
├── frontend/
│ └── fdl/
│ ├── __init__.py
│ ├── lexer.py # Hand-written tokenizer
│ └── parser.py # Recursive descent parser
├── ir/
│ ├── __init__.py
│ ├── ast.py # Canonical Fory IDL AST (Schema, Message, Enum, Service, RpcMethod)
│ ├── validator.py # Schema validation
│ └── emitter.py # Optional FDL emitter
└── generators/
├── base.py # Base generator class and GeneratorOptions
├── java.py # Java POJO generator
├── python.py # Python dataclass generator
├── go.py # Go struct generator
├── rust.py # Rust struct generator
├── cpp.py # C++ struct generator
├── csharp.py # C# class generator
├── javascript.py # JavaScript interface generator
└── services/
├── base.py # StreamingMode enum and shared helpers
├── java.py # Java gRPC stub generator (grpc-java style)
├── python.py # Python gRPC companion module (grpcio style)
├── go.py # Go gRPC stub generator (google.golang.org/grpc)
├── rust.py # Rust gRPC service module (tonic style)
├── csharp.py # C# gRPC service companion (Grpc.Core style)
├── javascript.py # JavaScript Node.js and gRPC-Web client generators
├── dart.py # Dart gRPC service companion
├── kotlin.py # Kotlin coroutine gRPC service companion
└── scala.py # Scala gRPC service companion
FDL Frontend
The FDL frontend is a hand-written lexer/parser that produces the Fory IDL AST:
- Lexer (
frontend/fdl/lexer.py): Tokenizes FDL source into tokens - Parser (
frontend/fdl/parser.py): Builds the AST from the token stream - AST (
ir/ast.py): Canonical node types -Schema,Message,Enum,Field,FieldType
Generators
Each generator extends BaseGenerator and implements:
generate(): Returns list ofGeneratedFileobjects for type definitionsgenerate_type(): Converts FDL types to target language typesgenerate_services(): Returns gRPC service companion files when--grpcis set- Language-specific registration helpers or modules
Service generators live in generators/services/ as mixins and are combined with the
corresponding type generator via multiple inheritance in each language generator class.
Generated Output
Java
Generates POJOs with:
- Private fields with getters/setters
@Nullableannotations for nullable fields and@Refannotations for ref fields- Schema module class
public class Cat {
@Ref
private Dog friend;
@Nullable
private String name;
private List<String> tags;
// ...
}
Python
Generates dataclasses with:
- Type hints
- Default values
- Registration function
@dataclass
class Cat:
friend: Optional[Dog] = None
name: Optional[str] = None
tags: List[str] = None
Go
Generates structs with:
- Fory struct tags
- Pointer types for nullable fields
- Registration function with error handling
type Cat struct {
Friend *Dog `fory:"ref"`
Name *string `fory:"nullable"`
Tags []string
}
Rust
Generates structs with:
#[derive(ForyStruct)],#[derive(ForyEnum)], and#[derive(ForyUnion)]macros#[fory(...)]field attributes- a registration helper for name-based registration
#[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
pub struct Cat {
pub friend: Arc<Dog>,
#[fory(nullable = true)]
pub name: Option<String>,
pub tags: Vec<String>,
}
C++
Generates structs with:
FORY_STRUCTmacro for serializationstd::optionalfor nullable fieldsstd::shared_ptrfor ref fields
struct Cat {
std::shared_ptr<Dog> friend;
std::optional<std::string> name;
std::vector<std::string> tags;
int32_t scores;
int32_t lives;
FORY_STRUCT(Cat, friend, name, tags, scores, lives);
};
C#
Generates classes with:
[ForyStruct],[ForyEnum], and[ForyUnion]model attributes- Auto-properties for schema fields
- Registration helper class and
ToBytes/FromByteshelpers
[ForyStruct]
public sealed partial class Cat
{
public Dog? Friend { get; set; }
public string Name { get; set; } = string.Empty;
public List<string> Tags { get; set; } = new();
}
For full C# IDL verification (including root cross-package imports and file-based roundtrip paths), run:
cd integration_tests/idl_tests
./run_csharp_tests.sh
JavaScript
Generates interfaces with:
export interfacedeclarations for messagesexport enumdeclarations for enums- Discriminated unions with case enums
- Registration helper function
export interface Cat {
friend?: Dog | null;
name?: string | null;
tags: string[];
scores: Map<string, number>;
lives: number;
}
gRPC Service Generation
Pass --grpc to generate gRPC service stubs alongside type definitions for all selected
languages that support service generation (Java, Python, Go, Rust, C#, JavaScript, Dart,
Kotlin, and Scala). Stubs use Fory serialization as the on-wire codec.
# Generate type definitions and gRPC stubs
foryc examples/service.fdl --lang java,python,go --grpc --output ./generated
# JavaScript gRPC-Web client (requires --grpc-web, implies JavaScript output)
foryc examples/service.fdl --javascript_out=./gen/js --grpc-web
# Python async mode (default) or sync mode
foryc examples/service.fdl --python_out=./gen/python --grpc --grpc-python-mode sync
Generated gRPC Output
For each language the compiler emits one gRPC companion file per schema file.
The following examples use the schema from examples/service.fdl:
package demo.greeter;
message HelloRequest { string name = 1; }
message HelloReply { string reply = 1; }
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
Java
Generates <ServiceName>Grpc.java with a grpc-java-style companion class:
- Method descriptors with double-checked-locking initialization
<ServiceName>ImplBaseabstract server base<ServiceName>Stub(async),<ServiceName>BlockingStub, and<ServiceName>FutureStubclient stubs- Factory methods
newStub,newBlockingStub, andnewFutureStub - Fory-backed marshaller shared by all methods in the class
// GreeterGrpc.java (demo/greeter/GreeterGrpc.java)
public final class GreeterGrpc {
public static final String SERVICE_NAME = "demo.greeter.Greeter";
public abstract static class GreeterImplBase implements io.grpc.BindableService {
public void sayHello(HelloRequest request,
io.grpc.stub.StreamObserver<HelloReply> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(
getSayHelloMethod(), responseObserver);
}
@Override
public final io.grpc.ServerServiceDefinition bindService() {
return GreeterGrpc.bindService(this);
}
}
public static final class GreeterStub
extends io.grpc.stub.AbstractAsyncStub<GreeterStub> { ... }
public static final class GreeterBlockingStub
extends io.grpc.stub.AbstractBlockingStub<GreeterBlockingStub> { ... }
public static final class GreeterFutureStub
extends io.grpc.stub.AbstractFutureStub<GreeterFutureStub> { ... }
}
Python
Generates <module>_grpc.py with a grpcio-style companion module. The default API
mode is async (grpc.aio); pass --grpc-python-mode sync for the classic sync API:
<ServiceName>Stubclient class wired to the Fory serializer/deserializer pair<ServiceName>Servicerserver base withUNIMPLEMENTEDstubs- Per-service registration helper and a top-level
add_servicer(servicer, server)dispatcher
# demo_greeter_grpc.py
class GreeterStub(object):
"""Client stub for Greeter."""
def __init__(self, channel):
self.say_hello = channel.unary_unary(
"/demo.greeter.Greeter/SayHello",
request_serializer=_serialize,
response_deserializer=_deserialize,
)
class GreeterServicer(object):
"""AsyncIO base servicer for Greeter."""
async def say_hello(self, request, context):
await context.abort(grpc.StatusCode.UNIMPLEMENTED, "Method not implemented!")
def add_servicer(servicer, server): ...
Go
Generates <file>_grpc.go with a google.golang.org/grpc-compatible stub file:
CodecV2implementinggrpc/encoding.CodecV2using the Fory thread-safe runtime<ServiceName>Clientinterface andNew<ServiceName>Clientconstructor<ServiceName>Serverinterface andUnimplemented<ServiceName>Serverstruct- Per-streaming-mode send/receive stream types
Register<ServiceName>Serverand aServiceDescvariable
// greeter_grpc.go
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest,
opts ...grpc.CallOption) (*HelloReply, error)
}
func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { ... }
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
mustEmbedUnimplementedGreeterServer()
}
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) { ... }
Rust
Generates two files: <module>_api.rs (service trait definitions) and
<module>_grpc.rs (tonic-compatible client/server modules):
- A service trait per service name
<service_name>_clientand<service_name>_serversubmodules compatible with tonic- Fory codec registered via the
<SERVICE>_SERVICE_NAMEconstant
// greeter_grpc.rs
pub mod greeter_client {
pub struct GreeterClient<T> { inner: tonic::client::Grpc<T> }
impl<T> GreeterClient<T> {
pub async fn say_hello(&mut self, request: impl tonic::IntoRequest<HelloRequest>)
-> std::result::Result<tonic::Response<HelloReply>, tonic::Status> { ... }
}
}
pub mod greeter_server {
pub trait Greeter: std::marker::Send + std::marker::Sync + 'static {
async fn say_hello(&self, request: tonic::Request<HelloRequest>)
-> std::result::Result<tonic::Response<HelloReply>, tonic::Status>;
}
}
C#
Generates <ServiceName>Grpc.cs with a Grpc.Core-style partial class:
- Static Fory marshallers for each distinct request/response type pair
Method<TReq, TRes>descriptors for each RPC<ServiceName>Baseabstract server base class<ServiceName>Clientclient classBindServicehelper for server-side registration
// GreeterGrpc.cs
public static partial class Greeter
{
static readonly string __ServiceName = "demo.greeter.Greeter";
public abstract class GreeterBase
{
public virtual Task<HelloReply> SayHello(
HelloRequest request, grpc::ServerCallContext context)
=> throw new grpc::RpcException(new grpc::Status(
grpc::StatusCode.Unimplemented, ""));
}
public class GreeterClient : grpc::ClientBase<GreeterClient>
{
public virtual HelloReply SayHello(
HelloRequest request, grpc::CallOptions options = default) { ... }
}
}
JavaScript
Generates <module>_grpc.js (Node.js, --grpc) and/or <module>_grpc_web.js
(browser, --grpc-web) TypeScript/JavaScript companion modules:
<ServiceName>Clientclass extendinggrpc.Client(Node) or a gRPC-Web base (browser)- Per-method call wrappers using the Fory serializer/deserializer pair
- A
<ServiceName>Servicedescriptor object for server-side registration (Node)
// greeter_grpc.js (Node)
export class GreeterClient extends grpc.Client {
sayHello(argument, metadata, options, callback) { ... }
}
export const GreeterService = {
sayHello: { path: "/demo.greeter.Greeter/SayHello", ... },
};
Dart
Generates <file>_grpc.dart with a dart-grpc-compatible companion:
<ServiceName>Clientclass extendinggrpc.Client<ServiceName>ServiceBaseabstract server base class- Fory codec passed as the
serialize/deserializepair on eachClientMethod
Kotlin
Generates <ServiceName>GrpcKt.kt with grpc-kotlin coroutine companions:
<ServiceName>CoroutineImplBaseabstract server base using suspend functions<ServiceName>CoroutineStubcoroutine client stub- Fory serialization used as the gRPC marshaller
Scala
Generates <ServiceName>GrpcScala.scala with ZIO/Monix-friendly stubs:
<ServiceName>Grpcobject with abindServicemethod for server registration<ServiceName>Stubclient class- Fory codec registered as the channel marshaller
CLI Reference
foryc [OPTIONS] FILES...
Arguments:
FILES FDL files to compile
Options:
--lang TEXT Target languages (java,python,cpp,rust,go,csharp,
javascript,swift,dart,scala,kotlin or "all")
Default: all
--output, -o PATH Output directory
Default: ./generated
--java_out DST_DIR Generate Java code in DST_DIR
--python_out DST_DIR Generate Python code in DST_DIR
--go_out DST_DIR Generate Go code in DST_DIR
--rust_out DST_DIR Generate Rust code in DST_DIR
--cpp_out DST_DIR Generate C++ code in DST_DIR
--csharp_out DST_DIR Generate C# code in DST_DIR
--javascript_out DST_DIR Generate JavaScript code in DST_DIR
--swift_out DST_DIR Generate Swift code in DST_DIR
--dart_out DST_DIR Generate Dart code in DST_DIR
--scala_out DST_DIR Generate Scala 3 code in DST_DIR
--kotlin_out DST_DIR Generate Kotlin code in DST_DIR
-I PATH Add a directory to the import search path
--grpc Generate gRPC service stubs alongside type definitions
--grpc-web Generate JavaScript gRPC-Web client code
--grpc-python-mode MODE Python gRPC API style: async (default) or sync
--help Show help message
Examples
See the examples/ directory for sample FDL files and generated output.
# Compile the demo schema
foryc examples/demo.fdl --output examples/generated
Development
# Install in development mode
pip install -e .
# Run the compiler
python -m fory_compiler compile examples/demo.fdl
# Or use the installed command
foryc examples/demo.fdl
License
Apache License 2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fory_compiler-1.5.0.tar.gz.
File metadata
- Download URL: fory_compiler-1.5.0.tar.gz
- Upload date:
- Size: 278.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95030941855c822ea2db0c6ed0241b78c308137091f781c9c9a6ad9b7d8bcd74
|
|
| MD5 |
d905d003f330d2efe14b02a88a7e8024
|
|
| BLAKE2b-256 |
1c4ccd91697f677850424bbc909f6042b6edd77150bcc7ef2dc179d1871f98b2
|
Provenance
The following attestation bundles were made for fory_compiler-1.5.0.tar.gz:
Publisher:
release-compiler.yaml on apache/fory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fory_compiler-1.5.0.tar.gz -
Subject digest:
95030941855c822ea2db0c6ed0241b78c308137091f781c9c9a6ad9b7d8bcd74 - Sigstore transparency entry: 2298836448
- Sigstore integration time:
-
Permalink:
apache/fory@36f6d21abfaf75966b033524d3f710c17f0910d9 -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/apache
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-compiler.yaml@36f6d21abfaf75966b033524d3f710c17f0910d9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fory_compiler-1.5.0-py3-none-any.whl.
File metadata
- Download URL: fory_compiler-1.5.0-py3-none-any.whl
- Upload date:
- Size: 327.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab583f3c64bb2824fd4d16644ab8a064c782133e339fc9fb4920bf87673e49cf
|
|
| MD5 |
70cdcc889199b62aac4cb33027b954ad
|
|
| BLAKE2b-256 |
2413592f455c51bca13a1e5b59f1e8ceab81fc991d9dba6f692f496b2cefc2c6
|
Provenance
The following attestation bundles were made for fory_compiler-1.5.0-py3-none-any.whl:
Publisher:
release-compiler.yaml on apache/fory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fory_compiler-1.5.0-py3-none-any.whl -
Subject digest:
ab583f3c64bb2824fd4d16644ab8a064c782133e339fc9fb4920bf87673e49cf - Sigstore transparency entry: 2298836463
- Sigstore integration time:
-
Permalink:
apache/fory@36f6d21abfaf75966b033524d3f710c17f0910d9 -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/apache
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-compiler.yaml@36f6d21abfaf75966b033524d3f710c17f0910d9 -
Trigger Event:
push
-
Statement type: