diff --git a/doc/classfile-resource-spec.md b/doc/classfile-resource-spec.md new file mode 100644 index 000000000..b25e3fe49 --- /dev/null +++ b/doc/classfile-resource-spec.md @@ -0,0 +1,500 @@ +# The XGo classfile resource specification + +This document defines the syntax and semantics of resources in XGo classfile frameworks. + +A resource is a framework-defined named entity that is visible to static analysis. + +In particular, this document defines: +- how a framework declares resource kinds and type-level resource bindings +- how a framework discovers concrete resource instances through DQL over pack documents +- how a framework declares API-position scope bindings for scoped resource references +- how work classfiles may imply top-level resource identities +- how source-level resource references participate in standardized static semantics + +## Terms + +The following terms are used throughout this document: +- Resource kind: a framework-defined resource category such as `sprite`, `sound`, or `sprite.costume` +- Top-level kind: a resource kind with no direct parent kind +- Scoped kind: a resource kind whose instances exist within the scope of another resource kind +- Framework package: the package named by the first package path of one project group +- String-based type: an exported defined type or exported alias whose type has underlying type `string` +- Canonical resource reference type: a string-based type bound to one resource kind +- Handle-bearing type: an exported defined interface type or exported defined struct type whose values may carry one + top-level resource identity of one resource kind +- Local resource name: the name of one resource instance within its direct parent scope, or at top level if its + resource kind is top-level +- Scope chain: the ordered chain of ancestor resource identities from the outermost ancestor to the direct parent +- Scope-unknown reference: a scoped resource reference whose complete scope chain is not statically available +- Framework registration identity: the identity of one project group in the active classfile registry +- Resource identity: the stable logical identity of a resource instance, independent from storage path, manifest path, + URI syntax, and runtime object identity +- Resource instance: one concrete resource available to static analysis in one classfile project under one framework + registration +- Resource-discovery comment: a framework source directive comment that declares one concrete resource discovery query + for one canonical resource reference type +- Resource-name-discovery comment: a framework source directive comment that declares one local resource name discovery + query for one canonical resource reference type +- Pack document: the pack document defined by the XGo classfile specification +- Discovery origin node: one node matched by a resource-discovery comment query and associated with one discovered + resource instance +- Callable site: one framework function declaration, one framework method declaration, or one interface method spec + declared by one top-level handle-bearing interface type declaration +- API position: the receiver or one numbered parameter position of one callable site +- Resource-api-scope-binding comment: a framework source directive comment that declares one direct API-position scope + binding for one scoped canonical resource reference type parameter position +- Resource-bearing work file kind: a work file kind whose work classfiles each imply one top-level resource instance +- Resource set: the set of concrete resource instances available to one implementation for one classfile project and one + framework registration +- Resource reference: a source-level reference to a resource + +The logical identity of a resource instance is the tuple: +- framework registration identity +- resource kind +- optional scope chain +- local resource name + +In this specification, the resource kind in a resource identity is the instance's own kind. It does not encode any +concrete ancestor identities, even when the kind spelling uses dotted segments such as `sprite.costume.frame`. + +## Notation + +The syntax in this document is specified using EBNF. It uses the same EBNF conventions as the XGo classfile +specification and the Go specification. + +```ebnf +ResourceComment = "//xgo:class:resource" ResourceKind . +ResourceDiscoveryComment = "//xgo:class:resource-discovery" StandardDQLQuery . +ResourceNameDiscoveryComment = "//xgo:class:resource-name-discovery" StandardDQLQuery . +ResourceAPIScopeBindingComment = "//xgo:class:resource-api-scope-binding" ScopeBindingTarget ScopeBindingSource . + +ResourceKind = ResourceSegment { "." ResourceSegment } . +ResourceSegment = lower_letter { lower_letter | decimal_digit | "_" } . +ScopeBindingTarget = ParameterPosition . +ScopeBindingSource = "receiver" | ParameterPosition . +ParameterPosition = "param." decimal_digit { decimal_digit } . +lower_letter = "a" ... "z" . +``` + +The lexical production `decimal_digit` is as in the Go specification. The directive comment form is as in Go directive +comment conventions. + +`StandardDQLQuery` is the remaining text of one resource-discovery or resource-name-discovery comment line and must be +one standard DQL query. + +`ResourceComment` attaches to one immediately following top-level type spec that declares exactly one exported type name +and no type parameters. + +`ParameterPosition` uses zero-based decimal indexing in ordinary parameter source order and must not use leading zeros +other than `0` itself. + +In one dotted `ResourceKind`, each `.` separates one nested resource-kind segment from its direct parent prefix. A +single-segment kind is top-level. A multi-segment kind is scoped. The direct parent kind of one multi-segment kind is +the prefix that remains after removing its final `.` segment. + +## Conformance + +An implementation conforms to this specification if it implements the syntax and semantics defined by this specification +and satisfies every rule stated using the terms "must" and "must not". + +Rules stated using "may" describe permitted behavior. Rules stated using "should" describe recommended behavior. + +Capabilities such as hover, completion, diagnostics, rename, and references are optional tool capabilities. If an +implementation provides one of those capabilities for standardized resource semantics, the capability must satisfy all +applicable "must" and "must not" rules in this specification. + +## Framework source metadata + +Framework source metadata declares which resource kinds exist, how source code refers to them, which canonical resource +reference types carry discovery queries and optional local-name discovery queries, and how framework callable sites may +provide explicit direct scope to scoped resource positions. + +### Resource comments + +A resource comment belongs to the immediately following top-level type spec that declares exactly one exported type name +and no type parameters in the framework package of the containing project group. + +A resource comment on any other declaration, on one grouped type declaration as a whole rather than on one contained +type spec, or in any other package, has no standardized meaning in this specification. + +A declaration must not bear more than one resource comment with standardized meaning. + +A resource kind is declared when a resource comment with standardized meaning names it. + +If the declaration bearing a resource comment is a string-based type: +- the comment declares the canonical resource reference type of the named resource kind +- the declaration is the canonical resource reference type declaration of that kind + +If the declaration bearing a resource comment is an exported defined interface type or exported defined struct type: +- the comment declares a handle-bearing type of the named resource kind +- the named resource kind must be top-level + +If the declaration bearing a resource comment is neither a string-based type nor an exported defined interface type nor +an exported defined struct type, the comment has no standardized meaning in this specification. + +Within one project group: +- the direct parent kind of each scoped kind must be declared in the same project group +- a resource kind must not have more than one canonical resource reference type +- a declaration that bears a resource comment with standardized meaning must belong to the framework package of the + containing project group + +A top-level resource kind may have zero, one, or more handle-bearing types. + +A handle-bearing type declaration does not by itself imply any resource instance and does not by itself define any +source-level resource-binding rule. + +Additional user-facing spellings may be expressed through ordinary Go aliases that reduce to the canonical resource +reference type of a kind and do not bear their own resource comments. + +### Resource-discovery comments + +A resource-discovery comment belongs to the immediately following declaration only if all of the following hold: +- the declaration is the canonical resource reference type declaration of one resource kind +- the declaration is in the framework package of the containing project group + +A resource-discovery comment on any other declaration, in any other package, or on the same declaration as another +resource-discovery comment, has no standardized meaning in this specification. + +Each canonical resource reference type declaration may bear at most one resource-discovery comment with standardized +meaning. + +A resource-discovery comment declares one DQL query for discovering concrete resource instances of that resource kind. + +### Resource-name-discovery comments + +A resource-name-discovery comment belongs to the immediately following declaration only if all of the following hold: +- the declaration is the canonical resource reference type declaration of one resource kind +- the declaration is in the framework package of the containing project group +- the same declaration bears one resource-discovery comment with standardized meaning + +A resource-name-discovery comment on any other declaration, in any other package, or on the same declaration as another +resource-name-discovery comment, has no standardized meaning in this specification. + +Each canonical resource reference type declaration may bear at most one resource-name-discovery comment with +standardized meaning. + +A resource-name-discovery comment declares one DQL query for discovering local resource names of that resource kind +relative to discovery origin nodes. + +### Resource-api-scope-binding comments + +A resource-api-scope-binding comment belongs to the immediately following callable site only if all of the following +hold: +- the callable site is one top-level function declaration, one top-level method declaration, or one method spec declared + by one top-level handle-bearing interface type declaration +- the callable site is in the framework package of the containing project group + +A resource-api-scope-binding comment on any other callable site, in any other package, or on the same callable site as +another resource-api-scope-binding comment with the same target parameter position, has no standardized meaning in this +specification. + +Each callable site may bear zero or more resource-api-scope-binding comments with standardized meaning, but at most one +such comment may target one parameter position. + +A set of resource-api-scope-binding comments with standardized meaning on one callable site must induce an acyclic +directed relation over API positions. + +The meaning of one resource-api-scope-binding comment is independent of its source order relative to other such comments +on the same callable site. + +A resource-api-scope-binding comment declares one direct scope source for its target parameter position. + +## Concrete resource introduction + +Concrete resource introduction in this specification occurs either through resource-discovery comments and optional +resource-name-discovery comments over pack documents or through work classfile implication. + +### Discovery-based introduction + +Resource-discovery comments and optional resource-name-discovery comments introduce project-derived resource instances +over pack documents. + +#### Discovery execution + +For a top-level resource kind, the resource-discovery comment query is evaluated on the root node of the pack document, +if any, derived from the active project group identified by the current framework registration identity. + +For a scoped resource kind whose direct parent kind is ``, the resource-discovery comment query is evaluated +relative to each discovery origin node of each discovered direct parent resource instance of kind ``. + +Relative evaluation means that the discovery origin node is used as the DQL query root for that evaluation. + +If a direct parent resource identity is available only from work-classfile implication and not from any discovery origin +node, that implied identity alone does not create a relative discovery root. + +An implementation must preserve at least one discovery origin node for each discovered resource instance. + +If one discovered resource identity is obtained from more than one discovery origin node, relative child discovery is +evaluated for each such origin node. Child identities obtained from those evaluations are merged by resource identity. + +If a canonical resource reference type declaration bears a resource-name-discovery comment, its query is evaluated +relative to each discovery origin node produced by the resource-discovery comment on that declaration. + +Relative evaluation of a resource-name-discovery comment does not change the discovery origin node associated with the +discovered resource instance and does not create any relative discovery root for child discovery. + +#### Discovery result interpretation + +A successful resource-discovery comment query match contributes one discovered resource instance candidate. + +For the rules below: +- a string scalar value is one pack-document string scalar value +- a node key name is the key by which one matched node appears as one member of its containing pack-document object, if + any +- a string member named `name` is one object member named `name` whose value is a string scalar value + +The candidate's local resource name is determined as follows: +- if a resource-name-discovery comment is present: + - if its relative evaluation for that candidate does not produce exactly one matched node, the candidate is invalid + for discovery and does not contribute a resource instance + - if that matched node denotes a string scalar value, that string value is the local resource name + - otherwise, if the matched node has a non-empty node key name, that key name is the local resource name + - otherwise, if the matched node has a string member named `name`, the value of that member is the local resource name + - otherwise, the candidate is invalid for discovery and does not contribute a resource instance +- otherwise: + - if the discovery origin node has a non-empty node key name, that key name is the local resource name + - otherwise, if the discovery origin node has a string member named `name`, the value of that member is the local + resource name + - otherwise, the candidate is invalid for discovery and does not contribute a resource instance + +For a top-level kind, the discovered identity is `(resource kind, local resource name)`. + +For a scoped kind, the discovered identity is `(resource kind, scope chain, local resource name)`, where the direct +parent identity is inherited from the current relative discovery execution context. + +### Work classfile implication + +If a handle-bearing type declaration bearing a resource comment is the registered work base class declaration of one or +more work file kinds, each such work file kind is resource-bearing. + +A resource-bearing work file kind implies one top-level resource instance for each work classfile of that kind in the +analyzed classfile project. + +The implied local resource name is the class file stem of the work classfile, before any class type naming normalization +or `-prefix=` application. + +The implied resource identity is independent from generated Go type naming, including `-prefix=` adjustments. + +This rule standardizes one classfile-native top-level resource identity. Framework-specific runtime lookup keys, +reflection binding keys, and generated Go identifiers remain framework-defined. + +This rule affects only the static resource model. Compilation and runtime behavior remain unchanged. + +Only work classfiles may imply resources through this rule. + +A work file kind must not imply more than one top-level resource kind through this rule. + +If two or more work classfiles imply the same resource identity in one analyzed classfile project: +- the identities collide +- an implementation may report a duplicate-resource diagnostic +- the colliding inputs must not be treated as distinct resource instances + +Implied resources from project classfiles and scoped classfile-implied resources are outside the scope of this +specification. + +### Identity merging + +If more than one source contributes the same resource identity, including resource-discovery comments and work-classfile +implication: +- they refer to the same logical resource instance +- metadata may merge +- the contributing sources must not change the resource kind, scope chain, or local resource name of that identity + +An implementation may preserve one origin, many origins, or provenance in a different internal form, subject to the +relative-discovery requirements above. + +### Example + +```go +// SpriteName identifies a sprite by name. +// +//xgo:class:resource sprite +//xgo:class:resource-discovery sprites.* +type SpriteName = string + +// SpriteCostumeName identifies a sprite costume by name. +// +//xgo:class:resource sprite.costume +//xgo:class:resource-discovery costumes.* +type SpriteCostumeName = string + +// WidgetName identifies a widget by name. +// +//xgo:class:resource widget +//xgo:class:resource-discovery widgets.* +//xgo:class:resource-name-discovery id +type WidgetName = string + +// SpriteImpl is a handle-bearing type of the sprite resource kind. +// +//xgo:class:resource sprite +type SpriteImpl struct{} +``` + +If one pack document contains `sprites.Hero`, the `sprite` query may discover `sprite(Hero)` and preserve `sprites.Hero` +as one discovery origin node of that resource. The `sprite.costume` query is then evaluated relative to that origin +node, so it may discover costumes of `Hero` without embedding `Hero` into the query text. + +If one `widget` origin node stores its local resource name in a child member `id` rather than in its node key or a child +member `name`, the `resource-name-discovery` query `id` is evaluated relative to that origin node and yields the local +resource name. + +If the containing project group declares `class .spx SpriteImpl`, the resource comment on `SpriteImpl` makes `.spx` +resource-bearing, and each `.spx` work classfile implies one top-level `sprite` resource instance whose local resource +name is the class file stem. + +## Static semantics + +### Resource references + +A source position participates in standardized resource semantics only if ordinary Go typing determines one canonical +resource reference type declaration of one resource kind for that position. + +For this purpose, a canonical resource reference type declaration is determined for a source expression in one of the +following ways: +- the expression's static type is that canonical resource reference type declaration, or can be reduced to it by + following Go alias declarations only +- the surrounding typed position requires that canonical resource reference type, and the expression is assignable to it + under ordinary Go typing rules + +A distinct defined type does not participate in standardized resource semantics solely because both it and a canonical +resource reference type have underlying type `string`. + +A Go alias declaration denotes the same resource kind as that canonical resource reference type if its denoted type can +be reduced, recursively through Go alias declarations only, to the same canonical resource reference type declaration. + +An expression is canonically resource-typed if one canonical resource reference type declaration is determined for it in +that way. + +For one canonically resource-typed expression: +- if the expression is a string literal or a statically evaluable string constant, it is a resource reference candidate +- if the expression cannot be statically evaluated, it remains resource-typed but is not a resolvable resource reference +- for a top-level kind, the lookup key is `(resource kind, local resource name)` +- for a scoped kind, the lookup key is `(resource kind, scope chain, local resource name)` +- if a scoped kind does not have a statically available complete scope chain, the reference is one `scope-unknown` + reference + +### API-position scope bindings + +In this subsection, the callable site is the function declaration, method declaration, or interface method spec to which +one resource-api-scope-binding comment with standardized meaning belongs. + +One resource-api-scope-binding comment has standardized meaning only if all of the following hold: +- the target parameter position exists on the callable site +- the target parameter position is not the variadic parameter position of the callable site +- the target parameter type determines one canonical resource reference type declaration of one scoped kind +- the source API position exists on the callable site +- if the source API position is one parameter position, it is not the variadic parameter position of the callable site +- the source API position determines either: + - one canonical resource reference type declaration of the direct parent kind of the target kind, or + - one handle-bearing type of that direct parent kind + +At one call that resolves to that callable site, the source API position is interpreted as follows: +- if the source API position is `receiver`, the source expression is the call's receiver expression, whether explicit or + implicit +- if the source API position is one parameter position, the source expression is the corresponding argument expression + +One resource-api-scope-binding comment contributes one explicit direct parent identity to its target argument position +only if the source expression yields one exact resource identity of the target kind's direct parent kind under the +resource semantics otherwise available at that source position. + +For this purpose, a source position may itself use explicit scope contributed by other resource-api-scope-binding +comments on the same callable site, subject to the acyclicity rule above. + +If the target kind is multiply scoped, a resource-api-scope-binding comment contributes at most the direct parent +identity. Any additional ancestor levels come from that direct parent identity's own scope chain, if available. + +If a target position receives one explicit direct parent identity from a resource-api-scope-binding comment, that +identity is explicit scope for that target position. + +Example: + +```go +//xgo:class:resource-api-scope-binding param.0 receiver +func (p *SpriteImpl) SetCostume__0(costume SpriteCostumeName) + +//xgo:class:resource-api-scope-binding param.0 receiver +//xgo:class:resource-api-scope-binding param.1 param.0 +func (p *SpriteImpl) SetCostumeAndFrame(costume SpriteCostumeName, frame SpriteCostumeFrameName) +``` + +If one call to `SetCostume__0` yields one exact `sprite` identity at its receiver position, the bound +`SpriteCostumeName` argument position may use that identity as explicit scope. + +If one call to `SetCostumeAndFrame` yields one exact `sprite` identity at its receiver position, `param.0` may use that +identity as explicit scope. If `param.0` then yields one exact `sprite.costume` identity, the bound +`SpriteCostumeFrameName` argument position may use that identity as explicit scope. + +### Scoped owner inference + +If all of the following hold: +- the current source position is inside a work classfile +- the registered work base class declaration of that work file kind bears a resource comment for one top-level kind + `` +- the referenced scoped kind has direct parent kind `` +- no explicit scope is otherwise statically available + +then the statically available direct parent identity is the implied top-level resource of the containing work classfile. + +Example: +- if the declaration of `SpriteImpl` bears `//xgo:class:resource sprite` +- and `class .spx SpriteImpl` is active +- and `SpriteCostumeName` bears `//xgo:class:resource sprite.costume` +- then a scoped `SpriteCostumeName` reference inside `Hero.spx` may use scope `(sprite, Hero)` + +If a resource-api-scope-binding comment contributes explicit scope to one target position, that explicit scope takes +precedence over the narrow rule above for that target position. + +Receiver-bound owner inference, project auto-binding owner inference, and other framework-specific context rules remain +framework-specific. + +If the narrow rule above does not apply, the scoped reference remains `scope-unknown`. + +The narrow rule above contributes at most one statically available parent identity. It does not by itself infer any +additional ancestor levels for multiply scoped kinds. + +If a framework uses plain `string` parameters or resource-like values that do not reduce to canonical resource reference +types, those positions are outside the standardized resource semantics of this specification. + +### Diagnostics + +A conforming implementation: +- may emit a "resource not found" diagnostic only when its framework-specific discovery semantics are exact for the + analyzed project state +- must not emit a "resource not found" diagnostic for dynamic expressions +- must not emit a "resource not found" diagnostic for `scope-unknown` references +- may emit a diagnostic for an empty resource name + +## Tool semantics + +### Hover + +When a source position resolves to a resource reference whose identity is present in the implementation's resource set, +an implementation that provides hover must return information about the corresponding resource instance. + +### Completion + +When the target type at an input position resolves to a resource kind, an implementation that provides completion may +base resource completion candidates on the implementation's resource set. + +For scoped kinds: +- if the scope chain is known, completion must be filtered to that scope chain +- if the scope chain is unknown, an implementation may suppress completion or provide degraded completion annotated with + scope information + +### Rename and references + +If an implementation supports rename or reference lookup for resources, resource identity must be based on +`(framework registration identity, resource kind, optional scope chain, local resource name)` rather than raw string +text. + +## Excluded semantics + +This specification intentionally does not standardize: +- framework-specific API-position scope rules beyond explicit resource-api-scope-binding comments +- API-position kind binding rules for positions that do not already depend on canonical resource reference types +- implied resources from project classfiles +- scoped classfile-implied resources beyond the work-classfile implication rule defined by this specification +- how runtime parameters such as `run(...)` are mapped back into static analysis +- the concrete encoding of preview URIs or editor-specific payloads +- one standardized completeness or exactness model for resource discovery diff --git a/doc/classfile-spec.md b/doc/classfile-spec.md index c8a26b7aa..d8a4a1513 100644 --- a/doc/classfile-spec.md +++ b/doc/classfile-spec.md @@ -13,13 +13,20 @@ There are two kinds of classfiles: The following terms are used throughout this document: - Class extension: the normalized classfile suffix used for framework lookup, e.g., `_app.gox` and `.gsh` - Class file stem: the filename without the class extension +- Classfile project: the source tree rooted at the analyzed package directory and interpreted by the classfile mechanism +- Project root directory: the root directory of one classfile project +- Pack root: the directory named by one `pack` directive and resolved relative to one project root directory +- Pack document: the logical merged configuration object derived from one pack root and its declared index filename +- Project file kind: a framework file kind declared by one `project` directive and used for project classfiles +- Work file kind: a framework file kind declared by one `class` directive and used for work classfiles +- Project classfile: the framework file that represents the project-level class +- Work classfile: a framework file that represents a non-project class within the same framework - Class type: the generated named type for a classfile +- Base class: an embedded framework type declared by classfile metadata +- Work prototype type: an exported type declared by classfile metadata and associated with one work file kind - Field declaration block: the unique top-level `var` declaration that is interpreted as class fields rather than package variables - Shadow entry: the synthetic function created from top-level statements -- Project classfile: the framework file that represents the project-level class -- Work classfile: a framework file that represents a non-project class within the same framework -- Base class: an embedded framework type declared by classfile metadata ## File classification @@ -172,9 +179,9 @@ Examples: - `get_p_#id_app.gox` has class file stem `get_p_#id` and normalized type name `get_p_id` Framework metadata may further transform the type name: -- A project file whose class file stem is `main`, or a framework that has no explicit project file, uses the project - base-class name as its default class type name. A leading `*` on the base-class name is removed -- A non-empty work-class `-prefix=` is prepended to the normalized work-file stem +- A project classfile whose class file stem is `main`, or a framework that has no explicit project classfile, uses the + name of the base type named by the project base class as its default class type name +- A non-empty `-prefix=` on a work file kind is prepended to the normalized class file stem of a work classfile - If neither of the previous rules applies and the class type name would otherwise equal one of the reserved names `init`, `main`, `go`, `goto`, `type`, `var`, `import`, `package`, `interface`, `struct`, `const`, `func`, `map`, `chan`, `for`, `if`, `else`, `switch`, `case`, `select`, `defer`, `range`, `return`, `break`, `continue`, @@ -194,7 +201,7 @@ For a framework classfile, framework-added fields precede user fields. Framework-added fields are inserted in the following order: - For a project classfile, the embedded project base class -- For a project classfile, each embedded work-class field requested by the `-embed` flag, in work-class declaration +- For a project classfile, each embedded work class field requested by the `-embed` flag, in work file kind declaration order and lexicographic source-file path order - For a work classfile, the embedded work base class - For a work classfile, an embedded pointer to the project class type, if a project class type exists and its field name @@ -359,18 +366,26 @@ The classfile loader recognizes the following module directives: ProjectDirective = "project" [ ProjectExt ExportedName ] PackagePath { PackagePath } . ClassDirective = "class" { ClassDirectiveFlag } WorkExt ExportedName [ ExportedName ] . ImportDirective = "import" [ ImportName ] PackagePath . +PackDirective = "pack" RelativeDirectoryPath PackIndexFile . ClassDirectiveFlag = "-embed" | "-prefix=" string_without_space . +RelativeDirectoryPath = string_without_space . +PackIndexFile = string_without_space . ``` -Every `class` or `import` directive belongs to the most recent preceding `project` directive. +`RelativeDirectoryPath` is one relative directory path token with no spaces. + +`PackIndexFile` is one plain file name token with no spaces. + +Every `class`, `import`, or `pack` directive belongs to the most recent preceding `project` directive. -A project group consists of one `project` directive together with all `class` and `import` directives that belong to it. +A project group consists of one `project` directive together with all `class`, `import`, and `pack` directives that +belong to it. -The first package path of a `project` directive is the framework package used to resolve any base-class symbols named by -that project group. +The first package path of a `project` directive is the framework package used to resolve the project base class, each +work base class, and each work prototype type named by that project group. -Any additional package paths participate in implicit framework-package export lookup but are not searched for -base-class symbols. +Any additional package paths participate in implicit framework-package export lookup but are not searched for those +symbols. ### Extension forms and normalization @@ -382,10 +397,9 @@ Both forms are part of the classfile mechanism. Neither form is a compatibility For newly defined framework registrations, `_[class].gox` is the recommended form. -This recommendation does not constrain the built-in registrations defined above. +The built-in registrations defined above remain valid as written. -This specification defines file classification and compilation semantics for both forms. It does not require auxiliary -tools to recognize arbitrary non-`.gox` class extensions automatically. +Auxiliary tool behavior for arbitrary non-`.gox` class extensions is implementation-defined. The textual extension token accepted by `project` and `class` directives may be written with a leading `*` and, for projects, may also be written with a leading `main`. @@ -403,18 +417,22 @@ For each `project` directive: - If `ProjectExt` and `ExportedName` are present, the directive defines a project file kind and names the project base class - If `ProjectExt` and `ExportedName` are omitted, the directive defines no project file kind or project base class and - still defines the package lookup set and the project group to which subsequent `class` and `import` directives belong + still defines the package lookup set and the project group to which subsequent `class`, `import`, and `pack` + directives belong - When a project base class is named, the first package path is the package from which that symbol is resolved - A project base-class name may be written with a leading `*`, in which case the generated project class embeds a pointer to the named base type rather than the base type itself For each `class` directive: -- The exported symbol names the work base class +- The exported symbol names the work base class and is resolved from the framework package of the containing project + group - The optional final exported symbol is the work prototype type -- If a project group declares more than one work class kind, every work class in that group must declare a prototype - type +- If a project group declares more than one work file kind, every work file kind in that group must declare a work + prototype type +- When a work prototype type is named, the work prototype type symbol is resolved from the framework package of the + containing project group +- `-embed` causes the project class type to embed a field for each generated work class instance of that work file kind - `-prefix=` prepends the given string to every generated work class type name -- `-embed` causes the project class type to embed a field for each generated work class instance of that work kind For each `import` directive: - The imported package becomes available to classfiles as an auto-imported package name @@ -422,7 +440,16 @@ For each `import` directive: - If multiple `import` directives in the same project group resolve to the same auto-import name, the last directive wins -## Project and work-class assembly +For each `pack` directive: +- The project group may declare at most one `pack` directive +- The directory path is resolved relative to the project root directory of the analyzed classfile project +- The directory path must not contain any `..` path component +- The index filename determines the root configuration filename of the pack root and the child configuration filename + expected at each descendant directory level +- The index filename must not contain `/` or `\` +- The index filename must end in one of `.json`, `.yml`, or `.yaml` + +## Project and work class assembly A test framework registration is a framework registration whose project extension has the suffix `test.gox`. @@ -434,35 +461,35 @@ For each framework registration, a package may contain at most one explicit proj It is an error for a package to contain more than one explicit project classfile for the same framework registration. -If a framework registration provides a project base class but the package contains no explicit project file for that -framework, the compiler still synthesizes a default project class type. +If a framework registration provides a project base class but the package contains no explicit project classfile for +that framework, the compiler still synthesizes a default project class type. The synthesized project class has no source file of its own. Its type name is derived by the project type-naming rules described earlier. -### Work-instance assembly for project `Main` +### Work instance assembly for project `Main` For every non-test framework registration that provides a project base class, the compiler generates a project method named `Main` on the project class type. The project base class is therefore required to provide a method named `Main`. -The generated project method constructs work-class instances and forwards them to the embedded project base-class method +The generated project method constructs work class instances and forwards them to the embedded project base-class method `Main`. The grouping rule is: -- If the framework has exactly one work class kind and that `Main` parameter is variadic, all work files of that kind - are passed as variadic arguments -- Otherwise, work files are grouped by their declared prototype type and passed as slices in `Main` parameter order +- If the framework has exactly one work file kind and that `Main` parameter is variadic, all work files of that kind are + passed as variadic arguments +- Otherwise, work files are grouped by their declared work prototype type and passed as slices in `Main` parameter order -The project `Main` method constructs one fresh work-class instance for each work file in the package. When `-embed` is -present on a work class declaration, the freshly created work instance is also assigned into the corresponding embedded -field on the project instance before the project `Main` call. +The project `Main` method constructs one fresh work class instance for each work file in the package. When `-embed` is +present on the corresponding `class` directive, the freshly created work instance is also assigned into the +corresponding embedded field on the project instance before the project `Main` call. ## Synthesized helper methods -The compiler may synthesize additional work-class methods when the declared work prototype requires them. +The compiler may synthesize additional work class methods when the declared work prototype type requires them. ### `Classfname` -If the work prototype contains a method named `Classfname`, the compiler generates: +If the work prototype type contains a method named `Classfname`, the compiler generates: ```xgo func (this *T) Classfname() string @@ -476,9 +503,9 @@ Examples: ### `Classclone` -If the work prototype contains a method named `Classclone`, the compiler generates a shallow-clone method named -`Classclone` with no parameters other than the receiver. Its result list is adopted from the prototype's `Classclone` -declaration. +If the work prototype type contains a method named `Classclone`, the compiler generates a shallow-clone method named +`Classclone` with no parameters other than the receiver. Its result list is adopted from the work prototype type's +`Classclone` declaration. The generated implementation copies `*this` by value into a temporary variable and returns the address of that temporary value. @@ -492,7 +519,7 @@ If one exists, no class-based package `main` is synthesized. If none exists, the compiler selects a class entrypoint as follows: 1. It considers only non-test framework registrations -2. Among framework project groups, it prefers a unique project group whose explicit project file has a shadow entry +2. Among framework project groups, it prefers a unique project group whose explicit project classfile has a shadow entry 3. If no such group exists, it prefers a unique remaining project group, including one that is represented only by a synthesized default project class 4. If no project group is selected, it selects the unique normal classfile that has a shadow entry, if exactly one @@ -507,6 +534,40 @@ func main() { new(T).Main() } If no class type is selected, the compiler generates an empty `main` function unless automatic main generation is disabled in compiler configuration. +## Pack roots and pack documents + +One active project group may derive zero or one pack document from one classfile project. + +If one active project group declares one `pack` directive, its pack root is the resolved directory named by that +directive. + +Active project groups do not share pack roots or pack documents. Each active project group derives its own pack +document independently from its own `pack` directive, if any. + +For one active project group whose `pack` directive resolves to one pack root `R` and one index filename `F`, the +corresponding pack document is one logical object tree derived as follows: +- `R/F` is parsed as one object value +- each descendant directory of `R` that contains `F` contributes one child object +- each such child object is parsed from that descendant `F` and merged into the root object at the relative + directory path from `R`, creating intermediate objects as needed +- directories that do not contain `F` contribute no object of their own +- files other than contributing `F` files are outside the standardized pack-document model + +If the project group declares no `pack` directive, no standardized pack document is derived for that project group. + +If `R/F` does not exist, does not parse as one object value, or if one merge would overwrite an existing key at the +same object level, no standardized pack document is derived for that project group. + +The project files remain the source of truth. Each pack document is derived from one analyzed project state and one +active project group. + +An implementation may materialize one pack document as one generated sibling file of its root configuration file named: +- `index_pack.json` if `F` ends in `.json` +- `index_pack.yml` if `F` ends in `.yml` +- `index_pack.yaml` if `F` ends in `.yaml` + +Its enablement mechanism is implementation-defined. + ## Compatibility The following compatibility aliases are also accepted: @@ -524,5 +585,5 @@ Accordingly: package semantics - Package initialization order for ordinary package variables is unchanged -The classfile mechanism therefore adds a source-level lowering rule. It does not add a new runtime object model beyond -what is produced by the lowered Go code. +The classfile mechanism therefore adds a source-level lowering rule and preserves the ordinary runtime object model +produced by the lowered Go code.