Builder Layer Semantics
May 19, 2026 ยท View on GitHub
Goal
The builder layer converts finalized repr data into reusable export fragments.
This layer sits between:
- parser/linker-owned semantic data
- compiler root / emitters that need concrete output pieces
Its purpose is not to fully generate all code. Its purpose is to answer:
- what does this symbol contribute?
- which declarations should exist?
- which
MINITlines should be emitted? - which function table entries should be registered?
Location
vphp/compiler/builder/
class.v
function.v
constant.v
module.v
fragments.v
Design Principle
The builder layer should prefer:
- normalized inputs
- small reusable outputs
- low policy
That means:
- parser decides what a class means
- linker resolves relationships
- builder turns those facts into export-ready parts
If a builder needs to understand raw AST, something is in the wrong layer.
Main Builders
ClassBuilder
Defined in class.v.
This is the central type export builder.
It is used for:
- PHP classes
- PHP interfaces
- enum-style class exports
ClassType
ClassBuilder is parameterized by:
.class_.interface_.enum_
This allows one builder to share:
- class entry declaration
- method table generation
- arginfo generation
MINITregistration
while still branching in the few places where PHP type behavior differs.
Internal Data Model
ClassBuilder stores:
-
type- class/interface/enum classification
-
php_name- final PHP-visible symbol name
-
c_name- C-safe name used in generated code
-
parent- parent class if any
-
create_object- whether Zend object creation handler should be installed
-
class_flags- low-level Zend class flags
-
interfaces- explicit interfaces to attach
-
properties- normalized class properties ready for declaration
-
constants- normalized class constants ready for declaration
-
methods- normalized methods ready for arginfo and method table emission
Mutating API
ClassBuilder is intentionally incremental.
Callers build it step-by-step:
set_parent(...)set_create_object(...)add_class_flag(...)add_interface(...)add_property(...)add_constant(...)add_method(...)add_abstract_method(...)
This lets c_emitter.v translate repr into a builder without directly formatting C strings everywhere.
Render Methods
ClassBuilder currently produces:
render_ce_declaration()render_ce_extern_declaration()render_arginfo_defs()render_methods_array()render_minit()render_impl_prelude()render_impl_postlude()
These methods are intentionally lower-level than full-file generation.
They return partial artifacts that can be assembled elsewhere.
export_fragments()
This is the most important builder-facing contract.
For ClassBuilder, the exported fragments currently include:
-
declarations
- class entry extern declaration
-
minit_lines- class/interface/enum registration block
Implementations are still partly supplied later by c_emitter.v, because wrapper bodies are not fully generic yet.
FuncBuilder
Defined in function.v.
Purpose:
- represent one PHP global function export in builder form
Stored data:
php_namec_func
Rendered outputs:
render_declaration()render_arginfo()render_fe()
export_fragments() contributes:
- function declaration
- function table entry
It does not produce full function implementations.
Those are still completed in c_emitter.v.
ConstantBuilder
Defined in constant.v.
Purpose:
- represent one PHP global constant registration
Stored data:
nametype_value
Rendered output:
render_register()
export_fragments() contributes:
MINITregistration lines
This builder is intentionally small and focused.
ModuleBuilder
Defined in module.v.
Purpose:
- assemble the final extension-level C scaffolding
This is the only builder that operates at the module level rather than the symbol level.
It owns:
- function table assembly
MINITMSHUTDOWNMINFO- globals struct/ginit generation
- module entry generation
Stored Data
ext_nameversiondescriptionfunctionsminit_contentini_entriesglobals
Important Distinction
ModuleBuilder is not a replacement for ExportFragments.
Instead:
- symbol builders produce fragments
export.vaggregates fragmentsModuleBuilderconsumes some of those fragments to build extension-level output
So ModuleBuilder is downstream from the symbol builders.
ExportFragments
Defined in fragments.v.
This is the transport object for the builder layer.
Fields:
declarationsimplementationsminit_linesfunction_table
Why it exists
Without ExportFragments, the compiler root would need to know too much about each symbol category:
- where declarations go
- which lines belong to
MINIT - how functions enter the function table
With fragments:
- each builder/export path contributes partial output
- assembly becomes a merge operation
merge(...)
merge(...) is intentionally simple concatenation.
Ordering decisions are made by the caller.
This is important because:
- interfaces must currently be collected before classes that implement them
- function and type exports are sometimes collected in separate passes
ExportFragments does not enforce ordering policy.
It only carries pieces.
Builder Boundaries
The builder layer should handle:
- Zend declaration boilerplate
- class/property/constant registration fragments
- reusable function table pieces
- module-level C scaffolding
The builder layer should not handle:
- AST parsing
- linker reconciliation
- large wrapper body templates
- V-side runtime glue
Current Collaboration Pattern
The current pattern is:
- parser builds
repr - linker enriches
repr c_emitter.vmapsrepr -> builder- builders emit fragments
export.vassembles fragmentsModuleBuilderrenders the final module-level blocks
This means the builder layer is both:
- a normalization boundary
- a reuse boundary
Current Limitations
These are known and acceptable for now.
ClassBuilder.export_fragments()does not yet carry full class implementationsFuncBuilderstill relies onc_emitter.vfor wrapper bodiesModuleBuilderis string-heavy and still fairly C-specific
These are not design failures. They simply show which parts of the pipeline are fully generic today and which are still emitter-owned.
Good Future Evolutions
Examples of healthy builder growth:
- richer arginfo modeling
- more reusable property/constant declaration helpers
- more implementation scaffolding moving from
c_emitter.vinto builders - specialized builders for future features such as exceptions or traits
Examples of unhealthy builder growth:
- directly walking AST
- embedding parser policy
- embedding runtime sync logic
Summary
The builder layer is the compiler's export normalization layer.
It takes stable semantic data and turns it into reusable code fragments that the rest of the compiler can assemble predictably.
If repr is the compiler's middle language, builder is the compiler's export language.