Documentation
¶
Overview ¶
Package ast defines the abstract syntax tree produced by the parser.
Every node carries a Pos (alias for lexer.Position) so diagnostics from later stages - semantic analysis, codegen, formatter, LSP - can map back to the originating source location. The AST is the single shared representation consumed by every downstream tool: keeping it small and strongly-typed lets the parser, semantic analyser, and codegen evolve independently.
AST: top-level declaration types (Type / Enum / Error / Scalar / Middleware / Service) + method / path / member shapes.
AST: expression / literal nodes + Decorator / DecoratorArg / ObjectField.
AST: TypeRef family + QualifiedIdent.
Index ¶
- func EachField(body []TypeMember, fn func(*Field) bool)
- func EachMember(body []TypeMember, fn func(TypeMember) bool)
- func HasDecorator(decs []*Decorator, name string) bool
- func MemberEqual(a, b TypeMember) bool
- func MembersEqual(a, b []TypeMember) bool
- type ArrayLit
- type BoolLit
- type Comment
- type CommentKind
- type Decl
- type Decorator
- type DecoratorArg
- type DurationLit
- type EnumDecl
- type EnumMember
- type EnumValue
- type EnumValueKind
- type ErrorDecl
- type Expr
- type Field
- type File
- type FloatLit
- type FreeComment
- type IdentExpr
- type Import
- type IntLit
- type MapType
- type Method
- type MethodResponse
- type MiddlewareDecl
- type Mixin
- type NamedTypeRef
- type NullLit
- type ObjectField
- type PackageDecl
- type Path
- type PathSegment
- type Pos
- type QualifiedIdent
- type ScalarDecl
- type ServiceDecl
- type ServiceMember
- type SizeLit
- type StringLit
- type TypeDecl
- type TypeMember
- type TypeRef
- func Generic(name string, args ...*TypeRef) *TypeRef
- func MapOf(key, value string) *TypeRef
- func MapOfTypes(key, value *TypeRef) *TypeRef
- func Named(name string) *TypeRef
- func NamedArr(name string) *TypeRef
- func NamedArrOpt(name string) *TypeRef
- func NamedOpt(name string) *TypeRef
- func Qualified(parts ...string) *TypeRef
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EachField ¶
func EachField(body []TypeMember, fn func(*Field) bool)
EachField calls fn for every Field directly declared in body, in source order. Mixin members are skipped — they're embedded type references, not fields with their own decorator chain or shape. This collapses the `for _, m := range td.Body { f, ok := m.(*Field); if !ok { continue } ... }` pattern that previously appeared at 10+ call sites across codegen and semantic.
The callback may return false to stop iteration early (useful for "find first matching field" lookups). Returning true continues.
func EachMember ¶
func EachMember(body []TypeMember, fn func(TypeMember) bool)
EachMember calls fn for every TypeMember in body. Used by walkers that need to see both Fields AND Mixins (e.g. validate emission, where the host's Validate() must call the embedded mixin's Validate()). Fn returns false to stop.
func HasDecorator ¶
HasDecorator reports whether decs contains a decorator with the given Name. Equivalent to `FindDecorator(decs, name) != nil` but reads better at call sites that only need the boolean.
func MemberEqual ¶
func MemberEqual(a, b TypeMember) bool
MemberEqual reports whether two TypeMembers (Field OR Mixin) are equivalent. The interface itself has no Equal method (would require every implementer to know about every other), so callers route through this helper. Type-asserting both to the same concrete and delegating to the typed Equal keeps the dispatch in one place.
func MembersEqual ¶
func MembersEqual(a, b []TypeMember) bool
MembersEqual is the slice form. Tests compare expected vs got body in a single call instead of looping per element.
Types ¶
type ArrayLit ¶
ArrayLit is a `[v1, v2, ...]` literal. Elements may be of mixed kind so the runtime / codegen handles validation per decorator.
type Comment ¶
Comment aliases lexer.Comment so consumers of the AST (codegen, lint tools, the formatter) can refer to one canonical type without pulling the lexer package into every import block.
type CommentKind ¶
type CommentKind = lexer.CommentKind
CommentKind aliases lexer.CommentKind for the same reason.
type Decl ¶
type Decl interface {
// DeclName returns the declared identifier (used for uniqueness checks).
DeclName() string
// DeclPos returns the position of the declaration keyword.
DeclPos() Pos
// contains filtered or unexported methods
}
Decl is the interface implemented by every top-level declaration node (TypeDecl, EnumDecl, ErrorDecl, ScalarDecl, MiddlewareDecl, ServiceDecl). Use a type switch on Decl to dispatch.
type Decorator ¶
type Decorator struct {
Pos Pos
Name string
Args []*DecoratorArg
TrailingDoc string
// HasParens reports whether the source wrote `(...)` after the
// decorator name, even when the inside was empty. The parser sets
// it so the semantic analyzer can flag `@positive()` (empty parens
// on a Flag decorator) and the formatter can normalise it away.
HasParens bool
// Propagated marks decorators that the semantic phase copied onto
// this site from an enclosing scope - currently used by
// `mergeServices` to flag method-level decorators it cloned from an
// `extend service` block. Codegen uses the flag to distinguish
// "inherited" decorators (which `@ignoreMiddleware` / `@ignoreTags`
// / `@ignoreSecurity` should clear) from the method's own
// decorators (which they should not).
Propagated bool
}
func FindDecorator ¶
FindDecorator returns the FIRST decorator in decs whose Name matches. Returns nil when no decorator matches or decs is empty. The nil-check is built in so callers don't have to skip nil entries themselves - this is the most common shape of decorator iteration across the codebase and was previously inlined as `for _, d := range decs { if d != nil && d.Name == "X" { ... break } }` at 15+ sites.
func FindDecoratorAny ¶
FindDecoratorAny returns the first decorator whose Name matches ANY of the supplied names. Useful for binding-kind classifiers (`path` vs `query` vs `header` vs `cookie` vs `body` vs `form`) that want a single pass instead of N FindDecorator calls.
type DecoratorArg ¶
type DecoratorArg struct {
Pos Pos
Name string
Named bool
Value Expr
Nested *Decorator
Object []*ObjectField
}
DecoratorArg is one argument inside `@name(...)`. Exactly one of Value, Nested, or Object is populated:
- Value (and optional Name+Named=true) for bare or `name: value` literals;
- Nested for `@inner(...)` arguments — the parser preserves the nested-decorator shape so future meta-decorators that consume another decorator can land without grammar churn;
- Object for `{ key: value, ... }` literals such as `@example({...})`.
type DurationLit ¶
DurationLit keeps the original source text (e.g. "5s", "1.5ms") rather than a parsed time.Duration so the formatter can round-trip exactly and the runtime can choose its own resolution.
func (*DurationLit) ExprPos ¶
func (e *DurationLit) ExprPos() Pos
type EnumDecl ¶
type EnumDecl struct {
Pos Pos
Decorators []*Decorator
Doc []string
Name string
Members []EnumMember
TrailingDoc []string // `// note` on the same line as the body's closing `}`
}
EnumDecl is `enum Name { Members* }`. Members are a mix of EnumValue (the actual enum entries) and FreeComment (free-floating section dividers / closing notes). All EnumValue entries must share a kind (all bare, all int, or all string); semantic phase enforces that. Use EnumDecl.EnumValues to iterate only the typed values when free-floating comments are not relevant.
func (*EnumDecl) EnumValues ¶
EnumValues returns only the *EnumValue entries from Members, preserving source order. Convenience for callers (semantic, codegen) that want the typed list and treat free-floating comments as cosmetic.
type EnumMember ¶
type EnumMember interface {
// MemberPos returns the position of the member's first token.
MemberPos() Pos
// contains filtered or unexported methods
}
EnumMember is the interface implemented by anything that can appear inside an `enum` body: *EnumValue for typed entries, *FreeComment for free-floating notes / section dividers.
type EnumValue ¶
type EnumValue struct {
Pos Pos
Name string
Kind EnumValueKind
IntValue int64
StrValue string
Decorators []*Decorator
}
EnumValue is one entry inside an enum declaration. IntValue / StrValue are only meaningful when Kind matches.
type EnumValueKind ¶
type EnumValueKind int
EnumValueKind tags the runtime representation of an enum value.
const ( // EnumBare - `Active` (no `=`); rendered as a Go string constant whose // value matches the identifier. EnumBare EnumValueKind = iota // EnumInt - `Active = 1`; rendered as an `int` constant. EnumInt // EnumString - `Active = "active"`; rendered as a `string` constant. EnumString )
type ErrorDecl ¶
type ErrorDecl struct {
Pos Pos
Decorators []*Decorator
Doc []string
Category string
Name string
Body []TypeMember
HasBody bool
TrailingDoc []string // `// note` on the same line as the body's closing `}`
}
ErrorDecl is `error <Category> Name [{ Body }]`. Body is optional - the shortest form (`error NotFound UserNotFound`) inherits all defaults from the category. HasBody distinguishes "explicit empty body `{}`" from "no body at all" (both produce empty Body slice).
type Expr ¶
type Expr interface {
// ExprPos returns the start position of the expression.
ExprPos() Pos
// contains filtered or unexported methods
}
Expr is the interface implemented by every literal value node that may appear as a decorator argument or default value.
type Field ¶
Field is a single `name TypeRef [@decorators]` line in a type body. The Decorators slice holds both the leading and trailing decorator chains merged in source order (parser-side concatenation).
func FieldOf ¶
FieldOf is shorthand for a Field with a single-segment named type (`FieldOf("id", "string")` → DSL `id string`).
func FindField ¶
func FindField(body []TypeMember, name string) *Field
FindField returns the first Field in body whose Name matches, or nil. Mixin members are not considered (a mixin's fields surface via Go's struct embedding at runtime, not under a single name in the host's body).
type File ¶
type File struct {
// LeadingDoc preserves a `//` block at the very top of the file
// when the first AST-bearing token is a file-level decorator
// (`@version`, `@doc`, ...). [Decorator] has no Doc field, so
// without LeadingDoc the lexer-attached comment would be lost
// after the parse / format round trip. When the first token is
// `package`, the same comment lands on [PackageDecl.Doc] instead
// and LeadingDoc stays empty.
LeadingDoc []string
Decorators []*Decorator
Package *PackageDecl
Imports []*Import
Decls []Decl
// Comments is the side channel containing every `//` comment in
// the source, in source order, with leading/trailing kind. The
// parser populates it from the lexer's accumulated set so tools
// (formatter, linters, doc generators) can read every comment
// without re-scanning the source. Decl-level Doc/TrailingDoc
// fields are convenience copies of comments that AST attachment
// already captured; this slice is the exhaustive view.
Comments []*Comment
}
type FreeComment ¶
FreeComment is a free-floating `//` comment block that appears inside a type / enum / service body and does not attach to any field, value, or method. Common patterns:
- Section dividers immediately after the opening `{`.
- Closing notes (TODO / NOTE) immediately before the `}`.
- Stand-alone blocks separated from surrounding members by a blank line.
Text holds one entry per `//` source line, with the leading `// ` (slashes plus optional single space) already stripped — the parser populates this from the lexer's Doc-attached buffer when the buffer is decided to be "free-floating" rather than the next member's leading doc.
Implements TypeMember, EnumMember, and ServiceMember so the same node can sit inside any body kind.
func (*FreeComment) MemberPos ¶
func (c *FreeComment) MemberPos() Pos
type IdentExpr ¶
type IdentExpr struct {
Pos Pos
Name *QualifiedIdent
}
IdentExpr is a reference to a named value (an enum value, a middleware name, etc.) used inside decorator arguments. Resolution happens in the semantic phase.
type Import ¶
Import models a single `import [alias] "path"` line. Alias is empty when omitted; semantic phase derives a default alias from the last path segment.
Doc captures the run of `//` comments immediately above the `import` keyword. TrailingDoc captures a `// note` on the same line as the path string, e.g. `import "auth" // for AuthRequired middleware`.
type MapType ¶
MapType represents `map<K, V>`. Both Key and Value are recursive TypeRef values so that nested maps and generic instances work uniformly.
type Method ¶
type Method struct {
Pos Pos
Decorators []*Decorator
Doc []string
Verb string
Name string
Path *Path
Request *NamedTypeRef
Response *MethodResponse
TrailingDoc []string
}
Method is a single `<verb> Name path { request? response? }`. Path is nil when the method body had no leading `/segment` - the runtime listens at `basePath + servicePrefix` in that case.
TrailingDoc captures a `// note` on the same line as the closing `}` of the method body, e.g. `} // returns 404 if not found`.
type MethodResponse ¶
type MethodResponse struct {
Pos Pos
Type *NamedTypeRef
}
MethodResponse describes the response side of a method. The framework always JSON-encodes the named type; endpoints that want to bypass the framework entirely use the `@passthrough` decorator (which forbids a response block) instead.
type MiddlewareDecl ¶
MiddlewareDecl is `middleware Name`. The DSL captures only the name - configuration (parameter shape, defaults, behaviour) lives in the hand-written Go impl file the scaffolder produces. Doc preserves the leading `//` block.
func (*MiddlewareDecl) DeclName ¶
func (d *MiddlewareDecl) DeclName() string
func (*MiddlewareDecl) DeclPos ¶
func (d *MiddlewareDecl) DeclPos() Pos
type Mixin ¶
type Mixin struct {
Pos Pos
Ref *NamedTypeRef
}
Mixin is a bare reference (qualified ident, optionally generic) inside a type body. The semantic phase expands its fields into the host type.
func MixinOf ¶
MixinOf builds a Mixin for a single-segment ref (`MixinOf("Profile")` → DSL bare `Profile`).
func MixinQualified ¶
MixinQualified builds a Mixin for a multi-segment qualified ref (`MixinQualified("shared", "Audit")` → DSL `shared.Audit`).
type NamedTypeRef ¶
type NamedTypeRef struct {
Pos Pos
Name *QualifiedIdent
Args []*TypeRef
}
NamedTypeRef references a declared type, possibly with generic arguments. Args is non-empty only for generic instances; the codegen renames such instances to e.g. `FooOfUserAndOrg`.
func (*NamedTypeRef) Equal ¶
func (n *NamedTypeRef) Equal(o *NamedTypeRef) bool
Equal reports whether two NamedTypeRefs share the same qualified name AND generic arguments (recursively).
type ObjectField ¶
ObjectField is one `name: value` pair inside a `{}` decorator argument.
type PackageDecl ¶
PackageDecl is the `package <name>` line. Optional in single-file projects; required when more than one file participates in the same logical package.
type Path ¶
type Path struct {
Pos Pos
Segments []*PathSegment
}
Path is the parsed representation of a route path. Each segment is either a literal (possibly hyphenated like `api-v1`) or a `{param}`.
type PathSegment ¶
PathSegment models one `/segment` between slashes. Param == true means the source had `{Literal}`; otherwise Literal is the literal text. An empty Literal with Param == false represents a trailing slash.
type Pos ¶
Pos aliases lexer.Position to keep ast/* free of a hard dependency on lexer naming and to make node-position fields read clearly.
type QualifiedIdent ¶
func (*QualifiedIdent) Equal ¶
func (q *QualifiedIdent) Equal(o *QualifiedIdent) bool
Equal reports whether two QualifiedIdent reference the same dotted name.
func (*QualifiedIdent) String ¶
func (q *QualifiedIdent) String() string
String returns the dotted form, e.g. `pkg.Name` or `Name`.
type ScalarDecl ¶
type ScalarDecl struct {
Pos Pos
Decorators []*Decorator
Doc []string
Name string
Primitive string
}
ScalarDecl is `scalar Name <PrimitiveType> [@decorators]`. Doc holds the run of `//` comments immediately preceding the `scalar` keyword, captured for hover popups and round-trip-safe formatting.
func (*ScalarDecl) DeclName ¶
func (d *ScalarDecl) DeclName() string
func (*ScalarDecl) DeclPos ¶
func (d *ScalarDecl) DeclPos() Pos
type ServiceDecl ¶
type ServiceDecl struct {
Pos Pos
Decorators []*Decorator
Doc []string
Name string
Members []ServiceMember
Extend bool
TrailingDoc []string // `// note` on the same line as the body's closing `}`
}
ServiceDecl is either a primary `service Name { ... }` (Extend == false) or a continuation `extend service Name { ... }` (Extend == true). The semantic phase merges all extends into the primary.
Members is a heterogeneous list of *Method (the actual endpoints) and *FreeComment (free-floating section dividers / closing notes). Use ServiceDecl.Methods when only the typed endpoints are needed.
func (*ServiceDecl) DeclName ¶
func (d *ServiceDecl) DeclName() string
func (*ServiceDecl) DeclPos ¶
func (d *ServiceDecl) DeclPos() Pos
func (*ServiceDecl) Methods ¶
func (d *ServiceDecl) Methods() []*Method
Methods returns only the *Method entries from Members in source order. Convenience for callers (semantic, codegen) that ignore free-floating comments and want the typed list.
type ServiceMember ¶
type ServiceMember interface {
// MemberPos returns the position of the member's first token.
MemberPos() Pos
// contains filtered or unexported methods
}
ServiceMember is the interface implemented by anything that can appear inside a `service` body: *Method for typed endpoints, *FreeComment for free-floating notes / section dividers.
type SizeLit ¶
SizeLit keeps the original source text (e.g. "1MB"). Same rationale as DurationLit.
type StringLit ¶
StringLit holds the unescaped contents of a `"..."` or backtick literal. (The lexer keeps escapes verbatim; the parser unescapes when constructing this node.)
type TypeDecl ¶
type TypeMember ¶
type TypeMember interface {
// MemberPos returns the position of the member's first token.
MemberPos() Pos
// contains filtered or unexported methods
}
TypeMember is the interface for items inside a `{}` type body - either a Field or a Mixin.
type TypeRef ¶
type TypeRef struct {
Pos Pos
Map *MapType
Named *NamedTypeRef
Array bool
Optional bool
// ArrayDepth captures multi-dimensional arrays (`Tag[][]` →
// 2). Single-dim arrays use depth 1. Code that only needs
// "is this an array?" can keep checking [Array] / `> 0`
// equivalently.
ArrayDepth int
}
TypeRef describes a type expression. Exactly one of Map or Named is set; Array and Optional are independent suffix flags so `T[]?` is legal.
`ArrayDepth` is the number of trailing `[]` suffixes parsed (0 = not an array). The legacy `Array bool` is kept as a derived convenience for the wide set of call sites that only care whether the field is "any kind of array" - it equals `ArrayDepth > 0` after every parse.
func Generic ¶
Generic builds a generic-instantiation TypeRef (`Generic("Page", Named("Order"))` → DSL `Page<Order>`).
func MapOf ¶
MapOf builds a `map<K, V>` TypeRef from two single-segment names. Use MapOfTypes when key or value need a more complex shape.
func MapOfTypes ¶
MapOfTypes is the general form taking already-built TypeRefs.
func Named ¶
Named builds a TypeRef for a single-segment named type (`Named("string")` → DSL `string`). Use Qualified for dotted refs.
func NamedArrOpt ¶
NamedArrOpt is array + optional (`string[]?`).