Summary
Add a -flat flag to the project directive in gox.mod. In flat mode, there are no true work classfiles — instead, all matching files are fragments of the project class. Their top-level code is merged into a single _xgo_WorkMain method on the project class rather than into individual Main methods on separate work class types.
Motivation
The current class framework model requires work classfiles to be structurally independent: each becomes its own Go struct with its own Main method. This works well for naturally distinct units of work (sprites, CLI commands, HTTP handlers, MCP tools), but it is not a good fit for projects that want to split a single large classfile into multiple files for organisational reasons without introducing any structural subdivision.
Flat mode addresses this gap. It allows the framework author to offer a "project-only" experience where the application developer can spread their logic across multiple files, all of which are treated as parts of one unified project class.
Design
gox.mod Syntax
The -flat flag is added to the project directive:
project -flat <filePattern> <BaseType> <importPath> [extraImports...]
When -flat is present:
- The first file matching
<filePattern> is the primary project classfile (same as without -flat). Top-level code in it is compiled into MainEntry() as usual.
- All remaining files matching
<filePattern> are project fragments. They are not treated as separate work classfiles. Instead:
- They share the same generated struct type as the project class (they are aliases of it, not separate types).
- Their top-level code is merged into a single method named
_xgo_WorkMain on the project class.
- No
class directive is needed or expected when -flat is used.
Invariant: -flat and class directives are mutually exclusive within the same framework. A gox.mod that declares project -flat must not also declare any class directives.
Compiler Behaviour
Struct generation
Only one struct is generated for the entire project — the project class. All fragment files contribute member declarations to this single struct. There is no second struct type.
_xgo_WorkMain method
If one or more fragment files exist, the compiler synthesises:
func (this *ProjectClass) _xgo_WorkMain() {
// top-level code from fragment file 1
// top-level code from fragment file 2
// ... in file order
}
The order of fragments follows the same deterministic ordering the compiler uses for work classfiles today (typically lexicographic by filename).
If no fragment files exist (i.e. only the primary project classfile is present), _xgo_WorkMain is not generated, and nil is passed to the Template Recv Method instead.
Synthesised Main method
The compiler synthesises Main on the project class as follows:
With fragment files:
func (this *ProjectClass) Main() {
XGot_<BaseType>_Main(this, this._xgo_WorkMain)
}
Without fragment files:
func (this *ProjectClass) Main() {
XGot_<BaseType>_Main(this, nil)
}
Global entry point
Unchanged from the standard case:
func main() {
new(ProjectClass).Main()
}
Template Recv Method
The framework author writes:
func XGot_<BaseType>_Main(app <ProjectProto>, workMain func())
app — the project class instance, typed as the protocol interface <ProjectProto> that the project class satisfies.
workMain — a bound method value this._xgo_WorkMain, or nil when no fragment files are present.
The framework implementation decides when (and whether) to call workMain. A typical implementation calls it once after setup:
func XGot_MyBase_Main(app iMyProto, workMain func()) {
app.Init()
if workMain != nil {
workMain()
}
app.Run()
}
Example: A Hypothetical Single-Page Script Framework
To illustrate flat mode concretely, consider a framework scriptfw where the application is logically one script but can be split across files. The framework uses the dedicated suffix *.script (alternatively, *_script.gox would also be valid).
gox.mod
xgo 1.7
project -flat *.script ScriptApp github.com/example/scriptfw
Example project
main.script // primary project classfile
helpers.script // fragment: helper logic
tasks.script // fragment: task definitions
Generated Go code
// Unified project struct — all files contribute to this one type
type MyProject struct {
scriptfw.ScriptApp
// member declarations from main.script, helpers.script, tasks.script
}
// From main.script — primary classfile
func (this *MyProject) MainEntry() {
// top-level code from main.script
}
// Synthesised from helpers.script + tasks.script fragments
func (this *MyProject) _xgo_WorkMain() {
// top-level code from helpers.script (in file order)
// top-level code from tasks.script
}
// Synthesised by the compiler
func (this *MyProject) Main() {
scriptfw.XGot_ScriptApp_Main(this, this._xgo_WorkMain)
}
func main() {
new(MyProject).Main()
}
Template Recv Method
func XGot_ScriptApp_Main(app iScriptProto, workMain func())
Comparison with Standard Mode
| Aspect |
Standard mode |
Flat mode (-flat) |
| Work classfiles |
Each becomes its own struct type |
No separate struct; all files share the project class |
| Top-level code in non-primary files |
Compiled into each work class's Main() |
Merged into _xgo_WorkMain() on the project class |
| Template Recv Method second parameter |
sprites ...Sprite / []ToolProto / etc. |
workMain func() |
class directives required |
Yes, for each work class kind |
Not allowed |
-embed flag |
Applicable |
Not applicable (no work classes to embed) |
| No non-primary files |
Template Recv Method receives zero work instances |
Template Recv Method receives nil for workMain |
gox.mod Grammar (updated)
project [-flat] <filePattern> <BaseType> <importPath> [extraImports...]
class [flags] <filePattern> <BaseType> [ProtocolType]
The -flat flag is only valid on the project line. Specifying it together with any class directive in the same gox.mod is a compile-time error.
When -flat is used, <filePattern> must be a dedicated framework suffix — either a custom extension (*.spx, *.yap, *.script, …) or the *_<class>.gox form (*_script.gox, …). The bare *.gox pattern is rejected at parse time.
Summary of Changes
| Component |
Change |
gox.mod parser |
Recognise -flat flag on project directive; reject *.gox as pattern when -flat is set |
| Classfile classifier |
In flat mode, treat all <filePattern> matches as project-class fragments rather than work classfiles |
| Code generator |
In flat mode, emit _xgo_WorkMain (if fragments exist) and adjust synthesised Main accordingly |
| Validation |
Error if -flat and class directives coexist in one gox.mod |
| Documentation |
Update class framework reference with flat mode section |
Summary
Add a
-flatflag to theprojectdirective ingox.mod. In flat mode, there are no true work classfiles — instead, all matching files are fragments of the project class. Their top-level code is merged into a single_xgo_WorkMainmethod on the project class rather than into individualMainmethods on separate work class types.Motivation
The current class framework model requires work classfiles to be structurally independent: each becomes its own Go struct with its own
Mainmethod. This works well for naturally distinct units of work (sprites, CLI commands, HTTP handlers, MCP tools), but it is not a good fit for projects that want to split a single large classfile into multiple files for organisational reasons without introducing any structural subdivision.Flat mode addresses this gap. It allows the framework author to offer a "project-only" experience where the application developer can spread their logic across multiple files, all of which are treated as parts of one unified project class.
Design
gox.modSyntaxThe
-flatflag is added to theprojectdirective:When
-flatis present:<filePattern>is the primary project classfile (same as without-flat). Top-level code in it is compiled intoMainEntry()as usual.<filePattern>are project fragments. They are not treated as separate work classfiles. Instead:_xgo_WorkMainon the project class.classdirective is needed or expected when-flatis used.Compiler Behaviour
Struct generation
Only one struct is generated for the entire project — the project class. All fragment files contribute member declarations to this single struct. There is no second struct type.
_xgo_WorkMainmethodIf one or more fragment files exist, the compiler synthesises:
The order of fragments follows the same deterministic ordering the compiler uses for work classfiles today (typically lexicographic by filename).
If no fragment files exist (i.e. only the primary project classfile is present),
_xgo_WorkMainis not generated, andnilis passed to the Template Recv Method instead.Synthesised
MainmethodThe compiler synthesises
Mainon the project class as follows:With fragment files:
Without fragment files:
Global entry point
Unchanged from the standard case:
Template Recv Method
The framework author writes:
app— the project class instance, typed as the protocol interface<ProjectProto>that the project class satisfies.workMain— a bound method valuethis._xgo_WorkMain, ornilwhen no fragment files are present.The framework implementation decides when (and whether) to call
workMain. A typical implementation calls it once after setup:Example: A Hypothetical Single-Page Script Framework
To illustrate flat mode concretely, consider a framework
scriptfwwhere the application is logically one script but can be split across files. The framework uses the dedicated suffix*.script(alternatively,*_script.goxwould also be valid).gox.modExample project
Generated Go code
Template Recv Method
Comparison with Standard Mode
-flat)Main()_xgo_WorkMain()on the project classsprites ...Sprite/[]ToolProto/ etc.workMain func()classdirectives required-embedflagnilforworkMaingox.modGrammar (updated)The
-flatflag is only valid on theprojectline. Specifying it together with anyclassdirective in the samegox.modis a compile-time error.When
-flatis used,<filePattern>must be a dedicated framework suffix — either a custom extension (*.spx,*.yap,*.script, …) or the*_<class>.goxform (*_script.gox, …). The bare*.goxpattern is rejected at parse time.Summary of Changes
gox.modparser-flatflag onprojectdirective; reject*.goxas pattern when-flatis set<filePattern>matches as project-class fragments rather than work classfiles_xgo_WorkMain(if fragments exist) and adjust synthesisedMainaccordingly-flatandclassdirectives coexist in onegox.mod