tree: 5f23f228388915774e32d9064f0647fc9842258c [path history] [tgz]
  1. examples/
  2. extension/
  3. fory_compiler/
  4. pyproject.toml
  5. README.md
compiler/README.md

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++
  • Rich type system: Primitives, enums, messages, lists, 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, repeated fields
  • File imports: Modular schemas with import support

Documentation

For comprehensive documentation, see the FDL Schema 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;
    repeated 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 --output ./generated

# Override package name
foryc schema.fdl --package myapp.models --output ./generated

# Language-specific output directories (protoc-style)
foryc schema.fdl --java_out=./src/main/java --python_out=./python/src

# Combine with other options
foryc schema.fdl --java_out=./gen --go_out=./gen/go -I ./proto

3. Use Generated Code

Java:

import demo.*;
import org.apache.fory.Fory;

Fory fory = Fory.builder().build();
DemoForyRegistration.register(fory);

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()
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 namespace-based registration:

message Config { ... }  // Registered as "package.Config"

Primitive Types

FDL TypeJavaPythonGoRustC++
boolbooleanboolboolboolbool
int8bytepyfory.int8int8i8int8_t
int16shortpyfory.int16int16i16int16_t
int32intpyfory.int32int32i32int32_t
int64longpyfory.int64int64i64int64_t
float32floatpyfory.float32float32f32float
float64doublepyfory.float64float64f64double
stringStringstrstringStringstd::string
bytesbyte[]bytes[]byteVec<u8>std::vector<uint8_t>
dateLocalDatedatetime.datetime.Timechrono::NaiveDatefory::Date
timestampInstantdatetime.datetimetime.Timechrono::NaiveDateTimefory::Timestamp

Collection Types

repeated string tags = 1;           // List<String>
map<string, int32> scores = 2;      // Map<String, Integer>

Field Modifiers

  • optional: Field can be null/None
  • ref: Enable reference tracking for shared/circular references
  • repeated: Field is a list/array
message Example {
    optional string nullable_field = 1;
    ref OtherMessage shared_ref = 2;
    repeated int32 numbers = 3;
}

Fory Options

FDL uses plain option keys without a (fory) prefix:

File-level options:

option use_record_for_java_message = true;
option polymorphism = true;

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
│   ├── validator.py      # Schema validation
│   └── emitter.py        # Optional FDL emitter
└── generators/
    ├── base.py           # Base generator class
    ├── 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

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 of GeneratedFile objects
  • generate_type(): Converts FDL types to target language types
  • Language-specific registration helpers

Generated Output

Java

Generates POJOs with:

  • Private fields with getters/setters
  • @ForyField annotations for nullable/ref fields
  • Registration helper class
public class Cat {
    @ForyField(ref = true)
    private Dog friend;

    @ForyField(nullable = true)
    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(ForyObject)] macro
  • #[fory(...)] field attributes
  • a registration helper for namespace-based registration
#[derive(ForyObject, 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_STRUCT macro for serialization
  • std::optional for nullable fields
  • std::shared_ptr for 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);
};

CLI Reference

foryc [OPTIONS] FILES...

Arguments:
  FILES                 FDL files to compile

Options:
  --lang TEXT          Target languages (java,python,cpp,rust,go or "all")
                       Default: all
  --output, -o PATH    Output directory
                       Default: ./generated
  --package TEXT       Override package name from FDL file
  --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